(四)UI绘制流程-瀑布流布局

来源:互联网 发布:宝软网java破解游戏 编辑:程序博客网 时间:2024/05/21 21:49

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一.前言

上一篇给出了流式布局,这里再来一篇瀑布流布局练手,同时代码稍微简洁一些。不再利用封装类来保存数据,直接把子 View 的四个边沿保存在对应的 LayoutParams 中。

二、分析

这里写图片描述

1.generateLayoutParams()获取封装子 View 的左上右下四个边沿的自定义 WaterfallLayoutParams。
2.onMeasure() 测量自身宽高的同时把在子 View 的 LayoutParams 存储该子 View 对应的四个边沿值。
3.onLayout()把每行 View 进行摆放。

三、实现

1.自定义的 LayoutParams

    //WaterfallLayout 下的子控件的布局,保存该控件的左上右下四个边沿值    public static class WaterfallLayoutParams extends ViewGroup.LayoutParams {        public int left = 0;        public int top = 0;        public int right = 0;        public int bottom = 0;        public WaterfallLayoutParams(Context context, AttributeSet attrs) {            super(context, attrs);        }        public WaterfallLayoutParams(int width, int height) {            super(width, height);        }        public WaterfallLayoutParams(android.view.ViewGroup.LayoutParams params) {            super(params);        }    }

二、generateLayoutParams()

由于使用了自定义的 LayoutParams,这里必须全部写出以下方法,不然会报强转错误,这边只是展示有这么个用法,自定义 LayoutParams 就暂时不在这边记录。

    @Override    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {        return new WaterfallLayoutParams(getContext(), attrs);    }    @Override    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {        return new WaterfallLayoutParams(WaterfallLayoutParams.WRAP_CONTENT, WaterfallLayoutParams.WRAP_CONTENT);    }    @Override    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        return new WaterfallLayoutParams(p);    }    @Override    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {        return p instanceof WaterfallLayoutParams;    }

三、onMeasure()

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //获取父容器为 WaterfallLayout 设置的测量模式和大小的建议值        int iWidthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int iHeightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int iWidthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int iHeightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        //记录测量的宽高        int measuredWith = 0;        int measuredHeight = 0;        //计算列的宽度        columnWidth = (iWidthSpecSize - mVerticalSpace * (columnCount - 1)) / columnCount;        //获取子 View 的数量        int childCount = getChildCount();        //如果不满一排,测量宽度为一排的实际宽度        if (childCount < columnCount) {            measuredWith = childCount * (columnWidth + mHorizontalSpace) - mHorizontalSpace;        } else {            measuredWith = iWidthSpecSize;        }        //循环遍历子 View,进行子 View 的测量以及 WaterfallLayout 宽高的计算        for (int i=0; i<childCount; i++) {            View childView = getChildAt(i);            //对当前获取的子 View 进行测量            measureChild(childView, widthMeasureSpec, heightMeasureSpec);            //按比例计算子 View 的高度            int childViewHeight = childView.getMeasuredHeight() * columnWidth / childView.getMeasuredWidth();            //获取最小高度的列            int minHeightColumn = getMinHeightColumn();            //获取子 View 的 LayoutParams,并强转为 WaterfallLayoutParams,记录子 View 的左上右下四个边沿            WaterfallLayoutParams layoutParams = (WaterfallLayoutParams) childView.getLayoutParams();            layoutParams.left = minHeightColumn * (columnWidth + mHorizontalSpace);            layoutParams.top = columnHeight[minHeightColumn];            layoutParams.right = layoutParams.left + columnWidth;            layoutParams.bottom = layoutParams.top + childViewHeight;            //添加图片后把高度加进去            columnHeight[minHeightColumn] = layoutParams.bottom + mVerticalSpace;        }        //测量高度为最高度的列的高度        measuredHeight = columnHeight[getMaxHeightColumn()];        //测量的最终目的 setMeasuredDimension        setMeasuredDimension(iWidthSpecMode == MeasureSpec.EXACTLY ? iWidthSpecSize : measuredWith,                 iHeightSpecMode == MeasureSpec.EXACTLY ? iHeightSpecSize : measuredHeight);    }

四、onLayout()

这样 onLayout() 方法就变得简洁了很多。

      @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = this.getChildAt(i);            WaterfallLayoutParams lParams = (WaterfallLayoutParams)child.getLayoutParams();            child.layout(lParams.left, lParams.top, lParams.right, lParams.bottom);        }        //由于程序会多次调用测量布局,所以布局完清空数据        //不清空数据,同一个 View 会被多次引用布局,以最后一次布局为主,        //可能导致所有子 View 的布局都在 FlowLayout 高度之外,导致界面没有东西        clearTop();    }

五、监听

监听还是一样,没有改变

    //监听类接口    public interface OnItemClickListener{        void onItemClick (View v, int index);    }    //监听类的实现    public void setOnItemClickListener(final OnItemClickListener listener){        int childCount = getChildCount();        for(int i = 0 ; i < childCount ; i++){            View childView = getChildAt(i);            final int finalI = i;            childView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    listener.onItemClick(v, finalI);                }            });        }    }

六、三个业务方法

    //获取当前高度最小的列    private int getMinHeightColumn() {        int minHeightColumn = 0;        for (int i=0; i<columnCount; i++) {            if (columnHeight[i] < columnHeight[minHeightColumn]) {                minHeightColumn = i;            }        }        return minHeightColumn;    }    //获取当前高度最大的列    private int getMaxHeightColumn() {        int minHeightColumn = 0;        for (int i=0; i<columnCount; i++) {            if (columnHeight[i] > columnHeight[minHeightColumn]) {                minHeightColumn = i;            }        }        return minHeightColumn;    }    //初始化没列高度    private void clearTop() {        for (int i=0; i<columnCount; i++) {            columnHeight[i] = 0;        }    }

七、附

代码链接:http://download.csdn.net/detail/qq_18983205/9849283
这里引用 v7 包,报错的可以参考 http://blog.csdn.net/qq_18983205/article/details/60874618

原创粉丝点击