android 自定义View学习一之onMeasure和onLayout

一般来说一个自定义View包含以下三个部分:
布局、显示、交互

布局:onMeasure、onLayout (ViewGroup)
显示:onDraw–>canvas paint matrix clip rect animation path(贝塞尔) line
交互:事件分发 解决事件冲突的能力

在这里插入图片描述
我们可以查看android给我们提供的布局里面涉及onDraw的地方。

FrameLayout的源码看不到onDraw
LinearLayout有onDraw:判断一下是水平方向还是竖直方向,绘制一下行而已,非常简单。

总结:ViewGroup一般涉及onDraw的地方很少,或者需要的实现是比较少。

自定义View主要实现onMeasure + onDraw
自定义ViewGroup主要实现:onMeasure+onLayout

自定义布局继承自ViewGroup有四个构造函数,参数分别有:
Context、AttributeSet、int、int
其中第二个是我们在xml里面正常设置的android自动的宽高等熟悉的属性集合对,第三个是主题的id,第四个是自定义属性的id
如果用过一些第三方控件的同学应该知道。

实际上最终调用的inflate去实例化的时候也有对应的四个方法,但是最终都会调用到有四个参数的实例化方法,如果你没有传递这些参数进来则给默认值。

onMeasure和onLayout方法,其实主要是onMeasure,因为onLayout方法里面的参数都是来自onMeasure。

我们知道布局里面大小有三种类型设置,MATCH_PARENT、WRAP_CONTENT和确切的尺寸大小,当父布局设置WRAP_CONTENT的时候你知道给多少吗?显然不知道,所以我们的度量一般都是从子View开始度量。

我们的View都是树形结构,从顶层的ViewGroup开始,调用度量下层的子View进行度量,下层的布局又再次调用它的子View进行度量,度量是从上而下,但是真正开始计算是从底部的View开始度量结束一层层计算往上提交。

这里面有一个关键的辅助类:MeasureSpec

我们通过或者一个View的layoutParmas,里面记录了这个view设置的宽高属性,它的宽和高分别是多少。那么我们能知道把它转成一个具体的多少dp dip等尺寸吗?layoutParams可以看做是子View自身的布局大小期望值,但是我们具体最终会分配多少呢?这个时候MeasureSpec就上场了,通过将父View自身能拥有的大小和子View自身的期望值通过一系列的度量计算之后返回的MeasureSpec就是子View所能拥有的大小。

还是面对对象的逻辑:子View如果设置WRAP_CONTENT我们能知道给多少吗?如果我们自己也是WRAP_CONTENT呢?

所以需要MeasureSpec来作为一个辅助的计算工具类。

private static final int MODE_SHIFT = 30; 
private static final int MODE_MASK  = 0x3 << MODE_SHIFT; 
public static final int UNSPECIFIED = 0 << MODE_SHIFT; 
public static final int EXACTLY     = 1 << MODE_SHIFT; 
public static final int AT_MOST     = 2 << MODE_SHIFT; 

定义三种mode,分别对应MATCH_PARENT、WRAP_CONTENT和确切的尺寸大小。
MeasureSpec 是通过 int 值的位运算来实现的,一个int有32位。
高两位分别代表这三种mode:10 00 01,剩下的来标识具体的尺寸绰绰有余

通过MeasureSpec 的 makeMeasureSpec来转换成具体的大小

度量的判断在网上有一张非常经典的图这里我就不盗图了,免得又被判定为高度相似,我有几篇文章盗了几张图被判断高度相似了。

那张图其实没什么卵用,因为你根本记不住也没必要去记,java的代码都是很面对对象的,是可以理解的方式来学习的。

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {}

int spec:这个度量的子View的方法,第一个参数是你自己当前能拥有多大的尺寸,也就是你的父布局传递下来的,一层一层传递。

int padding:子View所有占据的宽高得先减去我的内间距

int childDimension:子View的layoutParams,它所有要求的宽或者高

测量宽高:

 var childwithMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 
 right + left, params.width)
 var childheightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
  top + bottom, params.height)

