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

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

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

一.前言

上一篇给出了流式布局,这里再来一篇瀑布流布局练手,同时代码稍微简洁一些。不再利用封装类来保存数据,直接把子 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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值