自定义Viewgroup机制

作者: 奔跑吧李博 | 来源:发表于2018-12-28 19:20 被阅读17次

在我们做的项目中,如果需要做某些定制性高的自定义ViewGroup,如果有不错的Viewgroup机制功底,那做起来就会有奇效。富有创造性的viewgroup会让人看得瞠目结舌,所以从一定程度上说,写viewgroup的水平很看出一个开发人员的技术如何。比如Android自带的RelativeLayout、LinearLayout、FrameLayout、Viewpager等都是继承自ViewGroup类。

ViewGroup层级图:

Viewgroup绘制流程:

会通过onMeasure、onLayout、onDraw顺序的流程方法进行绘制。

onMeasure

ViewGroup的测量需要遍历所有子view,对所有子view进行测量。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int childCount = this.getChildCount();
  for (int i = 0; i < childCount; i++) {
      View child = this.getChildAt(i);
      this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
      int cw = child.getMeasuredWidth();
      int ch = child.getMeasuredHeight();
  }
}

onLayout

自定义viewgroup必须要实现onLayout方法,在其中需要遍历所有子view对其调用layout方法,设置各个子view的左上右下坐标。而viewgroup的宽高则会根据所有子view所需最大的宽高来设置自己的宽高。

protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
  int childCount = this.getChildCount();
  for (int i = 0; i < childCount; i++) {
      View child = this.getChildAt(i);
      LayoutParams lParams = (LayoutParams) child.getLayoutParams();
      child.layout(lParams.left, lParams.top, lParams.left + childWidth,
              lParams.top + childHeight);
  }
}
onDraw

viewgroup会按照子类的排列顺序,调用子类的onDraw方法分别进行绘制。

示例:写一个水平的LinearLayout的功能。
public class MyLinearLayout extends ViewGroup {

    public MyLinearLayout(Context context) {
        super(context);
    }

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        View childView;
        int startLeft = l;  //父view当前的左边距位置,这里只每一个子view的起始左边距位置
        for (int i = 0; i < getChildCount(); i++) {
            childView = getChildAt(i);

            int width = childView.getMeasuredWidth();  //当前子view的宽
            int height = childView.getMeasuredHeight();  //当前子view的高

            childView.layout(startLeft, t, startLeft + width, height);
            startLeft += width;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        measureChildren(widthMeasureSpec, heightMeasureSpec);  //对所有子view进行测量

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.AT_MOST && heigthMode == MeasureSpec.AT_MOST) {
            //宽高都为wrapcontent,则设置尺寸为子view所需宽高的最大值
            setMeasuredDimension(totalWidth(), totalHeight());
        } else if (widthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(totalWidth(), heightSize);
        } else if (heigthMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSize, totalHeight());
        }
    }

    /**
     * 计算子view所需最大宽度,宽度为所有子view之和
     * @return
     */
    private int totalWidth() {
        int totalWidth = 0;
        for (int i = 0; i < getChildCount(); i++) {
            totalWidth += getChildAt(i).getMeasuredWidth();
        }
        return totalWidth;
    }

    /**
     * 计算子view所需最大高度,为子view中最大高度
     * @return
     */
    private int totalHeight() {
        int maxHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            if (getChildAt(i).getMeasuredHeight() > maxHeight) {
                maxHeight = getChildAt(i).getMeasuredHeight();
            }
        }
        return maxHeight;
    }
}
布局代码:
    <com.example.apple.viewgroupuse.MyLinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <View
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#00BC1B"/>

        <View
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#12B7F5"/>

        <View
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF9F00"/>

        <View
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#EF1200"/>
    </com.example.apple.viewgroupuse.MyLinearLayout>
运行结果:

添加子view会水平线性摆放


相关文章

网友评论

    本文标题:自定义Viewgroup机制

    本文链接:https://www.haomeiwen.com/subject/coqvgxtx.html