度量方法源码:

	//这三个参数的含义上面解释了
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    	//子View的期望mode
        int specMode = MeasureSpec.getMode(spec);
        //子View的期望尺寸
        int specSize = MeasureSpec.getSize(spec);
		//减去我的内间距
        int size = Math.max(0, specSize - padding);
		//跟父布局计算之后的子View想要的期望mode和尺寸
		//不是子View想要多少最终就有多少,还是需要一层层传递上去等待最终的分配
        int resultSize = 0;
        int resultMode = 0;
		//具体的逻辑
        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
        	//确切的子View想要的尺寸大小
            if (childDimension >= 0) {
            	//你想要多少我就给你多少,当然先记着,等最终分配,包括mode
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                //子View是match,那么就把我自己所能分配给子View全分配给你
                //记住mode
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                //WRAP_CONTENT,自适应,那么我也全给你,当然要看最终的分配
                //也记住mode,因为你是可以压缩给的
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                //子View有设置最大的尺寸的时候就记录最大的尺寸和mode
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                //子View如果是MATCH就把我自己所有的都给你,记住mode
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                //自适应,跟上面一样
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                //如果你有确切的尺寸,那么就记录一下尺寸和mode
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                //子View想要和父View一样大,那就把自己全给你,记住mode
                //View.sUseZeroUnspecifiedMeasureSpec是一个静态变量 false
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                //和上面的一样,子View子使用全给你,并记住mode
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

经过上面的将layoutParams转为MeasureSpec之后,我们才能真正的度量子View的确切大小

//测量子View
 childView.measure(childwithMeasureSpec,childheightMeasureSpec)
 //获取测量之后的子View的宽高
 var childMeasuredWith = childView.measuredWidth
 var childheight = childView.measuredHeight

还有一点onMeasure可能会被调用多次,例如FrameLayout,所以你如果在onMeasure里面定义的变量最好小心一点,什么时候会被度量多次是由你的父布局决定的,具体看需求。

这是一个练习onMeasure和onLayout的例子:流式布局
在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:text="搜索历史"
            android:textColor="@android:color/black"
            android:textSize="18sp"/>
        <com.example.flowlayout.MyFlowLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:layout_margin="8dp">
            <TextView
                android:id="@+id/sgw"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="水果味孕妇奶粉" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="儿童洗衣机" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="洗衣机全自动" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="小度" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="儿童汽车可坐人" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="抽真空收纳袋" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="儿童滑板车" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="稳压器 电容" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="羊奶粉" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="奶粉1段" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="图书勋章日" />
        </com.example.flowlayout.MyFlowLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:text="搜索发现"
            android:textColor="@android:color/black"
            android:textSize="18sp" />
        <com.example.flowlayout.MyFlowLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="惠氏3段" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="奶粉2段" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="图书勋章日" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="伯爵茶" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="阿迪5折秒杀" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="蓝胖子" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="婴儿洗衣机" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="小度在家" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="遥控车可坐" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="搬家袋" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="剪刀车" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="滑板车儿童" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="空调风扇" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="空鼓锤" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_button_circular"
                android:text="笔记本电脑" />
        </com.example.flowlayout.MyFlowLayout>
    </LinearLayout>

</ScrollView>

java代码的实现:

public class FlowLayout extends ViewGroup {
    private static final String TAG = "FlowLayout";
    private int mHorizontalSpacing = dp2px(16); //每个item横向间距
    private int mVerticalSpacing = dp2px(8); //每个item横向间距

    private List<List<View>> allLines = new ArrayList<>(); // 记录所有的行,一行一行的存储,用于layout
    List<Integer> lineHeights = new ArrayList<>(); // 记录每一行的行高,用于layout


    public FlowLayout(Context context) {
        super(context);
//        initMeasureParams();
    }

    //反射
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
//        initMeasureParams();
    }

    //主题style
    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
//        initMeasureParams();
    }
    //四个参数 自定义属性

    private void clearMeasureParams() {
        allLines.clear();
        lineHeights.clear();
    }

    //度量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        clearMeasureParams();//内存 抖动
        //先度量孩子
        int childCount = getChildCount();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);  //ViewGroup解析的父亲给我的宽度
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的父亲给我的高度

        List<View> listViews = new ArrayList<>(); //保存一行中的所有的view
        int lineWidthUsed = 0; //记录这行已经使用了多宽的size
        int lineHeight = 0; // 一行的行高

        int parentNeededWidth = 0;  // measure过程中,子View要求的父ViewGroup的宽
        int parentNeededHeight = 0; // measure过程中,子View要求的父ViewGroup的高

        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);

            LayoutParams childLP = childView.getLayoutParams();
            if (childView.getVisibility() != View.GONE) {
                //将layoutParams转变成为 measureSpec
                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                        childLP.width);
                int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                        childLP.height);
                childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

                //获取子view的度量宽高
                int childMesauredWidth = childView.getMeasuredWidth();
                int childMeasuredHeight = childView.getMeasuredHeight();

                //如果需要换行
                if (childMesauredWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {

                    //一旦换行,我们就可以判断当前行需要的宽和高了,所以此时要记录下来
                    allLines.add(listViews);
                    lineHeights.add(lineHeight);

                    parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing;
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing);

                    listViews = new ArrayList<>();
                    lineWidthUsed = 0;
                    lineHeight = 0;
                }
                // view 是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局
                listViews.add(childView);
                //每行都会有自己的宽和高
                lineWidthUsed = lineWidthUsed + childMesauredWidth + mHorizontalSpacing;
                lineHeight = Math.max(lineHeight, childMeasuredHeight);

                //处理最后一行数据
                if (i == childCount - 1) {
                    allLines.add(listViews);
                    lineHeights.add(lineHeight);
                    parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing;
                    parentNeededWidth = Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing);
                }

            }
        }



        //再度量自己,保存
        //根据子View的度量结果,来重新度量自己ViewGroup
        // 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth: parentNeededWidth;
        int realHeight = (heightMode == MeasureSpec.EXACTLY) ?selfHeight: parentNeededHeight;
        setMeasuredDimension(realWidth, realHeight);
    }

    //布局
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int lineCount = allLines.size();

        int curL = getPaddingLeft();
        int curT = getPaddingTop();

        for (int i = 0; i < lineCount; i++){
            List<View> lineViews = allLines.get(i);

            int lineHeight = lineHeights.get(i);
            for (int j = 0; j < lineViews.size(); j++){
                View view = lineViews.get(j);
                int left = curL;
                int top =  curT;

                 int right = left + view.getMeasuredWidth();
                 int bottom = top + view.getMeasuredHeight();
                 view.layout(left,top,right,bottom);
                 curL = right + mHorizontalSpacing;
            }
            curT = curT + lineHeight + mVerticalSpacing;
            curL = getPaddingLeft();
        }

    }

