首先流式布局相信大家都见到过,比如说下图中的京东热搜就是流式布局的应用。还有更多应用的地方在这里就不一一举例了。
下面我们就来看看是如何实现的。首先新建一个class,继承自ViewGroup。在generateLayoutParams(AttributeSet attrs)
里直接返回MarginLayoutParams就行了。
1 2 3 4 @Override public LayoutParams generateLayoutParams (AttributeSet attrs) { return new MarginLayoutParams (getContext(), attrs); }
然后就是onLayout(boolean changed, int l, int t, int r, int b)
了,大部分的代码都添加了注释,相信大家都能看懂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Override protected void onLayout (boolean changed, int l, int t, int r, int b) { int count = getChildCount(); int cWidth; int cHeight; MarginLayoutParams params; int lastWidth = getPaddingLeft(); int lastHeight = getPaddingTop(); for (int i = 0 ; i < count; i++) { View childView = getChildAt(i); cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); params = (MarginLayoutParams) childView.getLayoutParams(); int width = cWidth + params.leftMargin + params.rightMargin; int height = cHeight + params.topMargin + params.bottomMargin; if (width + lastWidth > r - getPaddingRight()) { lastWidth = getPaddingLeft(); lastHeight += height; } childView.layout(lastWidth, lastHeight, lastWidth + cWidth, lastHeight + cHeight); lastWidth += width; } }
之后就是onMeasure(int widthMeasureSpec, int heightMeasureSpec)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int totalWidth = getPaddingLeft() + getPaddingRight(); int maxWidth = getPaddingLeft() + getPaddingRight(); int maxHeight = getPaddingBottom() + getPaddingTop(); int count = getChildCount(); measureChildren(widthMeasureSpec, heightMeasureSpec); int cWidth; int cHeight; MarginLayoutParams params; int width; int height; for (int i = 0 ; i < count; i++) { View childView = getChildAt(i); cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); params = (MarginLayoutParams) childView.getLayoutParams(); width = cWidth + params.leftMargin + params.rightMargin; height = cHeight + params.topMargin + params.bottomMargin; if (i == 0 ) { maxHeight += height; } if (width + totalWidth > widthSize) { maxWidth = Math.max(maxWidth, totalWidth); totalWidth = getPaddingLeft() + getPaddingRight(); totalWidth += width; maxHeight += height; } else { totalWidth += width; } Log.i(TAG, "i = " + i + ", width = " + width + ", totalWidth = " + totalWidth + ", widthSize = " + widthSize + ((TextView) childView).getText()); Log.i(TAG, "height = " + height); Log.i(TAG, "i = " + i + ", maxHeight = " + maxHeight); } setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : maxHeight); }
在onMeasure(int widthMeasureSpec, int heightMeasureSpec)
中,如果测量模式是MeasureSpec.EXACTLY,则直接设置测量出来的宽和高;否则需要测量每个子View,根据item的行数来得到宽和高。
这样,FlowLayout就写好了,那就让我们来看看效果吧。当android:layout_width="wrap_content"
时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?xml version="1.0" encoding="utf-8" ?> <com.yuqirong.viewgroup.view.FlowLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:tools ="http://schemas.android.com/tools" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:background ="#ddd" android:padding ="20dp" > <TextView style ="@style/text_flag_01" android:text ="杭州" /> <TextView style ="@style/text_flag_01" android:text ="宁波" /> <TextView style ="@style/text_flag_01" android:text ="上海" /> <TextView style ="@style/text_flag_01" android:text ="北京" /> <TextView style ="@style/text_flag_01" android:text ="重庆" /> <TextView style ="@style/text_flag_01" android:text ="南昌" /> <TextView style ="@style/text_flag_01" android:text ="苏州" /> </com.yuqirong.viewgroup.view.FlowLayout >
运行的效果图:
当android:layout_width="300dp"
时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?xml version="1.0" encoding="utf-8" ?> <com.yuqirong.viewgroup.view.FlowLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:tools ="http://schemas.android.com/tools" android:layout_width ="300dp" android:layout_height ="wrap_content" android:background ="#ddd" android:padding ="20dp" > <TextView style ="@style/text_flag_01" android:text ="杭州" /> <TextView style ="@style/text_flag_01" android:text ="哈尔滨" /> <TextView style ="@style/text_flag_01" android:text ="宁波" /> <TextView style ="@style/text_flag_01" android:text ="呼和浩特" /> <TextView style ="@style/text_flag_01" android:text ="上海" /> <TextView style ="@style/text_flag_01" android:text ="北京" /> <TextView style ="@style/text_flag_01" android:text ="重庆" /> <TextView style ="@style/text_flag_01" android:text ="南昌" /> <TextView style ="@style/text_flag_01" android:text ="苏州" /> </com.yuqirong.viewgroup.view.FlowLayout >
运行的效果图: