java布局中有一个流式布局,但是android布局中并没有。手机上用到流式布局大概就是热门标签的添加吧。流式布局就是控件一个一个的自动往右添加,如果超出宽度,则自动到下一行。
1.对于本布局,我们需要能得到margin属性的LayoutParams,即MarginLayoutParams. 2.在onMeasure()方法中计算所有子view的高度和宽度,以便得到FlowLayout 的宽高(流式布局为warp_content模式)。 3.在onLayout()方法中放置所有子view的位置。
只需要我们重写generateLayoutParams()方法
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); }首先得到父布局的测量模式和宽高,然后遍历所有的子view,得到子view的宽高,计算父布局wrap_content模式下的宽高,最后根据模式设置父布局的宽高。但是在测量时应注意一点,在遍历到最后一个子view时,可能会换行,会走换行的if语句,但是并没有将在view的高度进行累加,所以要单独写一个判断进行累加。
代码分析: allViews 存放所有的子view,lineH 存放每一行的最大高度,lineView 存放每一行的view。 然后遍历所有子view,设置每一行的高度,和每一行的子view,最后遍历每一行的子view。设置每一个view的left,top,right,bottom.
我用几个textView来测试,看一看效果 在res/values/styles.xml中:
<style name="text_flag_01"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_margin">4dp</item> <item name="android:background">@drawable/flag_01</item> <item name="android:textColor">#ffffff</item> </style>frag_01.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#7690A5" > </solid> <corners android:radius="5dp"/> <padding android:bottom="2dp" android:left="10dp" android:right="10dp" android:top="2dp" /> </shape>item_flow.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" style="@style/text_flag_01" android:layout_margin="5dp" android:layout_width="wrap_content" android:layout_height="wrap_content" > </TextView>代码动态添加textview
FlowLayout flow; String[] str = new String[]{"hallo world1","text","FlowLayout Image3","hallo world1", "textView2","FlowLayout Image3","hallo world1", "textView2","FlowLayout Image3"}; LayoutInflater inflater = LayoutInflater.from(this); for (int i = 0; i < str.length; i++) { TextView tv = (TextView) inflater.inflate(R.layout.item_flow,flow,false); tv.setText(str[i]); flow.addView(tv); }最后效果如图 到这里,流式布局基本上就实现了,如果想动态添加,可以自己定义一个接口实现单个添加标签。
上面的方法实现了流式布局,但是我们可以看到,在onMeasure()和onLayout()方法中都计算了子view的宽高。如此,我们可不可以只计算一次呢,在onMeasure()中就将view的坐标计算好呢? 要解决这个问题,就需要有一个数组或列表来保存每一个view的坐标。 比如定义一个类,记录坐标点
public class ViewPosition{ int left; int top; int right; int bottom; public ViewPosition(int left,int top,int right,int bottom){ this.left = left; this.top = top; this.right = right; this.bottom = bottom; } }在onMeasure()中实现坐标计算
List<ViewPos> vPos = new ArrayList<>(); if (childW + lineW > widthSize - getPaddingLeft() - getPaddingRight()){//如果放入该view是超过父布局宽度,换行 width = Math.max(lineW,childW);//取最大行宽为父布局行宽 height +=lineH;//高度累加 //开启新行 lineW = childW; lineH = childH; vPos.add(new ViewPos(getPaddingLeft()+lp.leftMargin, getPaddingTop()+lp.topMargin+height, getPaddingLeft() + childW - lp.rightMargin, getPaddingTop() + height + childH - lp.bottomMargin)); }else {//如果不换行,则宽度累加,高度取最大值 vPos.add(new ViewPos(getPaddingLeft() + lineW + lp.leftMargin, getPaddingTop() + height + lp.topMargin, getPaddingLeft() + lineW + childW - lp.rightMargin, getPaddingTop() + height + childH - lp.bottomMargin)); lineW += childW; lineH = Math.max(lineH,childH); }最后在onLayout()中就简单了
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); ViewPos pos = vPos.get(i); //设置View的左边、上边、右边底边位置 child.layout(pos.left, pos.top, pos.right, pos.bottom); } }参考博客:Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
