从 http://blog.csdn.net/lmj623565791/article/details/38352503 ,【张鸿洋的博客】 学习了FlowLayout的编写。原文的onLayout()方法稍显复杂,用自己的方法试了一下。
先上最终效果图(逼死强迫症):
自定义ViewGroup来实现这样的布局
public class FlowLayout extends ViewGroup {
private List<Integer> aboutChild = new ArrayList<>();
private List<Integer> aboutLineHeight = new ArrayList<>();
private boolean isFirstLayout = true;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*onMeasure()方法会执行多次,每次执行先清空List防止多次叠加出错*/
aboutChild.clear();
aboutLineHeight.clear();
/*默认的sizeWidth和sizeHeight的大小以match_parent计算,如果实际设置为wrap_content
则会在setMeasuredDimension()后设置*/
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
/*如果ViewGroup设置为wrap_content,则设置实际宽高为我们计算的width和height*/
int width = 0;
int height = 0;
int lineWidth = 0;
int lineHeight = 0;
int count = getChildCount();
/*每一行的子View数目*/
int childNumOfLine = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
/*child所占的宽高需要考虑它的margin*/
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
/*如果超过了最大宽度(match_parent所得)*/
if (lineWidth + childWidth > sizeWidth) {
width = Math.max(lineWidth, width);
height += lineHeight;
lineHeight = childHeight;
lineWidth = childWidth;
aboutChild.add(childNumOfLine);
childNumOfLine = 1;
aboutLineHeight.add(lineHeight);
} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
childNumOfLine++;
}
if (i == count - 1) {
width = Math.max(lineWidth, width);
height += lineHeight;
aboutChild.add(childNumOfLine);
aboutLineHeight.add(lineHeight);
Log.d("yzh", "aboutChild.size() = " + aboutChild.size());
}
}
/*根据测量模式决定是否使用我们测得的宽高*/
setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width,
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
/*避免多次执行*/
if (isFirstLayout) {
int left = 0;
int top = 0;
int childCursor = 0;
for (int i = 0; i < aboutChild.size(); i++) {
for (int j = 0; j < aboutChild.get(i); j++) {
View child = getChildAt(childCursor);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int lc = lp.leftMargin + left;
int tc = lp.topMargin + top;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
left += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
childCursor++;
child.layout(lc, tc, rc, bc);
}
left = 0;
top += aboutLineHeight.get(i);
}
isFirstLayout = false;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
主要的难点就是onMeasure中换行以及宽高的计算,拿笔多画一画应该能想清楚。
我们用TextView来测试一下。首先去自定义一下TextView的style。在values下的styles.xml文件夹中添加如下style:
<style name="text_style" >
<item name="android:textSize">20sp</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_margin">6dp</item>
<item name="android:background">@drawable/text_back</item>
</style>
里面还用到了自定义的background:
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#665690a5" >
</solid>
<corners android:radius="8dp"/>
<padding
android:bottom="4dp"
android:left="10dp"
android:right="10dp"
android:top="4dp" />
</shape>
最后在布局中:
<com.example.yin.flowlayout.FlowLayout
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F2E18C">
<TextView
style="@style/text_style"
android:text="Android"/>
<TextView
style="@style/text_style"
android:text="ios"/>
<TextView
style="@style/text_style"
android:text="other"/>
<TextView
style="@style/text_style"
android:text="php"/>
<TextView
style="@style/text_style"
android:text="WindowPhone"/>
<TextView
style="@style/text_style"
android:text="Java"/>
<TextView
style="@style/text_style"
android:text="c"/>
<TextView
style="@style/text_style"
android:text="c++"/>
<TextView
style="@style/text_style"
android:text="Window10"/>
<TextView
style="@style/text_style"
android:text="c#"/>
<TextView
style="@style/text_style"
android:text="Android"/>
<TextView
style="@style/text_style"
android:text="ios"/>
<TextView
style="@style/text_style"
android:text="other"/>
<TextView
style="@style/text_style"
android:text="php"/>
<TextView
style="@style/text_style"
android:text="WindowPhone"/>
<TextView
style="@style/text_style"
android:text="Java"/>
<TextView
style="@style/text_style"
android:text="c"/>
<TextView
style="@style/text_style"
android:text="c++"/>
<TextView
style="@style/text_style"
android:text="Window10"/>
<TextView
style="@style/text_style"
android:text="c#"/>
</com.example.yin.flowlayout.FlowLayout>