日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開源 > 綜合技術(shù) > 常用的自定義View例子(流布式布局)

常用的自定義View例子(流布式布局)

來源:程序員人生   發(fā)布時(shí)間:2016-08-15 08:42:55 閱讀次數(shù):2561次

經(jīng)常使用的自定義View例子

在Android開發(fā)中,我們常常會(huì)遇到流布式的布局,常常會(huì)用來1些標(biāo)簽的顯示,比如qq中個(gè)人便簽,搜索框下方提示的詞語(yǔ),這些是指都是流布式的布局,今天我就我們平常開放中遇到的流布式布局坐1些總結(jié)

轉(zhuǎn)載請(qǐng)注明博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/51765428
源碼下載地址:https://github.com/gdutxiaoxu/CustomViewDemo.git
效果圖

1. 先給大家看1下效果

  • 圖1


  • 圖2


仔細(xì)視察,我們可以知道圖2實(shí)際上是圖1效果的升級(jí)版,圖1當(dāng)我們控件的寬度超過這1行的時(shí)候,剩余的寬度它不會(huì)自動(dòng)散布到每一個(gè)控件中,而圖2的效果當(dāng)我們換行的時(shí)候,如控件還沒有占滿這1行的時(shí)候,它會(huì)自動(dòng)把剩余的寬度散布到每一個(gè)控件中

2.空話不多說了,大家來直接看來看1下圖1的源碼

1)代碼以下

`
/**
 * 博客地址:http://blog.csdn.net/gdutxiaoxu
 * @author xujun
 * @time 2016/6/20 23:49.
 */
public class SimpleFlowLayout extends ViewGroup {
    private int  verticalSpacing = 20;

    public SimpleFlowLayout(Context context ) {
        super(context);
    }

    /**
     * 重寫onMeasure方法是為了肯定終究的大小
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
//處理Padding屬性,讓當(dāng)前的ViewGroup支持Padding
        int widthUsed = paddingLeft + paddingRight;
        int heightUsed = paddingTop + paddingBottom;

        int childMaxHeightOfThisLine = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
//                已用的寬度
                int childUsedWidth = 0;
//                已用的高度
                int childUsedHeight = 0;
//                調(diào)用ViewGroup本身的方法丈量孩子的寬度和高度,我們也能夠自己根據(jù)MeasureMode來丈量
                measureChild(child,widthMeasureSpec,heightMeasureSpec);
                childUsedWidth += child.getMeasuredWidth();
                childUsedHeight += child.getMeasuredHeight();
//處理Margin,支持孩子的Margin屬性
                Rect marginRect = getMarginRect(child);
                int leftMargin=marginRect.left;
                int rightMargin=marginRect.right;
                int topMargin=marginRect.top;
                int bottomMargin=marginRect.bottom;

                childUsedWidth += leftMargin + rightMargin;
                childUsedHeight += topMargin + bottomMargin;
//總寬度沒有超過本行
                if (widthUsed + childUsedWidth < widthSpecSize) {
                    widthUsed += childUsedWidth;
                    if (childUsedHeight > childMaxHeightOfThisLine) {
                        childMaxHeightOfThisLine = childUsedHeight;
                    }
                } else {//總寬度已超過本行
                    heightUsed += childMaxHeightOfThisLine + verticalSpacing;
                    widthUsed = paddingLeft + paddingRight + childUsedWidth;
                    childMaxHeightOfThisLine = childUsedHeight;
                }

            }

        }
//加上最后1行的最大高度
        heightUsed += childMaxHeightOfThisLine;
        setMeasuredDimension(widthSpecSize, heightUsed);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        /**
         * 為了 支持Padding屬性
         */
        int childStartLayoutX = paddingLeft;
        int childStartLayoutY = paddingTop;

        int widthUsed = paddingLeft + paddingRight;

        int childMaxHeight = 0;

        int childCount = getChildCount();
//擺放每個(gè)孩子的高度
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                int childNeededWidth, childNeedHeight;
                int left, top, right, bottom;

                int childMeasuredWidth = child.getMeasuredWidth();
                int childMeasuredHeight = child.getMeasuredHeight();

                Rect marginRect = getMarginRect(child);
                int leftMargin=marginRect.left;
                int rightMargin=marginRect.right;
                int topMargin=marginRect.top;
                int bottomMargin=marginRect.bottom;
                childNeededWidth = leftMargin + rightMargin + childMeasuredWidth;
                childNeedHeight = topMargin + topMargin + childMeasuredHeight;

//                沒有超過本行
                if (widthUsed + childNeededWidth <= r - l) {
                    if (childNeedHeight > childMaxHeight) {
                        childMaxHeight = childNeedHeight;
                    }
                    left = childStartLayoutX + leftMargin;
                    top = childStartLayoutY + topMargin;
                    right = left + childMeasuredWidth;
                    bottom = top + childMeasuredHeight;
                    widthUsed += childNeededWidth;
                    childStartLayoutX += childNeededWidth;
                } else {
                    childStartLayoutY += childMaxHeight + verticalSpacing;
                    childStartLayoutX = paddingLeft;
                    widthUsed = paddingLeft + paddingRight;
                    left = childStartLayoutX + leftMargin;
                    top = childStartLayoutY + topMargin;
                    right = left + childMeasuredWidth;
                    bottom = top + childMeasuredHeight;
                    widthUsed += childNeededWidth;
                    childStartLayoutX += childNeededWidth;
                    childMaxHeight = childNeedHeight;
                }
                child.layout(left, top, right, bottom);
            }
        }
    }

    private Rect getMarginRect(View child) {
        LayoutParams layoutParams = child.getLayoutParams();
        int leftMargin = 0;
        int rightMargin = 0;
        int topMargin = 0;
        int bottomMargin = 0;
        if (layoutParams instanceof MarginLayoutParams) {
            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) layoutParams;
            leftMargin = marginLayoutParams.leftMargin;
            rightMargin = marginLayoutParams.rightMargin;
            topMargin = marginLayoutParams.topMargin;
            bottomMargin = marginLayoutParams.bottomMargin;

        }
        return new Rect(leftMargin, topMargin, rightMargin, bottomMargin);
    }

}`

