Android 进阶自定义 ViewGroup 自定义布局

前言

在咱们的实际应用中, 常常须要用到自定义控件,好比自定义圆形头像,自定义计步器等等。但有时咱们不只须要自定义控件,举个例子,FloatingActionButton 你们都很经常使用,因此你们也很常常会有一种需求,点击某个 FloatingActionButton 弹出更多 FloatingActionButton ,这个需求的通常思路是写 n 个 button 而后再一个个的去设置动画效果。但这实在是太麻烦了,因此网上有个 FloatingActionButtonMenu 这个开源库,这就是利用到了自定义布局 「ViewGroup」,如今就让我给他家介绍下,如何自定义布局 「layout」。html


难点

相比于自定义 View ,自定义 ViewGroup 的难点在于,子控件位置的肯定和布局大小的肯定。不像 单个 View 子要花粉好模式,测量好宽度就搞定了,ViewGroup 的长宽根据子 View 的数量和单个的大小变化而变化。这就是最大的坎,因此该如何肯定 ViewGroup 的大小呢?java


步骤

这里 我为你们设计一个 相似 LinearLayout 线性布局的 ViewGroup 做为范例。android

首先,若是是一个 LinearLayout 那么当设置 wrap_content 时,他就会以子空间中最宽的那个为它的宽度。同时在高度方面会是全部子控件高度的总和。因此咱们先写两个方法,分别用于测量 ViewGroup 的宽度和高度。编程

private int getMaxWidth(){
        int count = getChildCount();
        int maxWidth = 0;
        for (int i = 0 ; i < count ; i ++){
            int currentWidth = getChildAt(i).getMeasuredWidth();
            if (maxWidth < currentWidth){
                maxWidth = currentWidth;
            }
        }
        return maxWidth;
    }
 
    private int getTotalHeight(){
        int count = getChildCount();
        int totalHeight = 0;
        for (int i = 0 ; i < count ; i++){
            totalHeight += getChildAt(i).getMeasuredHeight();
        }
        return totalHeight;
    }
    
复制代码

对于 ViewGroup 而言咱们能够粗略的分为两种模式:固定长宽模式(match_parent),自适应模式(wrap_content),根据这两种模式,就能够对 ViewGroup 的绘制进行划分。这里关于 measureChildren 这个方法,他是用于将全部的子 View 进行测量,这会触发每一个子 View 的 onMeasure 函数,可是你们要注意要与 measureChild 区分,measureChild 是对单个 view 进行测量markdown

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
        measureChildren(widthMeasureSpec, heightMeasureSpec);
 
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width     = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode= MeasureSpec.getMode(heightMeasureSpec);
        int height    = MeasureSpec.getSize(heightMeasureSpec);
 
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
            int groupWidth = getMaxWidth();
            int groupHeight= getTotalHeight();
 
            setMeasuredDimension(groupWidth, groupHeight);
        }else if (widthMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(getMaxWidth(), height);
        }else if (heightMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(width, getTotalHeight());
        }
    }
复制代码

重写 onLayout

整完上面这些东西,咱们的布局大小七十九已经出来了,然咱们在活动的布局文件里面加上它,并添加上几个子 View 而后运行一下,先看看效果:ide

<com.entry.android_view_user_defined_first.views.MyLinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorAccent">
 
        <Button android:layout_width="100dp" android:layout_height="50dp" android:text="qwe"/>
 
        <Button android:layout_width="250dp" android:layout_height="150dp" android:text="qwe"/>
 
        <Button android:layout_width="200dp" android:layout_height="75dp" android:text="qwe"/>
 
    </com.entry.android_view_user_defined_first.views.MyLinearLayout>
复制代码

运行效果以下:

咱们看见布局出来了,大小好像也没啥问题,可是子 View 呢??! 这么没看见子 View 在看看代码,系统以前然咱们重写的 onLayout() 仍是空着的呀!!也就是说,子 View 的大小和位置根本就尚未进行过设定!让咱们来重写下 onLayout() 方法。函数

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        int currentHeight = 0;
        for (int i = 0 ; i < count ; i++){
            View view = getChildAt(i);
            int height = view.getMeasuredHeight();
            int width  = view.getMeasuredWidth();
            view.layout(l, currentHeight, l + width, currentHeight + height);
            currentHeight += height;
        }
    }
复制代码

再运行一下看看:

成功了有木有!布局

因为本文是本身学习过程的总结,若是文中有错误,但愿你们能在评论区指出学习

最后,祝你们编程愉快!动画

相关文章
相关标签/搜索