使用
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_flowlayout); FlowLayout flow_layout = (FlowLayout) findViewById(R.id.flow_layout); //一定要注意,我们自定义的FlowLayout中使用的是MarginLayoutParams,所以这里也只能用MarginLayoutParams,不然报ClassCastException MarginLayoutParams marginLayoutParams = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); int margins = (int) (2 * getResources().getDisplayMetrics().density + 0.5f); marginLayoutParams.setMargins(margins, margins, margins, margins); TextView tv1 = new TextView(new ContextThemeWrapper(this, R.style.text_style3), null, 0);//这是代码中设置style的方法! TextView tv2 = new TextView(new ContextThemeWrapper(this, R.style.text_style2), null, 0); TextView tv3 = new TextView(new ContextThemeWrapper(this, R.style.text_style1), null, 0); TextView tv4 = new TextView(new ContextThemeWrapper(this, R.style.text_style2), null, 0); tv1.setText("代码中添加View"); tv2.setText("并设置style"); tv3.setText("并设置margins"); tv4.setText("博客:http://www.cnblogs.com/baiqiantao/,如果TextView内容特别长会是这种效果"); tv1.setLayoutParams(marginLayoutParams); tv2.setLayoutParams(marginLayoutParams); tv3.setLayoutParams(marginLayoutParams); tv4.setLayoutParams(marginLayoutParams); flow_layout.addView(tv1); flow_layout.addView(tv2); flow_layout.addView(tv3); flow_layout.addView(tv4); } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#E1E6F6" android:orientation="vertical" > <com.bqt.myview.FlowLayout android:id="@+id/flow_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView style="@style/text_style1" android:text="包青天" /> <TextView style="@style/text_style1" android:text="流式布局" /> <TextView style="@style/text_style1" android:text="自定义ViewGroup" /> <TextView style="@style/text_style2" android:text="包青天" /> <TextView style="@style/text_style2" android:text="奋发图强" /> <TextView style="@style/text_style2" android:text="[email protected]" /> <TextView style="@style/text_style3" android:text="努力工作" /> <TextView style="@style/text_style3" android:text="月薪15K起" /> <TextView style="@style/text_style3" android:text="移动开发" /> <TextView style="@style/text_style3" android:text="android开发" /> </com.bqt.myview.FlowLayout> </LinearLayout>
分析
简单的分析 1、对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用MarginLayoutParams,我们需要重写ViewGroup 的几个相应的方法。 2、onMeasure中计算所有child的宽高,然后根据child的宽高,计算自己的宽和高。当然,如果不是wrap_content,直接使用父ViewGroup传入的计算值即可。 3、onLayout中对所有的childView进行布局。
onMeasure方法
- 首先得到其父容器传入的测量模式和宽高的计算值,然后遍历所有的childView,使用measureChild方法对所有的childView进行测量。
- 然后根据所有childView的测量得出的宽和高,得到该ViewGroup设置为wrap_content时的宽和高。
- 最后根据模式,如果是MeasureSpec.EXACTLY则直接使用父ViewGroup传入的宽和高,否则设置为自己计算的宽和高。
onLayout方法
- onLayout中完成对所有childView的位置以及大小的指定
- 遍历所有的childView,用于设置allViews的值,以及mLineHeight的值。
- 根据allViews的长度,遍历所有的行
- 遍历每一行的中所有的child,对child的left , top , right , bottom 进行计算和定位。
- 重置left和top,准备计算下一行的childView的位置。
View
public class FlowLayout extends ViewGroup { /**存储所有的View,按行记录*/ private List<List<View>> mAllViews = new ArrayList<List<View>>(); /**记录每一行的最大高度*/ private List<Integer> mLineHeight = new ArrayList<Integer>(); /**布局的宽高*/ private int width = 0, height = 0; /**每一行的宽度,width不断取其中最大的宽度*/ private int lineWidth = 0; /**每一行的高度,累加至height*/ private int lineHeight = 0; /**布局中的子控件*/ private View child; /**布局中子控件设置的LayoutParam*/ private MarginLayoutParams layoutParams; public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected LayoutParams generateLayoutParams(LayoutParams p) { return new MarginLayoutParams(p); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected LayoutParams generateDefaultLayoutParams() { return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //根据所有子控件设置自己的宽和高 for (int i = 0; i < getChildCount(); i++) { child = getChildAt(i); // 让系统去测量当前child的宽高 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 获取当前child实际占据的宽高 layoutParams = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; int childHeight = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; //如果加入当前child后超出最大允许宽度,则将目前最大宽度给width,累加height,然后开启新行 if (lineWidth + childWidth > MeasureSpec.getSize(widthMeasureSpec)) { width = Math.max(lineWidth, childWidth);// 对比得到最大宽度 // 开启新行,将当前行的宽高设为当前child的宽高 lineWidth = childWidth; lineHeight = childHeight; // 累加行高 height += lineHeight; } else { // 否则(不换行)累加行宽,lineHeight取最大高度 lineWidth += childWidth; lineHeight = Math.max(lineHeight, childHeight); } // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较,并累加行高 if (i == getChildCount() - 1) { width = Math.max(width, lineWidth); height += lineHeight; } } //如果是布局中设置的是wrap_content,设置为我们计算的值;否则,直接设置为父容器测量的值。 width = (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) ? MeasureSpec.getSize(widthMeasureSpec) : width; height = (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) ? MeasureSpec.getSize(heightMeasureSpec) : height; setMeasuredDimension(width, height); } @SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllViews.clear(); mLineHeight.clear(); lineWidth = 0; lineHeight = 0; // 存储每一行所有的childView List<View> lineViews = new ArrayList<View>(); for (int i = 0; i < getChildCount(); i++) { child = getChildAt(i); layoutParams = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 如果需要换行 if (childWidth + layoutParams.leftMargin + layoutParams.rightMargin + lineWidth > width) { // 记录这一行所有的View中的最大高度 mLineHeight.add(lineHeight); // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView mAllViews.add(lineViews); lineWidth = 0;// 重置行宽 lineViews = new ArrayList<View>(); } //如果不需要换行,则累加 lineWidth += childWidth + layoutParams.leftMargin + layoutParams.rightMargin; lineHeight = Math.max(lineHeight, childHeight + layoutParams.topMargin + layoutParams.bottomMargin); lineViews.add(child); } // 记录最后一行 mLineHeight.add(lineHeight); mAllViews.add(lineViews); //记录当前child相对前一个child的坐标位置 int left = 0; int top = 0; // 一行一行的遍历 for (int i = 0; i < mAllViews.size(); i++) { // 遍历每一行 lineViews = mAllViews.get(i); for (int j = 0; j < lineViews.size(); j++) { child = lineViews.get(j); if (child.getVisibility() == View.GONE) continue; layoutParams = (MarginLayoutParams) child.getLayoutParams(); //计算child的坐标 int leftPosition = left + layoutParams.leftMargin; int topPosition = top + layoutParams.topMargin; int rightPosition = leftPosition + child.getMeasuredWidth(); int bottomPosition = topPosition + child.getMeasuredHeight(); //对child进行布局 child.layout(leftPosition, topPosition, rightPosition, bottomPosition); //相对位置右移 left = rightPosition + layoutParams.rightMargin; } //相对位置从左侧重头开始,并下移 left = 0; top += mLineHeight.get(i); } } }
样式、背景
样式 <style name="text_style1"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_margin">2dp</item> <item name="android:background">@drawable/flowlayout_shape1</item> <item name="android:textColor">#ffffff</item> <item name="android:paddingLeft">5dp</item> <item name="android:paddingRight">5dp</item> <item name="android:textSize">11sp</item> </style> <style name="text_style2"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_margin">2dp</item> <item name="android:background">@drawable/flowlayout_shape2</item> <item name="android:textColor">#880</item> <item name="android:textSize">11sp</item> </style> <style name="text_style3"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_margin">2dp</item> <item name="android:background">@drawable/flowlayout_shape3</item> <item name="android:textColor">#43BBE7</item> <item name="android:textSize">11sp</item> </style>
背景图 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#7690A5" /> <corners android:radius="5dp" /> <padding android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp" /> </shape>
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#FFFFFF" /> <corners android:radius="40dp"/> <stroke android:color="#ff0000" android:width="2dp"/> <padding android:bottom="2dp" android:left="10dp" android:right="10dp" android:top="2dp" /> </shape>
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#C1FFC1" /> <stroke android:width="1dp" android:color="#912CEE" /> <corners android:radius="40dp" /> <padding android:bottom="2dp" android:left="10dp" android:right="10dp" android:top="2dp" /> </shape>
时间: 2024-10-23 22:04:36