基于FlowLayout流式布局 扩展的接口,可设置每行最大的列数,以及文本是居左、居中还是居右布局。
修改后的代码如下:
package com.lanmeng.test.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
/**
*
*/
public class FlowLayout extends ViewGroup {
private static final String TAG = "FlowLayout";
private List<Rect> mChildrenPositionList = new ArrayList<>();
private int mMaxLines = Integer.MAX_VALUE;
private int mMaxColumns = Integer.MAX_VALUE;
private int mVisibleItemCount;
public static final int LAYOUT_LEFT = 1;
public static final int LAYOUT_CENTER = 2;
public static final int LAYOUT_RIGHT = 3;
private int mLayoutMode = LAYOUT_LEFT;
/**
* @param context
*/
public FlowLayout(Context context) {
super(context);
}
/**
* @param context
* @param attrs
*/
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Construct
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mChildrenPositionList.clear();
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int[] a = helper(widthSize);
int measuredHeight = 0;
if (heightMode == MeasureSpec.EXACTLY) {
measuredHeight = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
measuredHeight = a[0];
}
int measuredWidth = 0;
if (widthMode == MeasureSpec.EXACTLY) {
measuredWidth = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
measuredWidth = a[1];
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
private int[] helper(int widthSize) {
boolean isOneRow = true;
int width = getPaddingLeft();
int height = getPaddingTop();
int rightPadding = getPaddingRight();
int maxHeight = 0;
int currLine = 1;
List<Integer> positions = new LinkedList<Integer>();
int count = 0; //line count;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams params = child.getLayoutParams();
MarginLayoutParams mlp;
if (params instanceof MarginLayoutParams) {
mlp = (MarginLayoutParams) params;
} else {
mlp = new MarginLayoutParams(params);
}
int childWidth = mlp.leftMargin + child.getMeasuredWidth() + mlp.rightMargin;
int childHeight = mlp.topMargin + child.getMeasuredHeight() + mlp.bottomMargin;
maxHeight = Math.max(maxHeight, childHeight);
// Change line or not
if (width + childWidth + getPaddingRight() > widthSize || count >= mMaxColumns) {
height += maxHeight;
width = getPaddingLeft();
maxHeight = childHeight;
isOneRow = false;
currLine++;
count = 0;
if (i > 1) {
positions.add(i - 1);
}
if (currLine > mMaxLines) {
break;
}
}
count++;
Rect rect = new Rect(width + mlp.leftMargin,
height + mlp.topMargin,
width + childWidth - mlp.rightMargin,
height + childHeight - mlp.bottomMargin);
mChildrenPositionList.add(rect);
width += childWidth;
}
if (getChildCount() > 0) {
positions.add(getChildCount() - 1);
}
//recalculate position by layout mode
int uselessSpace = 0;
int startLayoutPosition = 0;
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < positions.size(); i++) {
if (i == 0) {
startIndex = 0;
} else {
startIndex = positions.get(i - 1) + 1;
}
endIndex = positions.get(i);
// Log.d(TAG, "startIndex: " + startIndex + ", endIndex: " + endIndex);
uselessSpace = widthSize - mChildrenPositionList.get(endIndex).right - rightPadding;
switch (mLayoutMode) {
case LAYOUT_LEFT:
startLayoutPosition = 0;
break;
case LAYOUT_CENTER:
startLayoutPosition = uselessSpace / 2;
break;
case LAYOUT_RIGHT:
startLayoutPosition = uselessSpace;
break;
default:
break;
}
for (int j = startIndex; j <= endIndex; j++) {
Rect rect = mChildrenPositionList.get(j);
// Log.d(TAG, "i: " + j + " ---left.left: " + rect.left + " ---left.right: " + rect.right);
rect.left += startLayoutPosition;
rect.right += startLayoutPosition;
}
}
int[] res = new int[2];
res[0] = height + maxHeight + getPaddingBottom();
res[1] = isOneRow ? width + getPaddingRight() : widthSize;
return res;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int n = Math.min(getChildCount(), mChildrenPositionList.size());
for (int i = 0; i < n; i++) {
View child = getChildAt(i);
Rect rect = mChildrenPositionList.get(i);
child.layout(rect.left, rect.top, rect.right, rect.bottom);
}
mVisibleItemCount = n;
}
/**
* set Adapter
*/
public void setAdapter(Adapter adapter) {
removeAllViews();
int n = adapter.getItemCount();
for (int i = 0; i < n; i++) {
ViewHolder holder = adapter.onCreateViewHolder(this);
adapter.onBindViewHolder(holder, i);
View child = holder.itemView;
addView(child);
}
}
/**
* set max lines
*/
public void setMaxLines(int maxLines) {
mMaxLines = maxLines;
}
/**
* set max Columns
*/
public void setMaxColumns(int columns) {
mMaxColumns = columns;
}
/**
* layout left, center or right.
* @param mLayoutMode
*/
public void setLayoutMode(int mLayoutMode) {
this.mLayoutMode = mLayoutMode;
}
/**
* getVisibleItemCount
*
* @return
*/
public int getVisibleItemCount() {
return mVisibleItemCount;
}
/**
* @param <VH>
*/
public abstract static class Adapter<VH extends ViewHolder> {
/**
* @param parent
* @return
*/
public abstract VH onCreateViewHolder(ViewGroup parent);
/**
* @param holder
* @param position
*/
public abstract void onBindViewHolder(VH holder, int position);
/**
* @return
*/
public abstract int getItemCount();
}
public abstract static class ViewHolder {
public final View itemView;
/**
* @param itemView
*/
public ViewHolder(View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
}
}
}