//    @Override
//    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);
//    }

    public static int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
    }

}

kotlin代码的实现:

class MyFlowLayout : ViewGroup {

    constructor(context: Context):super(context){}
    constructor(context: Context,attrs: AttributeSet):super(context,attrs){}

    //记录所有的行,一行一行地存储,用于layout
    var allLines:MutableList<MutableList<View>> = mutableListOf()

    //记录每一行的行高,用于layout
    var allHeight : MutableList<Int> = mutableListOf()

    val mHorizontalSpacing = dp2px(16) //每个item横向间距
    val mVerticalSpacing = dp2px(8) //每个item横向间距

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        clearMeasureParams()//内存 抖动

        //记录当前行已经使用了多宽
        var lineWithSize = 0;

        //一行的行高
        var heightMaxSzie = 0;

        //获取父布局给我的宽高
        var selfwith = MeasureSpec.getSize(widthMeasureSpec)
        var selfheight = MeasureSpec.getSize(heightMeasureSpec)

        //子View所能占据的最大宽高必须先减去我的内间距
        val top = paddingTop
        val right = paddingRight
        val left = paddingLeft
        val bottom = paddingBottom

        //测量过程中子view要求的父布局宽高
        var parnetNeedWith = 0;
        var parentNeedHeight = 0;

        //保存一行中所有的子View
        var listView : MutableList<View> = mutableListOf()

        //先度量子View
        var childCout = childCount

