欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

测量与布局

程序员文章站 2022-03-17 13:07:32
...

ViewGroup的绘制流程

绘制流程分为三步:测量、布局、绘制,分别对应onMeasure(), onLayout(),onDraw()
  • onMeasure():测量当前控件的大小,为正式布局提供建议(只是建议,至于上是否适用,要看onLayout()函数
  • onLayout():使用layout()函数对所有子控件进行布局
  • onDraw():根据布局的位置绘图

onMeasure()函数与MeasureSpec

  • 布局绘制设计零个过程:测量过程和布局过程,测量过程通过measure()函数来实现,是View树自顶向下遍历,每个View在循环过程中将尺寸细节往下传递,当测量过程完成之后,所有的View都存储了自己的尺寸,布局过程则通过layout()函数来实现,这个函数也是自顶向下的,在这个过程中,每个父View扶额通过计算好的尺寸放置它的子View
onMeasure()函数
//widthMeasureSpec: 当前父类传递过来的建议值的宽
//heightMeasureSpec:当前父类传递过来的建议值的高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
MeasureSpec的组成
  • 它是由mode+size两部分组成的,widthMeasureSpec和heightMeasure转换为二进制数字都是32位,前2位表示模式,后30位表示size
  • **模式分类:**UNSPECIFIED(未指定)EXACTLY(完全)AT_MOST(至多)
    • UNSPECIFIED:父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小,对应的二进制数值:00000000000000000000000000000000
    • EXACTLY:父元素决定子元素的确切大小,子元素被限定在给定的边界里而忽略它本身的大小,对应的二进制数值:01000000000000000000000000000000
    • AT_MOST:子元素至多达到指定大小的值,对应的二进制数值:10000000000000000000000000000000
  • **模式提取:**widthMeasureSpec和heightMeasureSpec是由模式和数值组成的,而且二进制的前两位代表模式
    eg: 模拟模式提取:
int MODE_MASK = 0xc0000000;
public static int getMode(iint measureSpec){
    return (measureSpec & MODE_MASK);
}

public static int getSize(int measureSpec){
    return (measureSpec & ~MODE_MASK);
}

//Android中提供的提取模式的方法
//获取模式
MeasureSpec.getMode(int spec)
//获取数值
MeasureSpec.getSize(int spec)

//具体使用
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMesureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
  • 模式的用处:
  • XML布局和模式的关系:
    • wrap_content->MeasureSpec.AT_MOST
    • match_parent->MeasureSpec.EXACTLY
    • 具体值->MeasureSpec.EXACTLY
onLayout()函数
  • 在ViewGroup中的onLayout()函数的默认行为是
//派生自ViewGroup的类都必须去实现这个函数然后在内部按照规则在子布局中进行布局
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

eg:

public class MyLinLayout extends ViewGroup {
    public MyLinLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取父类传过来的建议的宽度和高度值
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);


        int height = 0;
        int width = 0;
        int count = getChildCount();
        //通过测量所有的子控件来决定他所占位置的大小
        for(int i = 0;i<count;i++){
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            height += childHeight;
            width = Math.max(childWidth, width);
        }
        setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY)?measureWidth:width, (measureHeightMode== MeasureSpec.EXACTLY?measureHeight:height));


    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for(int i = 0;i<count;i++){
            View child = getChildAt(i);
            int childHeight = child.getMeasuredHeight();
            int childWidth = child.getMeasuredWidth();
            child.layout(0, top, childWidth,top+childHeight);
            top+=childHeight;
        }
    }
}

getMeasuredWidth()与getWidth()函数的区别

  • getMeasuredWidth()函数在measure()过程结束后就可以获取到宽度值,而getWidth()函数要在layout()过程结束后才能获取到值
  • getMeasuredWidth()函数中的值是通过setMeasuredDimension()函数来进行设置的;而getWidth()函数中的值则是通过layout(left, top, right, bottom)函数来进行设置的
相关标签: Android自定义动画