自定义布局之流式布局

来源:互联网 发布:淘宝上传身份证 编辑:程序博客网 时间:2024/05/01 11:47

定义

什么是流式布局?就是当一行的末尾不能容纳新的子控件时,就另起一行。适用的场景包括关键字标签,搜索热词等。

实现

1.理解android View的3种测量模式

1)EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
2)AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
3)UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

2.继承ViewGroup,重写三个回调函数。

 (1)/**     * 为自定义布局设置一系列布局属性,比如layout_grayvity,layout_weight等     * 在这里设置的布局属性将反馈在view.getLayoutParams()函数的返回结果中。     * 在这个自定义的流式布局里只需要用到MarginLayoutParams     * @param attrs     * @return     */    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(),attrs);    }
 (2)/**     * 计算所有childView的宽度和高度,然后根据ChildView的计算结果,设置自己的宽和高     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {       super.onMeasure(widthMeasureSpec,heightMeasureSpec);         }
(3) /**     * 在这个函数中设置子view的位置以及大小     * @param changed     * @param l     * @param t     * @param r     * @param b     */    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {    }

具体代码

package com.example.chen.flowlayoutexample.widget;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;/** * Created by Administrator on 2015/8/14 0014. */public class FlowLayout extends ViewGroup {    public FlowLayout(Context context) {        super(context);    }    public FlowLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    /**     * 为自定义布局设置一系列布局属性,比如layout_grayvity,layout_weight等     * 在这里设置的布局属性将反馈在view.getLayoutParams()函数的返回结果中。     * 在这个自定义的流式布局里只需要用到MarginLayoutParams     * @param attrs     * @return     */    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(),attrs);    }    /**     * 计算所有childView的宽度和高度,然后根据ChildView的计算结果,设置自己的宽和高     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //获得上级容器为其推荐的宽和高        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);        //如果是wrap_content情况下,要重新计算宽,高        int width = 0;        int height = 0;        //设置一个变量获取每一行的宽度,高度        int lineWidth = 0;        int lineHeight = 0;        int count = getChildCount();        //遍历每一个子元素        for(int i =0; i < count; i++){            View child = getChildAt(i);            //测量每一个子view的宽和高            measureChild(child,widthMeasureSpec,heightMeasureSpec);            //得到child的布局参数(由generateLayoutParams生成)            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) child.getLayoutParams();            //计算当前子view实际占据的宽度和高度            int childWidth = child.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;            int childHeight = child.getMeasuredHeight() + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin;            //判断如果加入当前view超过目前给的最大宽度,则进行换行            if(lineWidth + childWidth >sizeWidth){                //将之前计算得的宽与当前行宽进行比较,取最大值                width = Math.max(width,lineWidth);                //当前行宽重新赋值为下一行的第一个子view的宽度                lineWidth = childWidth;                height += lineHeight;                lineHeight = childHeight;            }else{                lineWidth += childWidth;                lineHeight = Math.max(height,childHeight);            }            if(i == count - 1){                width = Math.max(width,lineWidth);                height += childHeight;            }        }        //最后判断测量模式        int measuredWidth = (modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width;        int measuredHeight = (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height;        //设置布局宽和高        setMeasuredDimension(measuredWidth, measuredHeight);    }    //记录多有的子view    private List<List<View>> mAllViews = new ArrayList<>();    //记录每一行的最大高度    private List<Integer> mLineHeight = new ArrayList<>();    /**     * 在这个函数中设置子view的位置以及大小     * @param changed     * @param l     * @param t     * @param r     * @param b     */    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        mAllViews.clear();        //获取布局的实际宽度        int width = getWidth();        int lineWdith = 0;        int lineHeight = 0;        //存储当前行的views        List<View> lineViews = new ArrayList<>();        for(int i =0; i < getChildCount(); i++){            View child = getChildAt(i);            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) child.getLayoutParams();            int childWidth = child.getMeasuredWidth();            int childHeight = child.getMeasuredHeight();            //判断需不需要换行            if(childWidth + marginLayoutParams.leftMargin + marginLayoutParams.leftMargin + lineWdith > width){                mLineHeight.add(lineHeight);                mAllViews.add(lineViews);                lineWdith = 0;                lineViews = new ArrayList<>();            }            lineWdith += childWidth + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;            lineHeight = Math.max(lineHeight,childHeight + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin);            lineViews.add(child);        }        //记录最后一行        mLineHeight.add(lineHeight);        mAllViews.add(lineViews);        int left = 0;        int top = 0;        //得到总的行数        int lineNums = mAllViews.size();        for(int i = 0 ; i < lineNums; i++) {            //取出每一行的view集合以及行高            lineViews = mAllViews.get(i);            lineHeight = mLineHeight.get(i);            //遍历每一行的所有的view            for (int j = 0; j < lineViews.size(); j++) {                View child = lineViews.get(j);                if (child.getVisibility() == View.GONE) {                    continue;                }                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();                int lc = left + lp.leftMargin;                int tc = top + lp.topMargin;                int rc = lc + child.getMeasuredWidth();                int bc = tc + child.getMeasuredHeight();                //进行子view的布局                child.layout(lc, tc, rc, bc);                left += lp.leftMargin + child.getMeasuredWidth() + lp.rightMargin;            }            left = 0;            top += lineHeight;        }    }}

参考

以上实现参考以下链接,加入一点点自己的理解。
http://blog.csdn.net/lmj623565791/article/details/38352503

0 0