• Android轶事之View要去大保健?View大小自己决定?


    -“爹,我要吃糖”
    -“好哒儿子”
    -“爹,我要吃包包”
    - “好哒儿子”
    - “爹,我要吃串串”
    - “好哒儿子”
    - “爹,我要大保健”
    - (啪啪啪,三耳光)

    儿子是一定要听爹话的,那么在Android世界里,是不是这样呢? 今天就来和大家讨论一下 View 父子之间的琐事。

    大家都知道,儿子肯定有自己想做的事情,也就是有自己的思想。那么转换到Android的View上面呢,就是测量啦。View自己会测量自己,告诉父布局他自身有多大,要占多大空间。可儿子就能胆大妄为,想怎么样就怎么样吗,答案是否定的。

    View的最终大小不是由自己决定的,而是由layout决定。

    这里我们来做一个实验。

    比如,新写一个矩形自定义view:

    public class RectView extends View {
        private Paint mPaint;
        private int mWidth;
        private int mHeight;
    
        public RectView(Context context) {
            this(context, null);
        }
    
        public RectView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
    
        }
    
        public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mPaint = new Paint();
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            if(widthMode == MeasureSpec.AT_MOST){
                mWidth = 200;
            }
    
    
            if(heightMode == MeasureSpec.AT_MOST){
                mHeight = 200;
    
            }
            setMeasuredDimension(mWidth,mHeight);
            }
    
        @Override
        protected void onDraw(Canvas canvas) {
            canvas.drawRect(0,0,mWidth,mHeight,mPaint);
            super.onDraw(canvas);
        }
    }

    这里简单处理了一下 这个view的wrap_content时候的大小,写死为200px(注意是px不是dp)。
    放到一个linearLayout下,看看效果

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:orientation="vertical"
        tools:context="com.wingsofts.father.MainActivity">
    
       <com.wingsofts.father.RectView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
        <com.wingsofts.father.RectView
    
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    

    这里写图片描述
    可以看到,在wrap_content的情况下,两个view的大小跟自己预期的一样。(这种情况就是:“老爹,我想吃包子” “好哒儿子”)。

    其实这篇文章的起源来与一个layout函数,大家都知道layout函数决定了一个view在什么位置。但是大家有没有想过,既然是先测量再layout的,为什么layout函数需要四个参数?既然测量知道view的大小了,那么只需要左上角x,y两个坐标不就好了吗?

    layout(int l,int t,int r,int b);

    难道测量的大小并不能真正决定view自己的大小吗?测量出来的值只是一个期望值,而不是最终的值吗,最终还是要听老爹话吗?我们来实验一下就知道了。(“老爹,我要大保健”)

    自己新建一个viewgroup,继承自LinearLayout

    public class MyLinear extends LinearLayout {
        public MyLinear(Context context) {
            this(context, null);
        }
    
        public MyLinear(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyLinear(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            //调用父类方法
            super.onLayout(changed, l, t, r, b);
            //将第一个view布局到0,0,50,50位置
            View v = getChildAt(0);
            v.layout(0,0,50,50);
        }
    }
    

    这时候布局只是把LinearLayout改成MyLinear。
    这里写图片描述
    (儿贼,听说你要大保健,先来尝尝爹的大宝剑!咔咔咔嚓,给削小了)
    这里写图片描述

    呃呃呃。。。看来在android世界中,儿子也是要听老子的啊。一般的要求绝对满足,有非分之想,过不了老子那关啊。

    • 再来个彩蛋吧。。

    话说,大家一定有过一个拿空白View(其实用Space标签比较好)去占一定大小的经历(损招,但是有时候很好用)。。

    比如通常希望一个空白view占据50dp的宽度,高度写为wrap_content 。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.wingsofts.father.MainActivity">
    
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
        <View
            android:layout_width="50dp"
            android:layout_height="wrap_content" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    

    那么你就会发现。。
    这里写图片描述

    WTF??? 为毛是全屏的高度。我写的是wrap_content啊。。难道View的wrap_content就是全屏??

    不要急。。我们来read the fxxking source code ..

    直接定位到view的onMeasure方法

       protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
    

    额。。没啥东西呀,继续跳到getDefaultSize()

    public static int getDefaultSize(int size, int measureSpec) {
            int result = size;
            int specMode = MeasureSpec.getMode(measureSpec);
            int specSize = MeasureSpec.getSize(measureSpec);
    
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            }
            return result;
        }

    呃呃。。 问:小白为什么长得像他哥哥。

    这下真像大白了:原来,在View自身wrap_content(也就是AT_MOST模式),switch的case下面是没有break的,也就是说,

    如果View没有处理AT_MOST模式,那么wrap_content自动当成match_parent处理。

    呃呃。。跑题了。。。 好吧,没啥说的了,写这篇文章就是告诉大家一个道理。儿子就得听爹的话。。。 拜拜。如果你喜欢我的博客,记得关注我。

  • 相关阅读:
    OpenEuler 中C与汇编的混合编程
    实验四 Web服务器2
    实验四 Web服务器1
    OpenEuler中C语言中的函数调用测试
    第14章学习笔记(20191213兰毅达)
    第13章学习笔记(20191213兰毅达)
    第12章学习笔记(20191213兰毅达)
    冲刺day5
    Oracle中Sequence使用
    Oracle中dual表的用途
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333539.html
Copyright © 2020-2023  润新知