首页 > 代码库 > 自定义空间--瀑布流容器

自定义空间--瀑布流容器

效果就是添加图片, 图片会自动添加到最短的一列中。跟瀑布一样垂下来。  这里说一句   onMeasure  和onLayout 会执行两次,如果在方法中去数据不是太好,可以再onChangeSize(),此方法在onMeasure后执行,可以获得控件测量的宽和高(getMeasureWidth、getMeasureHeight)不能获取到(getWidth,getHeight  这两个方法需要在onLayout执行后才有效);

<com.kallaite.rxjavademo.customcontrols.CustomImgLayout
android:layout_width="match_parent"
android:id="@+id/img_layout"
android:layout_height="wrap_content"
attrs:widthSpec="20"
attrs:heightSpec="20"
attrs:colums="3">
</com.kallaite.rxjavademo.customcontrols.CustomImgLayout>

布局中有三个自定义属性,如果需要实现自定义属性,请看前面文章,自定义空间--自定义属性。 colums 表示有即列。 widthSpec: 表示左右边距, heightSpec : 表示上下边距。

package com.kallaite.rxjavademo.customcontrols;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.kallaite.rxjavademo.R;

/**
* Created by yixijun on 17-8-7.
*/
public class CustomImgLayout extends ViewGroup{

private int colums;
private int heightSpec;
private int widthSpec;
private int top[];
private int childWidth;
private int height;

public CustomImgLayout(Context context) {
this(context,null);
}

public CustomImgLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}

public CustomImgLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context,attrs);
}

private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomImgLayout);
if (typedArray != null){
int colums = typedArray.getInteger(R.styleable.CustomImgLayout_colums, -1);
int heightSpec = typedArray.getInteger(R.styleable.CustomImgLayout_heightSpec, -1);
int widthSpec = typedArray.getInteger(R.styleable.CustomImgLayout_widthSpec, -1);
typedArray.recycle();
if (colums != -1){
this.colums = colums;
}
if (heightSpec != -1){
this.heightSpec = heightSpec;
}
if (widthSpec != -1){
this.widthSpec = widthSpec;
}
if (colums != -1){
top = new int[colums];
}else {
top = new int[1];
}
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     //去测量子控件的宽高
measureChildren(widthMeasureSpec,heightMeasureSpec);
     
int childCount = getChildCount();
if (colums == -1){
colums = 1;
}
if (widthSpec == -1){
widthSpec = 0;
}
if (heightSpec == -1){
heightSpec = 0;
}

int wrapWidth;
int wrapHeight;
     //每一个图片的宽度都是一样的,得到每一个图片的宽度
childWidth = (widthSize - widthSpec * (colums - 1)) / colums;
if (childCount < colums){
wrapWidth = childCount * childWidth + (childCount - 1) * widthSpec;
}else {
wrapWidth = widthSize;
}
clearTop();
for (int i = 0; i < childCount; i++) {
View childAt = getChildAt(i);
       //图片按照比例去缩放,这边通过缩放比例,得到控件测量的宽
height = childAt.getMeasuredHeight() * childWidth / childAt.getMeasuredWidth();
       //得到数组中最小高度对应的索引
int minColum = getMinHeightColum();
       //把控件放到最小高度的位置,保存高度。
top[minColum] += (heightSpec + height);
}
     //数组中最高的数量就是此控件容器的高度
wrapHeight = getMaxHeight();
     //此方法必须要执行,用来设置测量的参数,通过得到手动设置的宽高模式,来设置宽高属性,是自己测量到的,还是设置的。
setMeasuredDimension(widthMode == MeasureSpec.AT_MOST ? wrapWidth : widthSize,wrapHeight);
}

private int getMaxHeight() {
int colum = 0;
for (int i = 0; i < top.length; i++) {
if (top[i] > top[colum]){
colum = i;
}
}
return top[colum];
}

private int getMinHeightColum() {
int colum = 0;
for (int i = 0; i < top.length; i++) {
if (top[i] < top[colum]){
colum = i;
}
}
return colum;
}

private void clearTop() {
for (int i = 0; i < top.length; i++) {
top[i] = 0;
}
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
clearTop();
for (int i = 0; i < childCount; i++) {
View childAt = getChildAt(i);
       //得到图片的高度
int height = childAt.getMeasuredHeight() * childWidth / childAt.getMeasuredWidth();
       //最小的高度索引,通过索引可以得到图片应该在第几列,知道在第几列就知道了图片的左边距离,和高度距离,左边距离和高度加上宽、高,就知道控件应该摆放的位置了。
int minColum = getMinHeightColum();
int left = minColum * (childWidth + widthSpec);
int topDesc = top[minColum];
int rightDesc = left + childWidth;
int buttomDesc = topDesc + height;
       //把宽高保存一下,因为高度是要叠加起来的
top[minColum] += heightSpec + height;
childAt.layout(left,topDesc,rightDesc,buttomDesc);
final int finalI = i;
       //对应的控件点击事件,可以知道点击的是第几张图片。
childAt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (clickItem != null){
clickItem.clickItem(finalI);
}
}
});
}
}

interface ClickItem{
void clickItem(int i);
}
private ClickItem clickItem;
  //设置点击事件的接口方法。
public void setClickItemListener(ClickItem mClickItem){
clickItem = mClickItem;
}
}
 

自定义空间--瀑布流容器