多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 自定義ViewGroup

自定義ViewGroup

來源:程序員人生   發布時間:2016-07-01 13:18:23 閱讀次數:2597次

引子:標準的自定義ViewGroup應當包括甚么

第1部份

  1. 支持wrap_content

    即當ViewGroup的寬、高使用wrap-content時,ViewGroup的高寬根據子View的實際大小來肯定

    如果你不處理的話,“wrap-content”的和 “match-parent”是1樣的

  2. ViewGroup支持Padding

  3. 其子View支持margin

  4. 支持自定義屬性

    例如:gravity

第2部份

  1. 支持轉動效果(固然,是你有轉動的需求的話)

第3部份

  1. 滑動事件沖突處理

    touch事件分發

接下來,我們1步1步實現以上內容

第1步、自己來寫1個橫向的LinearLayout(先實現支持wrap_content和padding)

必須要掌握的內容

  1. onMeasure的寫法

  2. onLayout的寫法

難點

  1. onMeasure中widthMeasureSpec、heightMeasureSpec的理解

    這里不具體解釋這個問題,只需知道,就是由于MeasureSpec的規則致使了,如果你不進行特殊處理,導致wrap-content的效果為match-parent

    展開說1句:兒子的尺寸,不完全由兒子自己決定,與父親傳遞過來的丈量規格是有關系的

  2. onLayout中l,t,r,b的含義

    解釋1下:int l, int t, int r, int b,這4個值

    這4個值,就是通過onMeasure方法丈量后,得到的這個MyViewGroup的左上右下值

    注意:l,t,r,b是斟酌了其所在的父ViewGroup的padding值的(MyviewGroup本身的padding值,固然也斟酌進去了)

    這4個值,大家可以打印出來看看就明白了,這個不清楚的話,很難寫好onLayout方法

    l=自定義ViewGroup的父ViewGroup的leftPadding

    t=自定義ViewGroup的父ViewGroup的topPadding

    r=自定義ViewGroup的父ViewGroup的leftPadding+這個MyViewGroup的寬度(即上文的desireWidth)

    t=自定義ViewGroup的父ViewGroup的topPadding+這個MyViewGroup的高度(即上文的desireHeight)

開始

大家都知道,自定義View要經過onMeasure、onLayout、onDraw3個流程

onMeasure里要完成的內容(再次強調這里說的是自定義ViewGroup,自定義View不是本文討論的范疇)

  1. 要丈量其內部所有的子View的寬高

    measureChild(View child, int parentWidthMeasureSpec,
    int parentHeightMeasureSpec)

    measure(int widthMeasureSpec, int heightMeasureSpec)

    上面兩個方法都可使用,意思是1樣的

    • 第1個是,丈量每個子View的方法
    • 第2個是,讓每個子View去丈量自己的方法
  2. 丈量ViewGroup自己的寬高

    • 你要是懶得支持wrap_content,只需要寫

      super.onMeasure(widthMeasureSpec,heightMeasureSpec);便可,

      會調用父類的流程去丈量你自定義的ViewGroup的寬高(不具體展開源碼,源碼其實調用的是setMeasuredDimension(int measuredWidth, int measuredHeight)

      不支持wrap-content的意思就說:當你設置寬高為wrap-content時候,實際的效果卻是match_content

    • 你要是支持wrap_content的話,
      使用下面的方法來丈量ViewGroup本身的寬高

      setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
      resolveSize(desireHeight, heightMeasureSpec));

      你肯定想當你的ViewGroup寬度為wrap-content時,這個ViewGroup的寬度值是所有子View的寬度之和再加上左右padding(先不斟酌子View的margin),

      這就需要你在丈量每個子View的寬度時,累加所有的子View寬度再加上左右padding值作為ViewGroup的寬度(當ViewGroup的寬度值你設置的是wrap_content時,你要是設100dp,那就直接是100dp了),

      由于本例是橫向排列的ViewGroup,所以,ViewGroup的高度就是子View里最高的那個子View的高度再加上上下padding了

      代碼里的:

      desireWidth=所有子View的寬度累加上和+左右padding

      desireHeight=最高的那個子View的高度+上下padding

  3. onMeasure模板代碼:(只是支持wrap_content和padding,還不支持margin)

    ` @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        // ★1. 計算所有child view 要占用的空間
        desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
        desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
        int count = getChildCount();
        for (int i = 0; i < count; ++i) {
            View v = getChildAt(i);
    
            //1.1 開始遍歷丈量所有的子View,并且根據實際情況累加子View的寬(或高),為了計算全部viewgroup的寬度(高度)
            if (v.getVisibility() != View.GONE) {//不去丈量Gone的子View
                measureChild(v, widthMeasureSpec,
                        heightMeasureSpec);
    
                --------------只需要根據你的需求,更改虛線內部的代碼便可,其余的不用改--------------
                //由于橫向排列,累加所有的子View的寬度
                desireWidth += v.getMeasuredWidth();
                //高度是子View中最高的高度
                desireHeight = Math
                        .max(desireHeight, v.getMeasuredHeight());
                --------------只需要根據你的需求,更改虛線內部的代碼便可,其余的不用改--------------
    
            }
        }
    
        // 1.2 斟酌padding值
        //到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
        desireWidth += getPaddingLeft() + getPaddingRight();
        //高度同理略
        desireHeight += getPaddingTop() + getPaddingBottom();
    
    
        //★2.丈量ViewGroup的寬高,如果不寫這1步,使用wrap_content時效果為match_parent的效果
        // (下面的寫法比較簡潔,《Android群英傳》介紹了另外1種寫法,比這個略微麻煩1點)
        // see if the size is big enough
        desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
        desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
        // super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
                resolveSize(desireHeight, heightMeasureSpec));
    }`
    

