背景介绍:

最近需要做一个类似于Java awt包里的FlowLayout的流式布局,比如下图

Android原生控件中没有找到现成的,习惯先Google之,找到鸿洋_ 大神的: Android 自定义ViewGroup 实战篇 -> 实现FlowLayout。

写的太好了,基本实现了流式布局的功能,不过还有些功能没有实现:

  • flowLayout自身的padding

  • 子view的visibility为gone时隐藏

  • 兼容子view的居中显示

在大神的基础上进行继续开发,实现以上功能,并优化减少在onLayout方法中的一次遍历getChildAt。
FlowLayout的使用方式与系统的LinearLayout、RelativeLayout等基本一样(xml方式和java代码方式创建使用、动态修改、设置事件等)

上代码:

public class FlowLayout extends ViewGroup {    private boolean centerHorizontal;//是否水平居中显示    public FlowLayout(Context context) {        super(context);    }    public FlowLayout(Context context, AttributeSet attrs) {        super(context, attrs);        initFromAttributes(context, attrs);    }    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initFromAttributes(context, attrs);    }    private void initFromAttributes(Context context, AttributeSet attrs) {    /**    //res/values/attr.xml    //    //        //         //        //下面的代码配合res/values/attr.xml可以支持通过在xml中FlowLayout节点        //添加app:centerHorizontal="true"的方式设置水平居中属性        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);        centerHorizontal = a.getBoolean(R.styleable.FlowLayout_centerHorizontal, centerHorizontal);        a.recycle();     */    }    @Override    protected LayoutParams generateDefaultLayoutParams() {        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new LayoutParams(getContext(), attrs);    }    @Override    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        return new LayoutParams(p.width, p.height);    }    public boolean isCenterHorizontal() {        return centerHorizontal;    }    // 动态设置子view的居中状态    public void setCenterHorizontal(boolean centerHorizontal) {        if (this.centerHorizontal ^ centerHorizontal) {            this.centerHorizontal = centerHorizontal;            requestLayout();        }    }    /**     * 负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        // 获得它的父容器为它设置的测量模式和大小        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);        // 如果是warp_content情况下,记录宽和高        final int paddingWidth = getPaddingLeft() + getPaddingRight();        final int paddingHeight = getPaddingTop() + getPaddingBottom();        int width = paddingWidth;        int height = paddingHeight;        // 记录每一行的宽度,width不断取最大宽度        int lineWidth = paddingWidth;        // 每一行的高度,累加至height        int lineHeight = 0;        mAllViews.clear();        mLineHeight.clear();        mLineWidth.clear();        // 存储每一行所有的childView        List lineViews = new ArrayList<>();        int cCount = getChildCount();        // 遍历每个子元素        for (int i = 0; i < cCount; i++) {            View child = getChildAt(i);            if (child.getVisibility() == GONE) {                continue;            }            // 得到child的lp            LayoutParams lp = (LayoutParams) child.getLayoutParams();            // 测量每一个child的宽和高            final int marginWidth = lp.rightMargin + lp.leftMargin;            final int marginHeight = lp.topMargin + lp.bottomMargin;            // 子控件测量的时候需要将父控件的padding和当前子控件的margin算进去            child.measure(                getChildMeasureSpec(widthMeasureSpec, paddingWidth + marginWidth, lp.width),                getChildMeasureSpec(heightMeasureSpec, paddingHeight + marginHeight, lp.height)            );            // 当前子控件实际占据的宽度            int childWidth = child.getMeasuredWidth() + marginWidth;            // 当前子控件实际占据的高度            int childHeight = child.getMeasuredHeight() + marginHeight;            //如果加入当前child,则超出最大宽度,则得到目前最大宽度给width,累加height 然后开启新行            if (lineWidth + childWidth > sizeWidth) {                width = Math.max(width, lineWidth);// 取最大的                height += lineHeight;// 叠加当前高度                // 记录这一行所有的View、总的宽度以及最大高度                mLineHeight.add(lineHeight);                mLineWidth.add(lineWidth);                // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView                mAllViews.add(lineViews);                lineViews = new ArrayList<>();                // 重新开启新行,开始记录                lineWidth = paddingWidth;                lineHeight = 0;            }            // 否则累加值lineWidth,lineHeight取最大高度            lineWidth += childWidth;            lineHeight = Math.max(lineHeight, childHeight);            // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较            if (i == cCount - 1) {                width = Math.max(width, lineWidth);                height += lineHeight;            }            lineViews.add(child);        }        // 记录最后一行        mLineHeight.add(lineHeight);        mLineWidth.add(lineWidth);        mAllViews.add(lineViews);        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth                : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight                : height);    }    /**     * 存储所有的View,按行记录     */    private List> mAllViews = new ArrayList<>();    /**     * 记录每一行的最大高度     */    private List mLineHeight = new ArrayList<>();    private List mLineWidth = new ArrayList<>();    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int paddingLeft = getPaddingLeft();        final int paddingRight = getPaddingRight();        final int paddingTop = getPaddingTop();        final int paddingBottom = getPaddingBottom();        int width = getWidth() - paddingLeft - paddingRight;        int lineHeight;        List lineViews;        int left = paddingLeft;        int top = paddingTop;        // 得到总行数        int lineNums = mAllViews.size();        for (int i = 0; i < lineNums; i++) {            // 每一行的所有的views            lineViews = mAllViews.get(i);            // 当前行的最大高度            lineHeight = mLineHeight.get(i);            if (centerHorizontal) {                int leftRightSpace = width - (mLineWidth.get(i) - paddingLeft - paddingRight);//左右两端空白空间                left += leftRightSpace / 2;            }            // 遍历当前行所有的View            for (int j = 0; j < lineViews.size(); j++) {                View child = lineViews.get(j);                if (child.getVisibility() == View.GONE) {                    continue;                }                LayoutParams lp = (LayoutParams) child.getLayoutParams();                //计算childView的left,top,right,bottom                int lc = left + lp.leftMargin;                int tc = top + lp.topMargin;                int rc = lc + child.getMeasuredWidth();                int bc = tc + child.getMeasuredHeight();                rc = Math.min(rc, getWidth() - paddingRight - lp.rightMargin);                bc = Math.min(bc, getHeight() - paddingBottom - lp.bottomMargin);                child.layout(lc, tc, rc, bc);//布局子控件                left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;            }            left = paddingLeft;            top += lineHeight;        }    }    public static class LayoutParams extends MarginLayoutParams {        public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);        }        public LayoutParams(ViewGroup.LayoutParams source) {            super(source);        }        public LayoutParams(MarginLayoutParams source) {            super(source);        }        public LayoutParams(int width, int height) {            super(width, height);        }    }}

更多相关文章

  1. 详解ImageView的CENTER_CROP,CENTER_INSIDE,FIT_CENTER等属性
  2. Android特效 - 收藏集 - 掘金
  3. Androidの自定义Spinner实现
  4. Android报表控件achartengine介绍(一)
  5. Android(安卓)Widget工具箱
  6. Android(安卓)DrawableTextView图片文字居中显示
  7. 记录-解决设置透明状态栏,软键盘弹起问题
  8. Android中自定义ListView无法响应OnItemClickListener中的onItem
  9. Andriod 实现可拖动列表

随机推荐

  1. Android 办公自动化(Office Automation)
  2. android跨进程通信(IPC):使用AIDL
  3. android的第一天学习
  4. Android播放视频的方式
  5. Android test project 编译步骤
  6. android客户端程序访问服务器端webservic
  7. 【Android】EditText实现搜索功能,把键盘
  8. 离线下载安装android sdk
  9. Android 双屏异显
  10. Android工具之Hierarchy Viewer--分析应