美文网首页安卓学习Android自定义View嵌牛IT观察
安卓自定义view(一)- 自定义view的基础知识

安卓自定义view(一)- 自定义view的基础知识

作者: 小怪兽大作战 | 来源:发表于2019-02-26 15:33 被阅读1次

    view的视图架构

    image.png

    每一个Activity都包含的一个window,这个window的实现类是Phone Window。后Phone Window是顶层的view,叫docor view。docor view中有一个叫content的FrameLayout,我们经常在Activity的onCreate中使用setContentView(R.layout.id)设置我们自定义的视图,就是添加到这个叫content的framelayout中。然后上面还有一个TitleActionBar,这个就是TitleBar。

    在content这个view中,是我们在xml中自定义的view。从图中可以看出,我们自定义的view形成了一个树形结构。viewGroup中可以放置若干个view,而由于viewGroup本身也继承了view,所以ViewGroup的子view也可以是另一个Viewgroup,这样形成一个树形结构。

    MeasureSpec

    这是一个很重要的概念,一个view的MeasureSpec可以看作这个view暂定的大小。
    MeasureSpec是一个32位的int值。前两位表示测量模式,后两位表示暂定的测量大小。
    测量模式有三种,分别是:UNSPECIFIED,即父容器对子容器的大小不作任何要求;EXACTLY,父容器已经确定了自容器的精确大小;AT_MOST,父容器指定了一个最大的大小,view不能超过这个大小。

    MeasureSpec由当前view的parentView调用getChildMeasureSpec生成,传入的三个参数分别是,parentView的MeasureSpec(父容器的暂定大小),parentView的左右上下内边距和当前view的LayoutParams(自容器的期望大小)。

    /**
     * Does the hard part of measureChildren: figuring out the MeasureSpec to
     * pass to a particular child. This method figures out the right MeasureSpec
     * for one dimension (height or width) of one child view.
     *
     * 这个方法将根据父容器的MeasureSpec和子View LayoutParams中的宽/高
     * 为子View生成最合适的MeasureSpec
     *
     * @param spec 父容器的MeasureSpec
     * @param padding 父容器的内间距(padding)加上子View的外间距(margin)
     * @param childDimension 子View的LayoutParams中封装的width/height
     * @return 子View的MeasureSpec
     */
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        // ① 对父容器的MeasureSpec进行解包
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
    
        // ② 减去间距,得到最大可用空间
        int size = Math.max(0, specSize - padding);
    
        // 记录子View最终的大小和测量模式
        int resultSize = 0;
        int resultMode = 0;
    
        switch (specMode) {
        // ③ 父容器是精准测量模式
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {               //如果子view在LayoutParam中指定了大小,那么子view的resultSize 就是该大小,模式是EXACTLY
                resultSize = childDimension; 
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {       //如果子view中LayoutParams是MATCH_PARENT,则view的大小等于最大可用大小,测量模式是EXACTLY
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {   //如果子view中LayoutParams是WRAP_CONTENT,则view的大小等于最大可用大小,测量模式是AT_MOST
    
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
    
         // ④ 父容器指定了一个最大可用的空间
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;
    
        // ⑤ 父容器不对子View的大小作出限制
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        // ⑥ 将最终的size和mode打包为子View需要的MeasureSpec
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
    
    

    在view中每个子view的MeasureSpec都是由父容器的MeasureSpec、间距和自容器的LayoutParam来生成,逐级往上,最顶层的view,也就是docor view的MeasureSpec是怎么生成的呢?docor view的MeasureSpec由其上层的window的大小和docor view的自身的LayoutParam来生成。

    相关文章

      网友评论

        本文标题:安卓自定义view(一)- 自定义view的基础知识

        本文链接:https://www.haomeiwen.com/subject/juuayqtx.html