onLayout要實現的內容

    ` @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {

            //這個自定義的ViewGroup的有效內容的4個邊界
            final int parentLeft = getPaddingLeft();//這個MyViewGroup的最左側的距離(純潔的內容的左側界,不含padding)
            final int parentTop = getPaddingTop();//這個MyViewGroup的最上邊的距離(純潔的內容的上邊界,不含padding)
            final int parentRight = r - l - getPaddingRight();//這個MyViewGroup的最右側的距離(純潔的內容的右側界,不含padding)

            final int parentBottom = b - t - getPaddingBottom();//這個MyViewGroup的最下邊的距離(純潔的內容的下邊界,不含padding)

            if (BuildConfig.DEBUG)
                Log.d("onlayout", "parentleft: " + parentLeft + "   parenttop: "
                        + parentTop + "   parentright: " + parentRight
                        + "   parentbottom: " + parentBottom+"\n"+ "   l: " + l+ "   t: " + t+ "   r: " + r+ "   b: " + b);

            int left = parentLeft;
            int top = parentTop;

            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View v = getChildAt(i);
                if (v.getVisibility() != View.GONE) {
                    //得到每個子View的丈量后的寬高
                    final int childWidth = v.getMeasuredWidth();
                    final int childHeight = v.getMeasuredHeight();
                    //開始布局每個子View(左、上、右=左+子View寬、下=上+子View高)
                    v.layout(left, top, left + childWidth, top + childHeight);
                    //由于本例是橫向排列的,所以每個子View的left值要遞增
                    left += childWidth;
                }
            }
}`

完全代碼:

