流式布局---FlowLayout
1 public class FlowLayout extends ViewGroup { 2 public FlowLayout(Context context) { 3 this(context,null); 4 } 5 6 public FlowLayout(Context context, AttributeSet attrs) { 7 this(context, attrs,0); 8 } 9 10 public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { 11 super(context, attrs, defStyleAttr); 12 } 13 14 @Override 15 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 16 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 17 /** 18 * 测量=测量宽高+测量模式 19 * */ 20 //获得容器测量宽和高 21 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); 22 int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); 23 //测量模式 24 int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec); 25 int heigheMeasureMode = MeasureSpec.getMode(heightMeasureSpec); 26 27 //记录每一行的宽和高 28 int lineWidth=0;//每一行中最长的一行是他的宽度 29 int lineHeight=0;//几行的高度 30 31 //wrap_content-->AT_MOST 32 int width=0; 33 int height=0; 34 //获得内部元素的个数 35 int childCount = getChildCount(); 36 //遍历得到每一个View 37 for(int i=0;i<childCount;i++) 38 { 39 View child = getChildAt(i); 40 //测量子View的宽和高 41 measureChild(child,widthMeasureSpec,heightMeasureSpec); 42 //流式布局只需要知道每一个子View之间的margin,故创建MarginLayoutParams 43 //TODO 44 MarginLayoutParams lp= (MarginLayoutParams) child.getLayoutParams(); 45 //子View占据的宽度 46 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; 47 //子View占据的高度 48 int chileHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 49 //判断:如果需要换行 50 /** 51 * 如果TextView设置了pading,在比较的时候sizeWidth要去掉getPadingRight和getPadingLeft 52 */ 53 54 if(lineWidth+childWidth>sizeWidth) 55 { 56 //第一行的宽度 57 width=Math.max(width,lineWidth); 58 lineWidth=childWidth;//因为换行,故当前的宽度就是子View的宽度 59 height+=lineHeight;//当前的高度就是前一行的高度加上之前的高度 60 lineHeight=chileHeight;//下一行的高度 61 }else//未换行 62 { 63 lineWidth+=childWidth;//叠加行宽 64 lineHeight=Math.max(lineHeight,chileHeight);//得到当前行的最大高度 65 } 66 //如果到达最后一个控件 67 if(i==childCount-1) 68 { 69 width=Math.max(lineWidth,width); 70 height+=lineHeight; 71 } 72 73 } 74 75 /** 76 * EXACTLY:100dp,match_content 77 * AT_MOST:wrap_content 78 * UNSPICFIED:子控件想要多大就多大(一般不常用) 79 * 80 * */ 81 //如果是wrap_content 82 /** 83 * 如果TextView设置了pading,在比较的时候Width要加上getPadingRight和getPadingLeft 84 * height要加上getPadingTop和getPadingBottom 85 */ 86 setMeasuredDimension( 87 widthMeasureMode==MeasureSpec.EXACTLY?sizeWidth:width, 88 heigheMeasureMode==MeasureSpec.EXACTLY?sizeHeight:height); 89 90 } 91 92 //存储所有的View 93 /** 94 * 比如有三行,则mAllViews的长度就是3 95 * List<View>记录每一行的View 96 * */ 97 List<List<View>> mAllViews=new ArrayList<>(); 98 99 //每一行的高度 100 List<Integer> mLineHeight=new ArrayList<>(); 101 @Override 102 protected void onLayout(boolean b, int i, int i1, int i2, int i3) { 103 //该方法会调用多次,故:对List清除 104 mAllViews.clear(); 105 mLineHeight.clear(); 106 //当前ViewGroup的宽度和高度 107 int width = getWidth(); 108 // 109 int lineWidth=0; 110 int lineHeight=0; 111 List<View> lineViews=new ArrayList<>(); 112 int childCount = getChildCount(); 113 //遍历 114 for(int a=0;a<childCount;a++) 115 { 116 View child = getChildAt(a); 117 118 MarginLayoutParams lp= (MarginLayoutParams) child.getLayoutParams(); 119 int childHeight = child.getMeasuredHeight(); 120 int childWidth = child.getMeasuredWidth(); 121 //换行 122 /** 123 * 如果TextView设置了pading,,在比较的时候Width要加上getPadingRight和getPadingLeft 124 * top是getPadingTop 125 */ 126 if(childWidth+lp.leftMargin+lp.rightMargin+lineWidth>width) 127 { 128 129 //记录lineHeight 130 mLineHeight.add(lineHeight); 131 mAllViews.add(lineViews); 132 133 //重置 134 lineWidth=0; 135 lineHeight=childHeight+lp.topMargin+lp.bottomMargin; 136 //重置View的集合 137 lineViews=new ArrayList<>(); 138 } 139 lineWidth+=childWidth+lp.leftMargin+lp.rightMargin; 140 lineHeight=Math.max(lineHeight,childHeight+lp.topMargin+lp.bottomMargin); 141 lineViews.add(child); 142 } 143 144 //处理最后一行 145 mLineHeight.add(lineHeight); 146 mAllViews.add(lineViews); 147 148 /** 149 * 如果TextView设置了pading,left是getPadingLeft 150 * top是getPadingTop 151 */ 152 //设置子View的位置 153 int left=0;int top=0; 154 int lineNums=mAllViews.size(); 155 for(int a=0;a<lineNums;a++) 156 { 157 //当前行的所有View 158 lineViews=mAllViews.get(a); 159 lineHeight=mLineHeight.get(a); 160 for(int j=0;j<lineViews.size();j++) 161 { 162 View child=lineViews.get(j); 163 if(child.getVisibility()==View.GONE) 164 { 165 continue; 166 } 167 MarginLayoutParams lp= (MarginLayoutParams) child.getLayoutParams(); 168 //获取上下左右 169 int lc=left+lp.leftMargin; 170 int tc=top+lp.topMargin; 171 int rc=lc+child.getMeasuredWidth(); 172 int bc=tc+child.getMeasuredHeight(); 173 174 //为子View进行布局 175 child.layout(lc,tc,rc,bc); 176 left+=child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin; 177 } 178 /** 179 * 如果TextView设置了pading,left是getPadingLeft 180 */ 181 left=0; 182 top+=lineHeight; 183 } 184 } 185 每一个ViewGroup都会对应一个LayoutParams,在这里,我们只需要知道每一个子View之间的偏移量,故而只需要创建MarginLayoutParams, 186 //与当前的ViewGroup对应的LayoutParams,这个方法一定要写 187 @Override 188 public LayoutParams generateLayoutParams(AttributeSet attrs) { 189 return new MarginLayoutParams(getContext(),attrs); 190 } 191 }
以下是对应的xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.z.flowlayout.MainActivity"> <com.example.z.flowlayout.FlowLayout android:id="@+id/id_flowLayout" android:layout_width="wrap_content" android:layout_height="match_parent"></com.example.z.flowlayout.FlowLayout> </RelativeLayout>
以下是MainActivity的全部代码
public class MainActivity extends AppCompatActivity { private String[] views=new String[]{ "Hello World","Android","PHP","java", "UI","Welcome", "TAKE CLASS","Lisa", "Marry","Willen","Mark","Henry","Thank", "No","Yes","Sorry"}; private FlowLayout flowLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); flowLayout = (FlowLayout) findViewById(R.id.id_flowLayout); initData(); } private void initData() { for(int i=0;i<views.length;i++) { TextView tv = (TextView) LayoutInflater.from(this).inflate(R.layout.tv, flowLayout,false); tv.setText(views[i]); flowLayout.addView(tv); } } }