Android自定义View系统知识

Android自定义View onLayout详解

2019-04-01  本文已影响77人  1a473fcb13b0

onLayout的作用:

决定子View在ViewGroup中的位置

onLayout使用范围

只针对ViewGroup,且必须实现

onLayout(boolean changed, int l, int t, int r, int b)内参数个代表什么?

changed: 当前View的大小和位置改变了
l: 相对于父容器左边的距离
t: 相对于父容器上边的距离
r: 相对于父容器右边的距离
b: 相对于父容器下边的距离

示例图

layout() 和 onLayout()

layout() 和 onLayout() 。layout()方法用来确定View本身的位置,onLayout()方法则用来确定所有子元素的位置。View和ViewGroup中都有layout()和onLayout()两个方法,但两个类中都没有实现onLayout(),其原因和ViewGroup中没有onMeasure()方法是相同的:因为不同ViewGroup的子类对布局的要求不一样。

当我们自定义了一个ViewGroup的时候,会先确定这个ViewGroup的位置,然后,通过重写 onLayout() 方法,遍历所有的子元素并调用其 layout() 方法,在layout()方法中onLayout()方法又会被调用。ViewGroup就是通过这个过程,递归地对所有子View进行了布局。

示例代码:

/**
     *当这个view和其子view被分配一个大小和位置时,被layout调用。
     * @param changed 当前View的大小和位置改变了
     * @param l       相对于父容器左边的距离
     * @param t       相对于父容器上边的距离
     * @param r       相对于父容器右边的距离
     * @param b      相对于父容器下边的距离
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mAllViews.clear();
        mLineHeight.clear();

        // 当前ViewGroup的宽度
        int width = getWidth();

        int lineWidth = 0;
        int lineHeight = 0;

        // 获取每一行的view集合
        List<View> viewList = new ArrayList<>();
        /**
         * 1、通过getChildCount,获取子View的个数view个数
         */
        int childCount = getChildCount();
        /**
         * 2、遍历childCount,通过getChildAt获取到对应的view,并按每一行的viewList记录,记录在mAllViews内
         */
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
            int childWidth = childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            /**
             * 如果换行则添加则把高度加入mLineHeight,把当前行的viewList加入到mAllViews,并viewList,lineWidth,lineHeight
             * 如果不换行则记录宽度则直接添加当前控件的宽度到lineWidth内
             */
            if (lineWidth + childWidth > width - getPaddingLeft() - getPaddingRight()) {
                // 记录当前行的Views
                mAllViews.add(viewList);
                // 记录LineHeight
                mLineHeight.add(lineHeight);
                // 重置当前行的Views
                viewList = new ArrayList<>();
                // 重置宽
                lineWidth = childWidth;
                // 重置高
                lineHeight = childHeight;
            } else {
                lineWidth += childWidth;
                /**
                 * 取当前控件的高度和之前记录的高度的最大值
                 */
                lineHeight = Math.max(lineHeight, childHeight);
            }
            viewList.add(childView);
        }
        /**
         * 处理最后一行
         */
        mAllViews.add(viewList);
        mLineHeight.add(lineHeight);

        /**
         * 3、遍历mAllViews,通过view.layout(lc, tc, rc, bc);确定各个子View的位置
         */
        // 行的个数
        int lineNum = mAllViews.size();
        // view的初始位置
        int left = getPaddingLeft();
        int top = getPaddingTop();
        for (int i = 0; i < lineNum; i++) {
            viewList = mAllViews.get(i);
            lineHeight = mLineHeight.get(i);

            for (View view : viewList) {
                if (view.getVisibility() == GONE) {
                    continue;
                }
                MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
                int lc = left + lp.leftMargin;
                int tc = top + lp.topMargin;
                int rc = lc + view.getMeasuredWidth();
                int bc = tc + view.getMeasuredHeight();
                Log.d("onLayout","getMeasuredHeight="+view.getMeasuredHeight());
                Log.d("onLayout","getHeight="+view.getHeight());
                view.layout(lc, tc, rc, bc);
                left += view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            }
            left = getPaddingLeft();
            top += lineHeight;
        }
    }

getMeasuredWidth()、getMeasuredHeight()与getWidth()、getHeight()存在什么样的差别?

image.png image.png

1、getMeasuredWidth、getMeasuredHeight经过measure测量之后才能获取到的。
而getWidth、getHeight是经过layout设置过view布局位置之后才能获取到。所以,在onLayout使用getWidth和getHeight是不能得到对应的宽高的。
2、通常情况下,getWidth、getHeight和getMeasureWidth、getMeasureHeight得到的值是一致的,但是layout的四个坐标点是可以随便设置的,不一定会根据measure测量的大小对应来设置,所以两种方式存在不等的情况。

具体源码分析请参考:
https://blog.csdn.net/wsq_tomato/article/details/80323719

参考文章:
https://www.cnblogs.com/itgungnir/p/6721868.html

上一篇下一篇

猜你喜欢

热点阅读