`
    public class MyViewGroup extends ViewGroup {

    private int desireWidth;
    private int desireHeight;

    public MyViewGroup(Context context) {
        this(context, null);
    }

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        // ★1. 計算所有child view 要占用的空間
        desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
        desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
        int count = getChildCount();
        for (int i = 0; i < count; ++i) {
            View v = getChildAt(i);

            //1.1 開始遍歷丈量所有的子View,并且根據實際情況累加子View的寬(或高),為了計算全部viewgroup的寬度(高度)
            if (v.getVisibility() != View.GONE) {//不去丈量Gone的子View
                measureChild(v, widthMeasureSpec,
                        heightMeasureSpec);
                //由于橫向排列,累加所有的子View的寬度
                desireWidth += v.getMeasuredWidth();
                //高度是子View中最高的高度
                desireHeight = Math
                        .max(desireHeight, v.getMeasuredHeight());
            }
        }

        // 1.2 斟酌padding值
        //到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
        desireWidth += getPaddingLeft() + getPaddingRight();
        //高度同理略
        desireHeight += getPaddingTop() + getPaddingBottom();


        //★2.丈量ViewGroup的寬高,如果不寫這1步,使用wrap_content時效果為match_parent的效果
        // (下面的寫法比較簡潔,《Android群英傳》介紹了另外1種寫法,比這個略微麻煩1點)
        // see if the size is big enough
        desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
        desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
        //super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
                resolveSize(desireHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {


        //這個自定義的ViewGroup的有效內容的4個邊界
        final int parentLeft = getPaddingLeft();//這個MyViewGroup的最左側的距離(純潔的內容的左側界,不含padding)
        final int parentTop = getPaddingTop();//這個MyViewGroup的最上邊的距離(純潔的內容的上邊界,不含padding)
        final int parentRight = r - l - getPaddingRight();//這個MyViewGroup的最右側的距離(純潔的內容的右側界,不含padding)
        final int parentBottom = b - t - getPaddingBottom();//這個MyViewGroup的最下邊的距離(純潔的內容的下邊界,不含padding)

        if (BuildConfig.DEBUG)
            Log.d("onlayout", "parentleft: " + parentLeft + "   parenttop: "
                    + parentTop + "   parentright: " + parentRight
                    + "   parentbottom: " + parentBottom + "\n" + "   l: " + l + "   t: " + t + "   r: " + r + "   b: " + b);

        int left = parentLeft;
        int top = parentTop;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View v = getChildAt(i);
            if (v.getVisibility() != View.GONE) {
                //得到每個子View的丈量后的寬高
                final int childWidth = v.getMeasuredWidth();
                final int childHeight = v.getMeasuredHeight();
                //開始布局每個子View(左、上、右=左+子View寬、下=上+子View高)
                v.layout(left, top, left + childWidth, top + childHeight);
                //由于本例是橫向排列的,所以每個子View的left值要遞增
                left += childWidth;
            }
        }
    }
}

`

總結:

1般情況,自定義ViewGroup支持wrap-content和padding就夠用了

第2步、自己來寫1個橫向的LinearLayout(實現支持子View的margin)

每個自定義ViewGroup都必須,自定義這個類LayoutParams,和后面的3個方法,否則強轉報異常,
其余沒甚么好說的,計算距離,布局的時候把margin斟酌進去便可