2)思路解析

  1. 首先我們重寫onMeasure方法,在OnMeasure方法里面我們調(diào)用measureChild()這個(gè)方法去獲得每一個(gè)孩子的寬度和高度,每次增加1個(gè)孩子我們履行 widthUsed += childUsedWidth;
  2. 添加完1個(gè)孩子以后我們判斷widthUsed是夠超越控件本身的最大寬度widthSpecSize,
    若沒有超過履行

       widthUsed += childUsedWidth;
       if (childUsedHeight > childMaxHeightOfThisLine) {
        childMaxHeightOfThisLine = childUsedHeight;
        }  
    

    超過控件的寬度履行

        heightUsed += childMaxHeightOfThisLine + verticalSpacing;
        widthUsed = paddingLeft + paddingRight + childUsedWidth;
        childMaxHeightOfThisLine = childUsedHeight;  
    

    最后調(diào)用 setMeasuredDimension(widthSpecSize, heightUsed);這個(gè)方法去設(shè)置它的大小
    3.在OnLayout方法里面,所做的工作就是去擺放每個(gè)孩子的位置 ,判斷需不需要換行,不需要更改left值,需要換行,更改top值

3)注意事項(xiàng)

講授之前,我們先來了解1下1個(gè)基本知識(shí)

從這張圖片里面我們可以得出這樣結(jié)論

  1. Width=控件真實(shí)的寬度(realWidth)+PaddingLeft+PaddingRight
  2. margin是子控件相對(duì)父控件的距離

注意事項(xiàng)

  1. 為了支持控件本身的padding屬性,我們做了處理,主要代碼以下

      int widthUsed = paddingLeft + paddingRight;
      int heightUsed = paddingTop + paddingBottom;
       ----------
      if (widthUsed + childUsedWidth < widthSpecSize) {
              widthUsed += childUsedWidth;
              if (childUsedHeight > childMaxHeightOfThisLine) {
                    childMaxHeightOfThisLine = childUsedHeight;
                }
            } 
    
  2. 為了支持子控件的margin屬性,我們一樣也做了處理

                Rect marginRect = getMarginRect(child);
                int leftMargin=marginRect.left;
                int rightMargin=marginRect.right;
                int topMargin=marginRect.top;
                int bottomMargin=marginRect.bottom;
    
                childUsedWidth += leftMargin + rightMargin;
                childUsedHeight += topMargin + bottomMargin;
    

即我們?cè)谟?jì)算孩子所占用的寬度和高度的時(shí)候加上margin屬性的高度,接著在計(jì)算需要孩子總共用的寬高度的時(shí)候加上每一個(gè)孩子的margin屬性的寬高度,這樣自然就支持了孩子的margin屬性了

4.缺點(diǎn)

以下圖所見,在控件寬度良莠不齊的情況下,控件換行會(huì)留下1些剩余的寬度,作為想寫出魯棒性的代碼的我們會(huì)覺得別扭,因而我們相處了解決辦法。

解決方法見下面

圖2源碼解析

空話不多說,先看源碼

/**
 * 博客地址:http://blog.csdn.net/gdutxiaoxu
 * @author xujun
 * @time 2016/6/26 22:54.
 */
public class PrefectFlowLayout extends ViewGroup {


