自定义瀑布流控件WaterfallFlowLayout

来源:互联网 发布:java从小白到大牛 编辑:程序博客网 时间:2024/05/31 06:23

效果如下图所示:
这里写图片描述
实现效果:每个子项的宽度相同高度可能不同的布局
实现思路:
1.由于项宽度相同,则高度需要根据图片比例进行计算得出
2.用一个数组来存储每一列的总高度,将子控件添加到总高度最小的那一列的后面
3.通过onMeasure()方法计算出控件的高度,控件的高度为所有列高中最大的值,并自定义LayoutParams类,将子控件的left,top,right,bottom封装到layoutParams参数中,以便在onLayout方法中获取参数对子控件进行布局,最后通过setMeasuredDimension()方法来设置控件测量大小
4.在onLayout()方法中对子控件进行布局

一.自定义控件代码

public class WaterfallFlowLayout extends ViewGroup {    private int column = 4;//显示的列数    private int horizontalSpace=10;//列间距    private int verticalSpace=10;//行间距    private int childWidth;//子控件的宽度    private int[] columnHeight=new int[column];//存储每一列的高度    public WaterfallFlowLayout(Context context) {        this(context,null);    }    public WaterfallFlowLayout(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public WaterfallFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.WaterfallFlowLayout);        column=typedArray.getInteger(R.styleable.WaterfallFlowLayout_column,3);        horizontalSpace=typedArray.getDimensionPixelSize(R.styleable.WaterfallFlowLayout_horizontalSpace,0);        verticalSpace=typedArray.getDimensionPixelSize(R.styleable.WaterfallFlowLayout_verticalSpace,0);        typedArray.recycle();    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MyLayoutParams(getContext(),attrs);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        //测量之后的宽度高度        int newWidth = 0;        int newHeight = 0;        if (widthMode == MeasureSpec.EXACTLY) {            newWidth = widthSize;            newHeight = heightSize;        } else {            //根据子控件的高度宽度测量出自身的宽高            measureChildren(widthMeasureSpec,heightMeasureSpec);            //计算子控件的宽度            childWidth = (widthSize-(column-1)*horizontalSpace) / column;            int childNum = getChildCount();            if (childNum < column) {                newWidth = childWidth * childNum;            } else {                newWidth = widthSize;            }            for (int i = 0; i < childNum; i++) {                View childView = getChildAt(i);                //根据子控件的宽度计算出子控件应该显示的高度(需要等比例缩放)                int width=childView.getMeasuredWidth();                int height=childView.getMeasuredHeight();                float scale= (float) (height*1.0 / width);                int childHeight = (int) (childWidth *scale);                //获取所有列当中高度最小的位置,放置子控件                int column=getMinHeight();                MyLayoutParams params= (MyLayoutParams) childView.getLayoutParams();                params.left=column*(childWidth+horizontalSpace);                params.top=columnHeight[column]+verticalSpace;                params.right=params.left+childWidth;                params.bottom=params.top+childHeight;                //对添加的列的高度进行累加                columnHeight[column]+=childHeight+verticalSpace;            }            newHeight=getMaxHeight();//获取整个列中最大值        }        setMeasuredDimension(newWidth, newHeight);    }    //获取所有行中列高度最小的位置    private int getMinHeight(){        int minColumn=0;        for(int i=1;i<columnHeight.length;i++){            if(columnHeight[i]<columnHeight[minColumn]){                minColumn=i;            }        }        return minColumn;    }    //获取最大的高度    private int getMaxHeight(){        int maxHeight=columnHeight[0];        for(int i=1;i<columnHeight.length;i++){            if(columnHeight[i]>maxHeight){                maxHeight=columnHeight[i];            }        }        return maxHeight;    }    private void clearTop() {        for (int i = 0; i < columnHeight.length; i++) {            columnHeight[i] = 0;        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        //对子控件进行布局        int childNum=getChildCount();        clearTop();        for(int i=0;i<childNum;i++){            View childView=getChildAt(i);            MyLayoutParams params= (MyLayoutParams) childView.getLayoutParams();            childView.layout(params.left,params.top,params.right,params.bottom);        }    }    public static class MyLayoutParams extends ViewGroup.LayoutParams{        public int left;        public int top;        public int right ;        public int bottom ;        public MyLayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);        }    }

二.布局中使用

<ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent">        <com.example.qdq.uidemo.WaterfallFlowLayout            android:layout_width="wrap_content"            android:layout_height="match_parent"            app:horizontalSpace="5dp"            app:verticalSpace="5dp">            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/one" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/two" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/one" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/one" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/one" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:scaleType="centerCrop"                android:src="@drawable/one" />        </com.example.qdq.uidemo.WaterfallFlowLayout>    </ScrollView>

三自定义属性文件代码

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="WaterfallFlowLayout">        <attr name="column" format="integer"/>        <attr name="horizontalSpace" format="dimension"/>        <attr name="verticalSpace" format="dimension"/>    </declare-styleable></resources>

注意:本例中计算比例存在问题,当图片宽高大于屏幕宽高时,通过getMeasuredWidth()获取的宽度为屏幕宽度并非图片真实宽度,获取的高度也一样,导致比例计算不正确,导致显示异常

原创粉丝点击