`public class MyViewGroupMargin extends ViewGroup {

    private int desireWidth;
    private int desireHeight;

    public MyViewGroupMargin(Context context) {
        this(context, null);
    }

    public MyViewGroupMargin(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        // ★1. 計算所有child view 要占用的空間
        desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
        desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
        int count = getChildCount();
        for (int i = 0; i < count; ++i) {
            View v = getChildAt(i);
            //1.1 開始遍歷丈量所有的子View,并且根據實際情況累加子View的寬(或高),為了計算全部viewgroup的寬度(高度)
            if (v.getVisibility() != View.GONE) {//不去丈量Gone的子View

                LayoutParams lp = (LayoutParams) v.getLayoutParams();

                //▲變化1:將measureChild改成measureChildWithMargin
                measureChildWithMargins(v, widthMeasureSpec, 0,
                        heightMeasureSpec, 0);
              /*原來:  measureChild(v, widthMeasureSpec,
                        heightMeasureSpec);*/

                //▲變化2:這里在累加所有的子View的寬度時加上他自己的margin
                desireWidth += v.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
                desireHeight = Math
                        .max(desireHeight, v.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);

                /*原來://由于橫向排列,累加所有的子View的寬度
                desireWidth += v.getMeasuredWidth();
                //高度是子View中最高的高度
                desireHeight = Math
                        .max(desireHeight, v.getMeasuredHeight());*/
            }
        }

        // 1.2 斟酌padding值
        //到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
        desireWidth += getPaddingLeft() + getPaddingRight();
        //高度同理略
        desireHeight += getPaddingTop() + getPaddingBottom();


        //★2.丈量ViewGroup的寬高,如果不寫這1步,使用wrap_content時效果為match_parent的效果
        // (下面的寫法比較簡潔,《Android群英傳》介紹了另外1種寫法,比這個略微麻煩1點)
        // see if the size is big enough
        desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
        desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());

        setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
                resolveSize(desireHeight, heightMeasureSpec));
    }



    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int parentLeft = getPaddingLeft();
        final int parentRight = r - l - getPaddingRight();
        final int parentTop = getPaddingTop();
        final int parentBottom = b - t - getPaddingBottom();

        if (BuildConfig.DEBUG)
            Log.d("onlayout", "parentleft: " + parentLeft + "   parenttop: "
                    + parentTop + "   parentright: " + parentRight
                    + "   parentbottom: " + parentBottom);

        int left = parentLeft;
        int top = parentTop;

        int count = getChildCount();
        for (int i = 0; i < count; ++i) {
            View v = getChildAt(i);
            if (v.getVisibility() != View.GONE) {
                LayoutParams lp = (LayoutParams) v.getLayoutParams();
                final int childWidth = v.getMeasuredWidth();
                final int childHeight = v.getMeasuredHeight();


                //▲變化1:左邊要加上這個子View的左邊margin
                left += lp.leftMargin;
                //▲變化2:上側要加上子View的margin
                top = parentTop + lp.topMargin;

                if (BuildConfig.DEBUG) {
                    Log.d("onlayout", "child[width: " + childWidth
                            + ", height: " + childHeight + "]");
                    Log.d("onlayout", "child[left: " + left + ", top: "
                            + top + ", right: " + (left + childWidth)
                            + ", bottom: " + (top + childHeight));
                }
                v.layout(left, top, left + childWidth, top + childHeight);
                //▲變化3:由于是橫向排列的,所以下1個View的左邊加上這個view的右邊的margin(如果是縱向排列的則對應改變top)
                left += childWidth + lp.rightMargin;

            }
        }
    }

    //★★★★★★★★★★★★★★★★★★★★★★★要使用margin必須寫下面的方法★★★★★★★★★★★★★★★★★★★★★
    //***開始***每個自定義ViewGroup都必須,自定義這個類LayoutParams,和后面的3個方法,否則強轉報異常,模板代碼照抄便可**************

    public static class LayoutParams extends MarginLayoutParams {


        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);

        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }



        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
    }

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(
            AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(
            ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }
    //***結束***每個自定義ViewGroup都必須,自定義這個類LayoutParams,和后面的3個方法,否則強轉報異常,模板代碼照抄便可**************
}

`

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美一区二区三区在线视频 | 久久爱伊人 | 字幕网yellow 91在线 | 亚洲噜噜噜噜噜影院在线播放 | 2017琪琪理论影院 | 日韩一级欧美一级一级国产 | 亚洲福利院 | 女人18毛片特级一级免费视频 | 精品国产一级毛片 | 91久久精品国产91性色tv | 爽一爽色视频 | 久久久久欧美精品 | 亚洲欧美系列 | 变态 另类 国产 亚洲 | xxxx18野外xxxxfreexxxx日本 | 国产精品久久一区一区 | 国产午夜精品片一区二区三区 | 性欧美videofree丝袜 | 日韩精品国产精品 | 琪琪777影院在线观看 | 中文字幕在第10页线观看 | 国产精品自拍在线观看 | 空姐一级毛片 | xxxxx国产老太 | 国产v国产v片大片线观看网站 | 亚洲资源在线播放 | 亚洲综合激情另类小说区 | 国产深夜福利在线观看网站 | aa黄色 | 日韩欧美一区二区三区不卡在线 | 高清一区二区三区四区五区 | 免费精品久久久视频 | 亚洲爱v| 在线播放精品视频 | 在线播放www | free hd video 性欧美 | 欧美人与z0z0xxxx | 国产欧美另类久久精品91 | 成年人视频免费在线观看 | 亚洲手机在线手机观看高清hd | 午夜视频网址 |