Android流式布局实现

Android流式布局实现

Android流式布局实现

上一篇给大家介绍了一下 Android根据标签长度自动换行,后来在开发实际中发现了这个自定义控件在addview(textview)时,修改textview显示的内容时,这个自定义控件不能自动修改宽度去适应布局的变化,这一篇经过查找资料后摸索了一个比较流行的流式布局,这个流式布局在各大app上都非常的手欢迎(天猫、京东等的购物车都采用了这个流式布局)

接下来走一下完整的自定义控件的流程来给大家简单的介绍一下

1.控件的创建
(1)当这个流式布局在被加载如内存并显示在屏幕上这一过程中,首先会调用view.measure(w,h)这个方法,表示测量view的宽度与高度,其中参数w与h分别表示这个控件的父控件的宽高。
(2)在view.measure()方法的调用过程中又会调用view本身的一个回调方法,onMeasure(),这个是view自身的一个回调方法,用于让开发者在自定义View的时候重新计算自身的大小。一般会在这个方法中循环遍历,计算出这个控件的全部子孙控件的宽高。
(3)在View的宽高计算完成以后,考虑将这个控件显示到屏幕的指定位置上,此时view的onLayout()方法会被调用。 一般同时会在这个方法中计算出全部子孙控件在这个控件中的位置。
可能基本流程有些枯燥,接下来结合代码看看。

2.流式布局的实现

onMeasure()方法中: //通过计算每一个子控件的高度,得到自己的高度

for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);
        LayoutParams childLayoutParams = childView.getLayoutParams();
        childView.measure(
                getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                        childLayoutParams.width),
                getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                        childLayoutParams.height));
        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > selfWidth) {
            childLeft = paddingLeft + childWidth;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        } else {
            childLeft += childWidth + mHorizontalSpacing;
        }
    }

首先通过循环,遍历这个控件的所有子控件,同时调用子控件的measure()方法,这时measure方法的两个参数是控件能给这个子控件的最大宽高(我们都知道的,子控件再大,显示的大小也不能比父控件还大)。这里getChildMeasureSpec()方法的作用是用来计算一个合适子视图的尺寸大小(宽度或者高度),结合我们从子视图的LayoutParams所给出的MeasureSpec信息来获取最合适的结果。比如,如果这个View知道自己的大小尺寸(因为它本身的MeasureSpec的model为Exactly,)并且子视图的大小恰好跟父窗口一样大,父窗口必须用给定的大小去layout子视图
参数含义:spec 父窗口传递给子视图的大小和模式
padding 父窗口的边距,也就是xml中的android:padding
childDimension 子视图想要绘制的准确大小,但最终不一定绘制此值

当得到了每一个子控件的大小以后,再要计算自己的宽高就简单了。
int wantedHeight = childTop + lineHeight + paddingBottom;

同理,在onLayout中的这一句

for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);

        if (childView.getVisibility() == View.GONE) {
            continue;
        }

        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > myWidth) {
            childLeft = paddingLeft + childWidth;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        }
        childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        childLeft += childWidth + mHorizontalSpacing;
    } 

首先通过循环遍历,控制每个item子控件的显示位置,如果当前行还能放得下一个item,就放到当前行,如果放不下就放到下一行的最左边。
最终,遍历完成,也就相当于把自己的位置显示完成了。

完整代码如下


/**
 * Created by Administrator on 2016/7/20.
 * Android流式布局  
 * @auther madreain
 */

public class LabelLayout extends ViewGroup {
    private float mVerticalSpacing; //每个item纵向间距
    private float mHorizontalSpacing; //每个item横向间距

    public LabelLayout (Context context) {
        super(context);
    }
    public LabelLayout (Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setHorizontalSpacing(float pixelSize) {
        mHorizontalSpacing = pixelSize;
    }
    public void setVerticalSpacing(float pixelSize) {
        mVerticalSpacing = pixelSize;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int selfWidth = resolveSize(0, widthMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int childLeft = paddingLeft;
        int childTop = paddingTop;
        int lineHeight = 0;

        //通过计算每一个子控件的高度,得到自己的高度
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);
            LayoutParams childLayoutParams = childView.getLayoutParams();
            childView.measure(
                    getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                            childLayoutParams.width),
                    getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                            childLayoutParams.height));
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > selfWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            } else {
                childLeft += childWidth + mHorizontalSpacing;
            }
        }

        int wantedHeight = childTop + lineHeight + paddingBottom;
        setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

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

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;

        //根据子控件的宽高,计算子控件应该出现的位置。
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);

            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > myWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            }
            childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + mHorizontalSpacing;
        }
    }
}

下面介绍代码中加入这个流式布局及在流式布局中添加子布局

label_layout_show_label = new LabelLayout (this);
        //加入流式布局    
        LinearLayout.LayoutParams label_layout_show_labellayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        label_layout_show_labellayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        label_layout_show_labellayoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
        label_layout_show_labellayoutParams.setMargins(PixelOrdpManager.dip2px(getBaseContext(), 13), PixelOrdpManager.dip2px(getBaseContext(), 12), 0, PixelOrdpManager.dip2px(getBaseContext(), 12));
        label_layout_show_labellayoutParams.gravity = Gravity.CENTER;
        label_layout_show_label.setLayoutParams(label_layout_show_labellayoutParams);

        label_layout_show_label_parent.addView(label_layout_show_label);

        //流式布局中加入子布局    
        textViewLabelOne = new TextView(this);
        textViewLabelTwo = new TextView(this);
        textViewLabelThere = new TextView(this);

        textViewLabelOne.setText("");
        textViewLabelTwo.setText("");
        textViewLabelThere.setText("");

        textViewLabelOne.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelOne.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelOne.setTextSize(10);
        textViewLabelOne.setGravity(Gravity.CENTER);

        textViewLabelTwo.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelTwo.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelTwo.setTextSize(10);
        textViewLabelTwo.setGravity(Gravity.CENTER);

        textViewLabelThere.setBackgroundResource(R.drawable.add_success_label);
        textViewLabelThere.setTextColor(getResources().getColor(R.color.M4A4D4F));
        textViewLabelThere.setTextSize(10);
        textViewLabelThere.setGravity(Gravity.CENTER);

        LinearLayout.LayoutParams textViewLabellayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        textViewLabellayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
//        textViewLabellayoutParams.width = PixelOrdpManager.dip2px(getBaseContext(),100);
        textViewLabellayoutParams.height = PixelOrdpManager.dip2px(getBaseContext(), 25);
        textViewLabellayoutParams.setMargins(PixelOrdpManager.dip2px(getBaseContext(), 13), PixelOrdpManager.dip2px(getBaseContext(), 12), 0, PixelOrdpManager.dip2px(getBaseContext(), 12));
        textViewLabellayoutParams.gravity = Gravity.CENTER;
        textViewLabelOne.setLayoutParams(textViewLabellayoutParams);
        textViewLabelTwo.setLayoutParams(textViewLabellayoutParams);
        textViewLabelThere.setLayoutParams(textViewLabellayoutParams);


        label_layout_show_label.addView(textViewLabelOne);
        label_layout_show_label.addView(textViewLabelTwo);
        label_layout_show_label.addView(textViewLabelThere);
发布了56 篇原创文章 · 获赞 7 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章