美文网首页
自定义ViewGroup——FlowLayout

自定义ViewGroup——FlowLayout

作者: qmr777 | 来源:发表于2017-04-20 22:39 被阅读0次

    地址 FlowLayout
    第一篇博客,从简单的自定义View写起吧
    1.需求
    与淘宝的历史搜索类似,当前行空间足够时添加到当前行,否则自动换行
    支持padding和子View的margin

    2.效果图

    FlowLayout.jpg

    3.实现
    1.onMeasure
    <pre>
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (getChildCount() == 0) { //子view数量为0,不需要测量了
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    return;
    }

        measureChildren(widthMeasureSpec, heightMeasureSpec);
    
        final int childCount = getChildCount();
    
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
    
        final int maxWidth = width - getPaddingEnd();
    
        int currentWidth = getPaddingStart();
    
        int currentTop = getPaddingTop();
    
        View child = getChildAt(0);
        MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        currentTop += lp.topMargin;
    
    
        for (int i = 0; i < childCount; i++) {
            child = getChildAt(i);
            lp = (MarginLayoutParams) child.getLayoutParams();
            int calcRight = (currentWidth + getViewWidth(child) + lp.leftMargin);
            if (calcRight <= maxWidth) {
                currentWidth = currentWidth + child.getMeasuredWidth() + lp.rightMargin;
                Log.i(TAG, "onMeasure不换行: " + currentWidth);
                //currentWidth = currentWidth + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
                //doNothing
            } else {//换行
                currentTop = currentTop + lp.topMargin + lp.bottomMargin + getViewHeight(child);
                currentWidth = getPaddingStart() + lp.leftMargin;
                //calcRight = (currentWidth + getViewWidth(child));//currentWidth变了 需要重新算
                //child.layout(currentWidth, currentTop, calcRight, currentTop + getViewHeight(child));
                Log.i(TAG, "onMeasure换行: " + currentWidth);
                currentWidth += child.getMeasuredWidth() + lp.rightMargin;
            }
        }//end for
    
        currentTop = currentTop + child.getMeasuredHeight() + lp.bottomMargin;
    
        setMeasuredDimension(width,heightMode == MeasureSpec.EXACTLY ? height : currentTop);
    
    }
    

    </pre>

    首先,通过measureChildren方法通知所有子view测量自己,随后获取到系统提供给我们的宽高和测量模式。

    测量过程十分简单,能摆放子view的宽度maxWidth是Flowlayout的宽度减去左右padding,所以只要用一个值记录下当前行的宽度,小于等于maxWidth就继续加,大于就归零并且换行。这里每一个子view高度margin都是相同的,所以随便拿一个当成当前行高就好了。

    2.onLayout
    <pre>

    getViewHeight(child)就是child.getMeasureHeight</br>

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    //super.onLayout();
    int currentWidth = getPaddingStart();
    int currentTop = getPaddingTop();
    int childCount = getChildCount();
    int maxWidth = getWidthWithPadding();

        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            child.setTag(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            currentWidth += lp.leftMargin;
            //rowHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            int calcRight = (currentWidth + getViewWidth(child));
            if (calcRight <= maxWidth) {
                child.layout(currentWidth, currentTop, calcRight, currentTop + getViewHeight(child));
                currentWidth += child.getMeasuredWidth() + lp.rightMargin;
                //doNothing
            } else {//需要换列
                currentTop = currentTop + lp.topMargin + lp.bottomMargin + getViewHeight(child);
                currentWidth = getPaddingStart() + lp.leftMargin;
                calcRight = (currentWidth + getViewWidth(child));//currentWidth变了 需要重新算
                if(calcRight > maxWidth)
                    calcRight = maxWidth;
                child.layout(currentWidth, currentTop, calcRight, currentTop + getViewHeight(child));
                currentWidth += child.getMeasuredWidth() + lp.rightMargin;
            }
        }
    }
    

    </pre>

    onLayout中的代码和onMeasure十分相似,重点就在
    <code>child.layout(currentWidth,currentTop,calcRight,currentTop+getViewHeight(child));
    </code>
    这个方法接受4个参数,分别是子view在父view中的左 上 右 下。注意的是Android中的坐标系x轴正向是右,但是y轴的正向是下。</br>
    到这里 FlowLayout基本上就实现了。</br>

    相关文章

      网友评论

          本文标题:自定义ViewGroup——FlowLayout

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