如何簡單的自定義一個自動換行的流式布局(TagLayout)

批注 2020-02-10 212314.png

先上完整代碼:

public class TagLayout extends ViewGroup {

  List<Rect> childrenBounds = new ArrayList<>();

  public TagLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 0; i < getChildCount(); i++) {
      View child = getChildAt(i);
      Rect childBound = childrenBounds.get(i);
      child.layout(childBound.left, childBound.top, childBound.right, childBound.bottom);
    }
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int lineMaxWidth = 0;
    int heightUse = 0;
    int widthUse = 0;
    int lineMaxHeight = 0;

    int specMode=MeasureSpec.getMode(widthMeasureSpec);

    int specWidth = MeasureSpec.getSize(widthMeasureSpec);

    for (int i = 0; i < getChildCount(); i++) {
      View child = getChildAt(i);

      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);

      if (specMode!=MeasureSpec.UNSPECIFIED&&widthUse + child.getMeasuredWidth() > specWidth) {
        // 另起一行
        widthUse = 0;
        heightUse += lineMaxHeight;
        lineMaxHeight=0;
        measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);
      }

      Rect childBound;
      if (childrenBounds.size() <= i) {
        childBound = new Rect();
        childrenBounds.add(childBound);
      } else {
        childBound = childrenBounds.get(i);
      }

      childBound.set(
          widthUse,
          heightUse,
          widthUse + getMeasuredWidth(),
          heightUse + getMeasuredHeight());

      widthUse += child.getMeasuredWidth();
      lineMaxWidth = Math.max(lineMaxWidth, widthUse);
      lineMaxHeight = Math.max(lineMaxHeight, child.getMeasuredHeight());
    }

    int width = lineMaxWidth;
    int height = lineMaxHeight+heightUse;
    setMeasuredDimension(width, height);
  }

  @Override
  public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new MarginLayoutParams(getContext(), attrs);
  }
}

## 準備工作
定義一個容器來存儲子View的位置信息:
```java
  List<Rect> childrenBounds = new ArrayList<>();

先從onMeasure()階段開始

定義了四個量:

    int widthUse = 0;//使用了多少寬度
    int heightUse = 0;//使用了多少高度
    int lineMaxWidth = 0; //當前最大寬度
    int lineMaxHeight = 0;//當前最大高度

這個布局是個橫向排列的過程覆醇,我們用一個widthUse來記錄當前行橫向使用的寬度捆姜,則計算下個子View位置時候,只需在此基礎上累加即可识啦。
lineMaxHeight用來記錄當前行的子View中的最大高度负蚊,當換行時候傳遞給heightUse,使下一階段的子View添加下沉高度颓哮。

這段代碼是什么意義:

   measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);

這其實是安卓幫我們做了一個自動計算的過程:

    LayoutParams layoutParams = child.getLayoutParams();

      int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
      int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);

      int childWidthMode;
      int childWidthSize;

      switch (layoutParams.width) {
        case LayoutParams.MATCH_PARENT:
          switch (specWidthMode) {
            case MeasureSpec.EXACTLY:
            case MeasureSpec.AT_MOST:
              childWidthMode = MeasureSpec.EXACTLY;
              childWidthSize = specWidthSize - useWidth;
              break;
            case MeasureSpec.UNSPECIFIED:
              childWidthMode = MeasureSpec.UNSPECIFIED;
              childWidthSize = 0;
              break;
          }
          break;
        case LayoutParams.WRAP_CONTENT:
          break;
      }

可以類比為如上的代碼家妆,根據(jù)開發(fā)者傳入的參數(shù)類型進行數(shù)值的自動適配

onLayout()階段

循環(huán)設置布局參數(shù)

    for (int i = 0; i < getChildCount(); i++) {
      View child = getChildAt(i);
      Rect childBound = childrenBounds.get(i);
      child.layout(childBound.left, childBound.top, childBound.right, childBound.bottom);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冕茅,隨后出現(xiàn)的幾起案子伤极,更是在濱河造成了極大的恐慌蛹找,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哨坪,死亡現(xiàn)場離奇詭異庸疾,居然都是意外死亡,警方通過查閱死者的電腦和手機当编,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門届慈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忿偷,你說我怎么就攤上這事金顿。” “怎么了牵舱?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵串绩,是天一觀的道長。 經(jīng)常有香客問我芜壁,道長礁凡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任慧妄,我火速辦了婚禮顷牌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘塞淹。我一直安慰自己窟蓝,他們只是感情好,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布饱普。 她就那樣靜靜地躺著运挫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪套耕。 梳的紋絲不亂的頭發(fā)上谁帕,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音冯袍,去河邊找鬼匈挖。 笑死,一個胖子當著我的面吹牛康愤,可吹牛的內(nèi)容都是我干的儡循。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼征冷,長吁一口氣:“原來是場噩夢啊……” “哼择膝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起检激,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤调榄,失蹤者是張志新(化名)和其女友劉穎踊赠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體每庆,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡筐带,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缤灵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伦籍。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腮出,靈堂內(nèi)的尸體忽然破棺而出帖鸦,到底是詐尸還是另有隱情,我是刑警寧澤胚嘲,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布作儿,位于F島的核電站,受9級特大地震影響馋劈,放射性物質(zhì)發(fā)生泄漏攻锰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一妓雾、第九天 我趴在偏房一處隱蔽的房頂上張望娶吞。 院中可真熱鬧,春花似錦械姻、人聲如沸妒蛇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绣夺。三九已至,卻和暖如春欢揖,著一層夾襖步出監(jiān)牢的瞬間陶耍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工浸颓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人旺拉。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓产上,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛾狗。 傳聞我的和親對象是個殘疾皇子晋涣,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容