网上有很多的自动换行的layout了,但看了一圈,感觉很多人都把问题想复杂了,这也就导致了代码的复杂。有些时候你想要的只是一个可以自动换行的layout,可以设置横竖间距,不需要其他一些奇奇怪怪的功能和设置一些奇奇怪怪的属性。那么你可以考虑我写的这个
public class AutoLineLayout extends ViewGroup {
private int horizontalSpace;
private int verticalSpace;
private int theRealWidth;
public AutoLineLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.AutoLineLayout);
horizontalSpace= (int) Math.ceil(array.getDimension(R.styleable.AutoLineLayout_horizontalSpace,0));
verticalSpace=(int) Math.ceil(array.getDimension(R.styleable.AutoLineLayout_verticalSpace,0));
array.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//不测算宽度模式,因为不管是wrap_content还是match_parent都按match_parent来算
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int lineWidth=0;
int lineHeight=0;
int height=0;
int paddingStart=getPaddingStart();
int paddingEnd=getPaddingEnd();
int size=widthSize-paddingStart-paddingEnd;
theRealWidth=size;
measureChildren(widthMeasureSpec,heightMeasureSpec);
for (int i=0;i<getChildCount();i++){
View view=getChildAt(i);
int childHeight=view.getMeasuredHeight();
int childWidth=view.getMeasuredWidth();
int nowWidth=childWidth+lineWidth;
boolean changeLine=nowWidth>size;
changeLine=changeLine||(nowWidth-horizontalSpace>size);
if (changeLine){
lineWidth=childWidth+horizontalSpace;
height+=lineHeight+verticalSpace;//这里的lineHeight其实是上一行的所有view的高度的最大值
lineHeight=childHeight;
}else {
lineHeight=Math.max(lineHeight,childHeight);
lineWidth+=childWidth+horizontalSpace;
if (i==0)
height+=lineHeight;
}
}
height+=getPaddingTop()+getPaddingBottom();
setMeasuredDimension(widthSize,heightMode==MeasureSpec.EXACTLY?heightSize:height);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int paddingStart=getPaddingStart();
int leftOffset = paddingStart;
int topOffset = getPaddingTop();
int lineHeight=0;
for (int i=0;i<getChildCount();i++){
View childView=getChildAt(i);
int childWidth=childView.getMeasuredWidth();
int childHeight=childView.getMeasuredHeight();
int nowWidth=childWidth+leftOffset;
boolean changeLine=nowWidth>theRealWidth;
changeLine=changeLine||(nowWidth-horizontalSpace>theRealWidth);
if (changeLine){
leftOffset=paddingStart;
topOffset+=lineHeight+verticalSpace;
childView.layout(leftOffset,topOffset,childWidth+leftOffset,topOffset+childHeight);
leftOffset+=childWidth+horizontalSpace;
lineHeight=childHeight;
}else {
childView.layout(leftOffset,topOffset,childWidth+leftOffset,topOffset+childHeight);
leftOffset+=childWidth+horizontalSpace;
lineHeight=Math.max(lineHeight,childHeight);
}
}
}
}
下面是attr里的属性,用来控制当前每个View之间的横向间距和每行之间的纵向间距
//这个是之前在有个地方看到的,他的自动换行增加了这两个属性,我觉得很方便。 就拿过来用了
<declare-styleable name="AutoLineLayout">
<attr name="horizontalSpace" format="dimension"/>
<attr name="verticalSpace" format="dimension"/>
</declare-styleable>
网上的很多自动换行的View,都喜欢重写generateLayoutParams,返回一个带margin的lp。我感觉是没有必要的。因为已经有horizontalSpace和verticalSpace两个属性可以控制间距,同时也支持padding了,个人感觉是够用了。同时,像这样的自动换行的layout,一般是从网络获取数据之后动态填充,也没有必要考虑margin。 下面是动态添加的效果图
以及在xml文件中写死的情况下的效果图
补充,关于点击事件
关于点击事件,我的想法是由添加进来的控件本身来处理,和这个layout无关,你可以这样完成点击事件:
欢迎提出意见和改进