Android自定义控件(二)_2015-03-22

在Android自定义控件(一)中主要讲述的时一些绘制的类以及其中的常见方法。自定义控件的步骤基本可以概括为:控件绘制,控件测量以及位置分配 和控件的交互。此文中我们将对控件测量中常用的类和方法进行介绍。

1,View的测量在onMeasure中进行。

MeasureSpec类
MeasureSpec中又三个Mode常量:

  • UNSPECIFIED表示未指定,爹不会对儿子作任何的束缚,儿子想要多大都可以;
  • EXACTLY表示完全的,意为儿子多大爹心里有数,爹早已算好了;
  • AT_MOST表示至多,爹已经为儿子设置好了一个最大限制,儿子你不能比这个值大

view的最终测量尺寸是由view本身何其父容器共同决定的,父容器所谓的“意图”其实就由上述三个常量值表现,既然如此我们就该对这三个Mode常量做一个判断才行,不然怎么知道爹的意图呢

@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    // 声明一个临时变量来存储计算出的测量值  
    int resultWidth = 0;  
  
    // 获取宽度测量规格中的mode  
    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);  
  
    // 获取宽度测量规格中的size  
    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);  
  
    /* 
     * 如果爹心里有数 
     */  
    if (modeWidth == MeasureSpec.EXACTLY) {  
        // 那么儿子也不要让爹难做就取爹给的大小吧  
        resultWidth = sizeWidth;  
    }  
    /* 
     * 如果爹心里没数 
     */  
    else {  
        // 那么儿子可要自己看看自己需要多大了  
        resultWidth = mBitmap.getWidth();  
  
        /* 
         * 如果爹给儿子的是一个限制值 
         */  
        if (modeWidth == MeasureSpec.AT_MOST) {  
            // 那么儿子自己的需求就要跟爹的限制比比看谁小要谁  
            resultWidth = Math.min(resultWidth, sizeWidth);  
        }  
    }  
  
    int resultHeight = 0;  
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);  
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);  
  
    if (modeHeight == MeasureSpec.EXACTLY) {  
        resultHeight = sizeHeight;  
    } else {  
        resultHeight = mBitmap.getHeight();  
        if (modeHeight == MeasureSpec.AT_MOST) {  
            resultHeight = Math.min(resultHeight, sizeHeight);  
        }  
    }  
  
    // 设置测量尺寸  
    setMeasuredDimension(resultWidth, resultHeight);  
}  

如上代码所示我们从父容器传来的MeasureSpec中分离出了mode和size,size只是一个期望值我们需要根据mode来计算最终的size,如果父容器对子元素没有一个确切的大小那么我们就需要尝试去计算子元素也就是我们的自定义View的大小,而这部分大小更多的是由我们也就是开发者去根据实际情况计算的,这里我们模拟的是一个显示图片的控件,那么控件的实际大小就应该跟我们的图片一致,但是虽然我们可以做出一定的决定也要考虑父容器的限制值,当mode为AT_MOST时size则是父容器给予我们的一个最大值,我们控件的大小就不应该超过这个值。下面是运行效果:


上述代码中涉及的到常用方法:

  • MeasureSpec.getMod(int );获取到尺寸的模式。根据所获取到的模式信息来得到父布局的意图。
  • MeasureSpec.getSize(int); 获取到父意图中给定的测量大小。

2,ViewGroup中与测量相关的另外几个方法

ViewGroup对子元素测量师从measureChildren()方法开始的。

  • measureChildren 对所有子元素进行测量。
    measureChildren的逻辑很简单,通过父容器传入的父容器的widthMeasureSpec和heightMeasureSpec遍历子元素并调用measureChild方法去测量每一个子元素的宽高
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  
    final int size = mChildrenCount;  
    final View[] children = mChildren;  
    for (int i = 0; i < size; ++i) {  
        final View child = children[i];  
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {  
            measureChild(child, widthMeasureSpec, heightMeasureSpec);  
        }  
    }  
} 
  • measureChildWithMargins
  • measureChild 测量单个孩子的大小。此方法会调用getChildMeasureSpec来进行测量孩子的大小。
  • getChildMeasureSpec:// 测量子元素并考虑外边距
    一个View的大小由其父容器的测量规格MeasureSpec和View本身的布局参数LayoutParams共同决定
  • ViewGroup.onLayout()方法ViewGroup中的onLayout()方法是一个抽象方法,这意味着你在继承时必须实现,onLayout的目的是为了确定子元素在父容器中的位置,那么这个步骤理应该由父容器来决定而不是子元素

-View.layout(l,t,r,b); 给字View分配位置。
-View.getPaddingXXX();获取到View的内边距。

  • View.requestLayout方法的意义在于如果你的操作有可能会让控件的尺寸或位置发生改变那么就可以调用该方法请求布局,调用该方法后framework会尝试调用measure对控件重新测量:
public void setBitmap(Bitmap bitmap) {  
    this.mBitmap = bitmap;  
    requestLayout();  
    invalidate();  
}  ```
`requestLayout方法和invalidate方法并非都必需调用的,比如我们有一个更改字体颜色的方法,但是,如上我们所说,如果一旦尺寸大小或位置发生了变化,那么我们最好重新布局并迫使视图重绘,比如我们有个改变字体大小的方法`

public void setTextColor(int color) {
mPaint.setColor(color);
invalidate();
}

public void setTextSize(int size) {
mPaint.setTextSize(size);
requestLayout(); //需要调用重新测量
invalidate();
}

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/294186-0227ea1fce30c7b5.png)

-View.invalidate()  再次调用onDraw()进行渲染绘制。
补充:一个界面窗口的元素构成:

![一个界面窗口的元素构成.png](http://upload-images.jianshu.io/upload_images/294186-cf2aa15218fbfe27.png)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容