    public PrefectFlowLayout(Context context) {
        super(context);
    }

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

    public PrefectFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private int parentWidthSize;//父容器寬度
    private int horizontalSpacing = 16;//水平間距
    private int verticalSpacing = 16;//垂直間距
    private Line currentLine;//當(dāng)前行
    private List<Line> mLines = new ArrayList<>();//所有行的集合
    private int userWidth = 0;//當(dāng)前行已使用寬度

    /**
     * 行對(duì)象
     */
    private class Line {
        private List<View> children;//1行里面所添加的子View集合
        private int height;//當(dāng)前行高度
        private int lineWidth = 0;//當(dāng)前行已使用寬度

        public Line() {
            children = new ArrayList<>();
        }

        /**
         * 添加1個(gè)子控件
         *
         * @param child
         */
        private void addChild(View child) {
            children.add(child);
            if (child.getMeasuredHeight() > height) {
                height = child.getMeasuredHeight();//當(dāng)前行高度以子控件最大高度為準(zhǔn)
            }
            lineWidth += child.getMeasuredWidth();//將每一個(gè)子控件寬度進(jìn)行累加,記錄使用的寬度
        }

        /**
         * 獲得行的高度
         *
         * @return
         */
        public int getHeight() {
            return height;
        }

        /**
         * 獲得子控件的數(shù)量
         *
         * @return
         */
        public int getChildCount() {
            return children.size();
        }

        /**
         * 放置每行里面的子控件的位置
         *
         * @param l 距離最左側(cè)的距離
         * @param t 距離最頂真?zhèn)€距離
         */
        public void onLayout(int l, int t) {
            //當(dāng)前行使用的寬度,等于每一個(gè)子控件寬度之和+子控件之間的水平距離
            lineWidth += horizontalSpacing * (children.size() - 1);
            int surplusChild = 0;
            int surplus = parentWidthSize - lineWidth;//剩余寬度
            if (surplus > 0) {
                //如果有剩余寬度,則將剩余寬度平分給每個(gè)子控件
                surplusChild = (int) (surplus / children.size()+0.5);
            }
            for (int i = 0; i < children.size(); i++) {
                View child = children.get(i);
                child.getLayoutParams().width=child.getMeasuredWidth()+surplusChild;
                if (surplusChild>0){//如果長(zhǎng)度改變了后,需要重新丈量,否則布局中的屬性大小還會(huì)是原來的大小
                    child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth()+surplusChild,MeasureSpec.EXACTLY)
                            ,MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY));
                }
                child.layout(l, t, l + child.getMeasuredWidth(), t + child.getMeasuredHeight());
                l += child.getMeasuredWidth() + horizontalSpacing;
            }
        }
    }
    //  getMeasuredWidth()   控件實(shí)際的大小
    // getWidth()  控件顯示的大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //將之前丈量的數(shù)據(jù)進(jìn)行清空,以防復(fù)用時(shí)影響下次丈量
        mLines.clear();
        currentLine = null;
        userWidth = 0;
        //獲得父容器的寬度和模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        parentWidthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
        //獲得父容器的高度和模式
        int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
        int childWidthMode, childHeightMode;
        //為了丈量每一個(gè)子控件,需要指定每一個(gè)子控件的丈量規(guī)則
        //子控件設(shè)置為WRAP_CONTENT,具體丈量規(guī)則詳見,ViewGroup的getChildMeasureSpec()方法
        if (widthMode == MeasureSpec.EXACTLY) {
            childWidthMode = MeasureSpec.AT_MOST;
        } else {
            childWidthMode = widthMode;
        }
        if (heigthMode == MeasureSpec.EXACTLY) {
            childHeightMode = MeasureSpec.AT_MOST;
        } else {
            childHeightMode = heigthMode;
        }
        //獲得到子控件高和寬的丈量規(guī)則
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidthSize, childWidthMode);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, childHeightMode);
        currentLine = new Line();//創(chuàng)建第1行
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            //丈量每個(gè)孩子
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            int childMeasuredWidth = child.getMeasuredWidth();//獲得當(dāng)前子控件的實(shí)際寬度
            userWidth += childMeasuredWidth;//讓當(dāng)前行使用寬度加上當(dāng)前子控件寬度
            if (userWidth <= parentWidthSize) {
                //如果當(dāng)前行使用寬度小于父控件的寬度,則加入該行
                currentLine.addChild(child);
                userWidth += horizontalSpacing;//當(dāng)前行使用寬度加上子控件之間的水平距離
                if (userWidth > parentWidthSize) {//如果當(dāng)前行加上水平距離后超越父控件寬度,則換行
                    newLine();
                }
            } else {
                if (currentLine.getChildCount() == 0) {//以防出現(xiàn)1個(gè)子控件寬度超過父控件的情況出現(xiàn)
                    currentLine.addChild(child);
                }
                newLine();
                currentLine.addChild(child);//并將超越范圍確當(dāng)前的子控件加入新的行中
                userWidth = child.getMeasuredWidth()+horizontalSpacing;//并將使用寬度加上子控件的寬度;
            }
        }
        if (!mLines.contains(currentLine)) {//加入最后1行,由于如果最后1行寬度不足父控件寬度時(shí),就未換行
            mLines.add(currentLine);
        }
        int totalHeight = 0;//總高度
        for (Line line : mLines) {
            totalHeight += line.getHeight() + verticalSpacing;//總高度等于每行的高度+垂直間距
        }
        setMeasuredDimension(parentWidthSize + getPaddingLeft() + getPaddingRight(),
                resolveSize(totalHeight + getPaddingTop() + getPaddingBottom(), heightMeasureSpec));//resolveSize(),將實(shí)際高度與父控件高度進(jìn)行比較,選取最適合的
    }

    /**
     * 換行
     */
    private void newLine() {
        mLines.add(currentLine);//記錄之前行
        currentLine = new Line();//重新創(chuàng)建新的行
        userWidth = 0;//將使用寬度初始化
    }

    /**
     * 放置每一個(gè)子控件的位置
     *
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        l += getPaddingLeft();
        t += getPaddingTop();
        for (int i = 0; i < mLines.size(); i++) {
            Line line = mLines.get(i);
            line.onLayout(l, t);//設(shè)置每行的位置,每行的子控件由其自己去分配
            t += line.getHeight() + verticalSpacing;//距離最頂真?zhèn)€距離,即每行高度和垂直間距的累加
        }
    }

    /**
     * 獲得子控件的丈量規(guī)則
     *
     * @param mode 父控件的丈量規(guī)則
     * @return 子控件設(shè)置為WRAP_CONTENT,具體丈量規(guī)則詳見,ViewGroup的getChildMeasureSpec()方法
     */
    private int getMode(int mode) {
        int childMode = 0;
        if (mode == MeasureSpec.EXACTLY) {
            childMode = MeasureSpec.AT_MOST;
        } else {
            childMode = mode;
        }
        return childMode;
    }
}

