一、基礎準備
- 《Android 手把手進階自定義View(六)- measure 測量過程解析》
- 《Android 手把手進階自定義View(七)- layout 佈局過程解析》
- 《Android 手把手進階自定義View(八)- draw 繪製過程解析》
前三篇我們學習了 View 的三大流程:測量、佈局、繪製,本篇我們來做一個自動換行的 ViewGroup。
二、自動換行的 ViewGroup
具體要實現的部分是如上圖所示的尺碼部分,超過一行後會自動換到下一行。
完整代碼如下:
class FlexLayout(context: Context?, attrs: AttributeSet?) : ViewGroup(context, attrs) {
//子view的rect
var childrenBounds = ArrayList<Rect>()
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)
//已使用寬度
var widthUsed = 0
//已使用高度
var heightUsed = 0
//當前行width已使用的寬度
var lineWidthUsed = 0
//當前行view的高度最大值
var lineMaxHeight = 0
for (i in 0 until childCount) {
//獲取子view
val childView = getChildAt(i)
//測量子view,因爲要換行,所以這裏的widthUsed不傳入,我們下面自己計算
measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0)
//換行邏輯
if (widthSpecMode != MeasureSpec.UNSPECIFIED && lineWidthUsed + childView.measuredWidth > widthSpecSize) {
lineWidthUsed = 0
heightUsed += lineMaxHeight
lineMaxHeight = 0
measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, heightUsed)
}
//避免重複創建
var childBound: Rect
if (childrenBounds.size <= i) {
childBound = Rect()
childrenBounds.add(childBound)
} else {
childBound = childrenBounds[i]
}
//設置rect邊界
childBound.set(
lineWidthUsed, heightUsed, lineWidthUsed + childView.measuredWidth,
heightUsed + childView.measuredHeight
)
//當前行使用寬度加上當前childView的測量寬度
lineWidthUsed += childView.measuredWidth
//計算最大的寬度
widthUsed = Math.max(widthUsed, lineWidthUsed)
//當前行childView的最大高度
lineMaxHeight = Math.max(lineMaxHeight, childView.measuredHeight)
}
val width = widthUsed
//viewGroup的使用高度要加上最後一行的最大高度
val height = heightUsed + lineMaxHeight
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
for (i in 0 until childCount) {
val childView = getChildAt(i)
val childBounds = childrenBounds[i]
childView.layout(childBounds.left, childBounds.top, childBounds.right, childBounds.bottom)
}
}
override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
return MarginLayoutParams(context, attrs)
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.lerendan.customviewsample.study.FlexLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#9c9c9c"
android:gravity="center"
android:textSize="25dp"
android:text="上海市"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#FD6464"
android:gravity="center"
android:textSize="25dp"
android:text="廣州市"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#2196F3"
android:gravity="center"
android:textSize="25dp"
android:text="深圳市深圳市"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#00BCD4"
android:gravity="center"
android:textSize="25dp"
android:text="北京市"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="#EEFF41"
android:gravity="center"
android:textSize="25dp"
android:text="杭州市"/>
</com.lerendan.customviewsample.study.FlexLayout>
</LinearLayout>
Build 後 xml 佈局文件中的效果:
可以看到 ViewGroup 的寬高都符合我們的預期。再看看實際運行效果 :