Android-超簡單的流式佈局
阿新 • • 發佈:2018-12-18
流式佈局,一般在商城類的專案中用到會非常多比如
淘寶中,購物選擇商品列表的時候,這個就是流式佈局
創作起來也很簡單,
只要你計算出寬度,和高度,如果超出螢幕寬度,則換行擺放即可
然後我就嘗試著寫了一下,果然還是可以的
效果圖
核心方法主要是viewgroup的layout方法
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 為什麼定義x,因為是從這個座標開始出發 // 往右進行擺放 int x = (int) (this.getPaddingLeft() + leftMargin); int paddingRight = this.getPaddingRight(); // 為什麼定義y,因為從上往下,top等於y軸線 int y = (int) (this.getPaddingTop() + topMargin); int sumWidth = r - l; int childCount = this.getChildCount(); int childMaxHeight = 0; for (int i = 0; i < childCount; i++) { View view = this.getChildAt(i); // 如果大於了。需要規整 if (sumWidth < x + view.getMeasuredWidth() + paddingRight) { // 跨行 // 改變x軸起始點 x = (int) (getPaddingLeft() + leftMargin); // 改變y軸起始點 y += childMaxHeight; childMaxHeight = 0; } view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight()); // 改變橫座標,切記加入view的寬度.否則會出問題 x += view.getMeasuredWidth() + rightMargin; // 取最大寬度,為下一步跨行做準備 childMaxHeight = (int) Math.max(childMaxHeight, view.getMeasuredHeight() + topMargin + bottomMargin); } }
其次就是測量方法了.測量方法需要測量出總大小來控制view的大小
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 獲取子view的數量 int childCount = this.getChildCount(); // 獲取到本view的寬度最大值 int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - this.getPaddingLeft() - this.getPaddingRight(); // 需要測量view的寬度以及view的高度。 // 所有的合集 // 總高度 int sumHeight = 0; // 一行的子類總高 int childMaxHeight = 0; // 總寬度 int sumWidth = 0; // 一行的子類總寬 int sumChildWidth = 0; for (int i = 0; i < childCount; i++) { View view = this.getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec); // 如果有leftMargin的話,需要在測量的時候,加上這個 sumChildWidth = (int) (sumChildWidth + leftMargin + rightMargin); // 如果小於兩者相加,所以超了,需要計算高度 // 取高度最大值,也就是所有控制元件的最大值 // 加完之後要清除,否則下一行高度無法計算 if (maxWidth < (sumChildWidth + view.getMeasuredWidth())) { // 跨行 sumHeight += childMaxHeight; childMaxHeight = 0; // 跟自己比較,獲取最大值,優先取最大 sumWidth = Math.max(sumChildWidth, sumChildWidth); sumChildWidth = 0; } // 判斷子類高度最大值 childMaxHeight = (int) Math.max(childMaxHeight, view.getMeasuredHeight() + topMargin + bottomMargin); // 取子類行總寬,需要判斷父類的寬度 sumChildWidth += view.getMeasuredWidth(); } // 因為最後一行可能沒有超過,所以不會進入,則需要重新加一下最後一行 sumHeight += childMaxHeight; sumWidth = Math.max(sumChildWidth, sumWidth); setMeasuredDimension(measureWidth(widthMeasureSpec, sumWidth), measureHeight(heightMeasureSpec, sumHeight)); } private int measureHeight(int heightMeasureSpec, int sumHeight) { int result = 0; int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); //EXACTLY //精確值模式,當控制元件的layout_width和layout_height屬性指定為具體數值或match_parent時。 //AT_MOST //最大值模式,當空間的寬高設定為wrap_content時。 //UNSPECIFIED //未指定模式,View想多大就多大,通常在繪製自定義View時才會用。 // 如果為精確值模式,那麼不用判斷了,直接返回 if (mode == MeasureSpec.EXACTLY) { result = size; return result; } result = sumHeight + this.getPaddingTop() + this.getPaddingBottom(); if (mode == MeasureSpec.AT_MOST) { result = Math.min(size, result); } return result; } private int measureWidth(int widthMeasureSpec, int sumWidth) { int result = 0; int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); // 如果為精確值模式,那麼不用判斷了,直接返回 if (mode == MeasureSpec.EXACTLY) { result = size; return result; } result = widthMeasureSpec + this.getPaddingLeft() + this.getPaddingRight(); if (mode == MeasureSpec.AT_MOST) { result = Math.min(size, result); } return result; }
演示結果就更簡單了
int[] colors = { Color.DKGRAY, Color.GRAY, Color.LTGRAY, Color.WHITE, Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.CYAN, Color.MAGENTA, Color.TRANSPARENT }; FlowLayout viewById = (FlowLayout) findViewById(R.id.fl); viewById.setItemMargin(10, 10, 10, 10); for (int i = 0; i < 1000; i++) { TextView textView = new TextView(MainActivity.this); textView.setText("我是條目 " + i); int i1 = new Random().nextInt(colors.length); textView.setBackgroundColor(colors[i1]); viewById.addView(textView); }
詳細可以移步我的github