        //for循环递归调用
        for (index in 0 until childCout) {
            var childView = getChildAt(index)
            var params = childView.layoutParams
            if (childView.visibility != View.GONE) {
                //将layoutParams转变成为 measureSpec
                // 一个int 高两位来作为MeasureSpec 的类型判断,10 00 01 分别作为:确认的多少尺寸,match 和wrap 三种类型
                //低30位表示具体的尺寸
                var childwithMeasureSpec = getChildMeasureSpec(widthMeasureSpec, right + left, params.width)
                var childheightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, top + bottom, params.height)

                //测量子View
                childView.measure(childwithMeasureSpec,childheightMeasureSpec)

                //获取测量之后的子View的宽高
                var childMeasuredWith = childView.measuredWidth
                var childheight = childView.measuredHeight

                //判断是否需要换行
                if (childMeasuredWith + lineWithSize + mHorizontalSpacing > selfwith) {
                    //换行,记录宽高
                    allLines.add(listView)
                    allHeight.add(heightMaxSzie)

                    //测量过程中子view要求的父布局宽高,计算
                    parentNeedHeight = parentNeedHeight + childheight + mVerticalSpacing //加一行高度,计算的是总行高
                    parnetNeedWith = Math.max(parnetNeedWith,lineWithSize +  mHorizontalSpacing) //?:已经使用的宽度 + 横向间距 :子View要求的宽度,取所有行的最大宽度

                    //listView 创建一个新再记录新的一行
                    listView = mutableListOf<View>()

                    lineWithSize = 0 //记录这行已经使用了多宽的size  重置为0
                    heightMaxSzie = 0 // 一行的行高 重置为0
                }

                lineWithSize = childMeasuredWith + lineWithSize + mHorizontalSpacing
                heightMaxSzie = Math.max(heightMaxSzie,childheight) //取一行中子View最高的那个最为行高

                listView.add(childView)

                //处理最后一行,当最后一行不满足换行,但是也需要添加对应的子View到数据里去
                if (index == childCout-1) {
                    //记录宽高
                    allLines.add(listView)
                    allHeight.add(heightMaxSzie)

                    parentNeedHeight = parentNeedHeight + childheight + mVerticalSpacing //加一行高度,计算的是总行高
                    parnetNeedWith = Math.max(parnetNeedWith,lineWithSize +  mHorizontalSpacing) //?:已经使用的宽度 + 横向间距 :子View要求的宽度
                }
            }
        }

        //子View度量完了之后再度量自己
        //根据子View的度量结果,来重新度量自己ViewGroup
        // 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
        var widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var heightMode = MeasureSpec.getMode(heightMeasureSpec)

        //真实的宽高
        //如果父View给自己分配的是确切的宽高,那么直接使用,如果不是,把子View希望的宽高设置成自己希望的宽高
        //测量子View的时候我们已经把自己的padding传进去了,所以子View希望能给予的最大宽高是包括自己的需求的
        var realwidth = if (widthMode == MeasureSpec.EXACTLY) selfwith else parnetNeedWith
        var realheight = if (heightMode == MeasureSpec.EXACTLY) selfheight else parentNeedHeight

        //设置自己度量的宽高
        setMeasuredDimension(realwidth,realheight)
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        //开始布局参数,有多少行,每一行有多少个子View我们已经清楚了
        var outCount= allLines.size

        //我们每一行的listView记录的是每一个子View,不包含我们自己padding,所有要加上
        var top = paddingTop
        var left =paddingLeft
        var lineView: MutableList<View>
        for (index in 0 until outCount) {
            lineView = allLines.get(index)
            var size = lineView.size
            for (child in 0 until size) {
                var view = lineView.get(child)
                //每一个view都需要设置左上右下四个点

                //每一次都拿外面定义的,内部不需要改变
                //父View最左边开始第一个
                var right = view.measuredWidth + left
                var bottom = view.measuredHeight + top

                view.layout(left,top,right,bottom)

                //第二个同一行top是一样,left需要加上第一个的距离和同一行之间view的间距
                left = right + mHorizontalSpacing
            }

            //外层每循环一次 topOut 需要重新设置,left则要复原
            var height = allHeight.get(index)
            // 顶部间距 + 当前行的行高 + 行间距
            top += height + mVerticalSpacing
            left = paddingLeft
        }
    }

    private fun clearMeasureParams() {
        allLines.clear()
        allHeight.clear()
    }

    fun dp2px(dp: Int): Int {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics).toInt()
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值