自定义View-流式布局

[toc]

流式布局

自定义view与自定义viewgroup的区别

  • 自定义view一般继承View等其他View,通常实现onMeasureonDraw方法
  • 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承ViewGroup或各种Layout,通常实现onMeasureonLayout

Android坐标系说明

两种坐标系

Android屏幕坐标系png.png
视图坐标系.png

MeasureSpec讲解

MeasureSpace是View的内部类,是int类型的,用高两位表示mode,低30位表示size

属性说明

  • UNSPECIFIED 不对View大小进行限制,通常为系统使用,不用太关注
  • EXACTLY 确切的大小,比如100dp
  • AT_MOST 大小不可超过某个数值,比如 matchParent最大不能超过父布局

普通View的Measure的创建规则

通过表格快查子View的mode

普通View的Measure的创建规则

通过系统代码解析子View的MeasureSpec创建过程

一般是通过调用系统的 getChildMeasureSpec 获取子View的MeasureSpec,代码解析如下:

/**
 * @param spec The requirements for this view(父元素ViewGroup的MeasureSpec)
 * @param padding The padding of this view for the current dimension and margins, if applicable (父元素的padding值)
 * @param childDimension How big the child wants to be in the current dimension(需要被测量的子View的) 属性值
 *              View.getLayoutParams().width/height
 */
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        // 获取父View的Mode与Size
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        // 计算父View留给子View的具体尺寸(去除padding值的尺寸)
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;
        // 判断父View的mode
        switch (specMode) {
        // Parent has imposed an exact size on us
        // 如果父View是 EXACTLY (确定值)
        //      子View为 具体尺寸 比如 100dp,则子view的mode为 EXACTLY,最大尺寸就是childDimension
        //      子View为 MATCH_PARENT, 则子view的mode为 EXACTLY,最大尺寸为 size(父View留给子View的最大尺寸)
        //      子View为 WRAP_CONTENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                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.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        // 如果父View是 AT_MOST (大小与内容有关)
        //      子View为 具体尺寸 比如 100dp,则子view的mode为 EXACTLY,最大尺寸就是childDimension
        //      子View为 MATCH_PARENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
        //      子View为 WRAP_CONTENT, 则子view的mode为 AT_MOST,最大尺寸为 size(父View留给子View的最大尺寸)
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                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.
                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
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                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
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        // 计算子View的 MeasureSpec 并 返回
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

getWidth与getMeasureWidth区别

  • getWidth是在onLayout以后才能获取到,是通过视图右边的坐标减去左边的坐标计算出来的
  • getMeasureWidth是在onMeasure结束以后才可以获取到的值,是通过setMeasuredDimension设置的值

coding

代码地址点击我

补充说明

onMeasure调用次数

  • onMeasure的调用次数与父View的算法有关系,比如:FrameLayout调用了子View的onMeasure有2次。

FlexboxLayout推荐

Android可伸缩布局-FlexboxLayout(支持RecyclerView集成)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。