2.思路解析

  1. 對(duì)照?qǐng)D1的實(shí)現(xiàn)思路,我們封裝了Line這個(gè)內(nèi)部類,看到這個(gè)名字,相信大家都猜到是甚么意思了,其實(shí)就是1個(gè)Line實(shí)例對(duì)象代表1行,Line里面的List children用來寄存孩子

    private List<View> children;//1行里面所添加的子View集合
    
  2. Line里面還封裝了void onLayout(int l, int t)方法,即自己去造訪每一個(gè)孩子的位置,
    實(shí)現(xiàn)剩余的寬度平均分配,主要體現(xiàn)在這幾行代碼

           if (surplus > 0) {
                //如果有剩余寬度,則將剩余寬度平分給每個(gè)子控件
                surplusChild = (int) (surplus / children.size()+0.5);
            }
          -------
    

    //重新分配每一個(gè)孩子的大小 child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth()+surplusChild,MeasureSpec.EXACTLY)
    ,MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY));

今天就寫到這里了,有時(shí)間再來補(bǔ)充,最近考試比較忙,已好久沒有更新博客了。

源碼下載地址:https://github.com/gdutxiaoxu/CustomViewDemo.git

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 午夜精品久久久久久久久久久久久 | 999精品嫩草久久久久久99 | 国产精品黄色小视频 | 国产精品高清一区二区三区 | 久久91 | 日韩精品视频中文字幕 | 久久久成人精品 | 黄性视频 | 国产福利不卡 | 免费黄色一级大片 | 日韩精品视频免费在线观看 | 国产在线精品二区 | 亚洲免费在线 | 亚洲 欧美 视频 | yw.139尤物在线精品视频 | 久99视频 | 国产日韩精品在线 | 日韩一区二区三区在线看 | 国产精品久久中文字幕 | 另类 欧美 日韩 国产 在线 | 日韩综合久久 | 国产一区二区三区久久 | 91av在线免费观看 | 日日夜夜av| 久久精品国产亚洲一区二区三区 | 中文资源在线观看 | 精品久久久一区二区 | 国产一区二区免费在线观看 | 伊人天天| 欧美另类视频在线 | 91一区二区 | 久久国产精品久久久久久久久久 | 久久久亚洲国产 | 国产视频第一区 | 综合视频一区 | 精品一区视频 | 亚洲乱码一区二区三区三上悠亚 | 久久国产高清 | av三级在线播放 | 国内精品视频一区 | 99久免费精品视频在线观78 |