自定義ViewGroup實(shí)現(xiàn)類似B站歷史記錄布局
需求:為用戶提供ai畫圖的咒語標(biāo)簽,這些標(biāo)簽長短不一,既要類似瀑布流緊密排列,又能根據(jù)屏幕寬度自適應(yīng)換行。

基本思路:將這塊區(qū)域的布局定義為MyFlowLayout作為父布局,并且自定義一個(gè)MyFlowLayout類作為父布局類。在java中定義一個(gè)LinearLayout類alternationLayout對(duì)應(yīng)標(biāo)簽item文件,獲取到標(biāo)簽的數(shù)據(jù)源后,使用alternationLayout.addview()方法,將標(biāo)簽item作為childView向父布局中動(dòng)態(tài)添加。
自定義ViewGroup的基本步驟:
1.重寫onMeasure方法:
先測量子布局,后確定父布局。子布局一個(gè)個(gè)傳入,測量的寬度和高度隨之增加,裝填完成時(shí),父布局大小隨之確定。
換行的邏輯:
該行寬度lineWidth再加一個(gè)子控件寬度childWidth,若超過父控件寬度sizeWidth(即1080px)則換行。換行之前,記下這一行的寬度lineWidth,并且判斷這行是否是目前最寬的,最寬的要作為父布局寬度width。而對(duì)于高度heigth,未換行時(shí),每傳入一個(gè)都會(huì)判斷該child是否是最高的,最高的作為lineHeight。(本例每個(gè)條目高度一樣,而該方法可以復(fù)用在子控件高度不同的場景。)
最后一個(gè)childView:將最大寬傳給width,高度疊加本行后傳給height(因?yàn)樽詈笠粋€(gè)有可能不用換行,循環(huán)中可以看到不換行時(shí)是不會(huì)刷新height值的)。這兩個(gè)值會(huì)由onLayout調(diào)用。
日志


2.重寫onLayout方法:
第一步是用集合存放每一行的高度和每一行的子控件集合,繪制的時(shí)候要使用。第二部用它們來布局,因?yàn)閘ayout方法的四個(gè)參數(shù)確定的是左上角和右下角的位置。
子控件布局邏輯:左上角的位置,在不換行時(shí),top不變,left按子控件寬度自增,right是left+控件寬。換行時(shí),top位置加一個(gè)行高,left歸零。
3.綁定視圖
alternation_card_item
【知識(shí)補(bǔ)充】
`ViewGroup` 是 Android 中一個(gè)用于容納和管理其他 View 的視圖類,它繼承自 `View` 類。下面是 `ViewGroup` 類中一些常用的方法:
1. `addView(View child)`:向 ViewGroup 中添加一個(gè)子視圖。
2. `removeView(View view)`:從 ViewGroup 中移除指定的子視圖。
3. `getChildAt(int index)`:獲取 ViewGroup 中指定位置的子視圖。
4. `getChildCount()`:獲取 ViewGroup 中子視圖的數(shù)量。
5. `onLayout(boolean changed, int l, int t, int r, int b)`:為 ViewGroup 中的子視圖設(shè)置布局參數(shù)。
6. `setLayoutParams(ViewGroup.LayoutParams params)`:設(shè)置 ViewGroup 的布局參數(shù)。
7. `setOrientation(int orientation)`:設(shè)置 ViewGroup 的方向,可以是水平或垂直。
8. `generateLayoutParams(AttributeSet attrs)`:根據(jù)給定的屬性集生成一個(gè)布局參數(shù)對(duì)象。
此外,還有一些方法用于重載視圖的繪制過程:
9. `dispatchDraw(Canvas canvas)`:在繪制 ViewGroup 時(shí)調(diào)用其所有子視圖的 `draw()` 方法。
10. `drawChild(Canvas canvas, View child, long drawingTime)`:在繪制 ViewGroup 時(shí)調(diào)用其某個(gè)子視圖的 `draw()` 方法。
還有一些其他方法,比如:
11. `requestLayout()`:請(qǐng)求 ViewGroup 重新進(jìn)行測量和布局。
12. `onMeasure(int widthMeasureSpec, int heightMeasureSpec)`:測量 ViewGroup 及其所有子視圖的尺寸。
13. `onInterceptTouchEvent(MotionEvent ev)`:攔截觸摸事件,用于在 ViewGroup 內(nèi)部處理事件。
14. `dispatchTouchEvent(MotionEvent ev)`:分發(fā)觸摸事件,將其傳遞給所有子視圖進(jìn)行處理。
以下是一些常用的回調(diào)方法:
onMeasure(int widthMeasureSpec, int heightMeasureSpec):測量子視圖的尺寸大小。
onLayout(boolean changed, int left, int top, int right, int bottom):對(duì)子視圖進(jìn)行布局。
dispatchDraw(Canvas canvas):繪制子視圖及其自身的內(nèi)容。
onInterceptTouchEvent(MotionEvent ev):攔截子視圖的觸摸事件。
onDetachedFromWindow():當(dāng) ViewGroup 從窗口中移除時(shí)會(huì)被調(diào)用,用于清理資源。