-
Android View measurements are best understood first
-
Github source address
-
Yards cloud address
-
Let’s take a look at the functions to be implemented this time
1. Streaming layout analysis
1. Width and height of each row
- PaddingLeft of FlowLayout + paddingRight. If this value is larger than the width of the FlowLayout, you need to start a new line and addView
- The height required for each row is the maximum height of all child views
- When a line is wrapped, the height value is the height used + the height needed for the line. The height is stored in the List, followed by onLayout for the position of the view
- The child view is stored as a List
-
, which is a two-dimensional array for onLayout
2. Sub-view measurement
- Each child view gets its own LayoutParams, plus FlowLayout’s MeasureSpec
- Get the child view’s MeasureSpec from the getChildMeasureSpec method
- Measure (childWidthMeasureSpec, childHeightMeasureSpec)
- Finally, childview. measuredWidth is the actual measuredWidth measured by the childView
3. Size of FlowLayout (onMeasure)
- Here black represents the Activity, the outermost ViewGroup
- FlowLayout = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent; If not, it will be affected by the size of the child view, we need to measure how much space the child view needs to add up, and then assign the required space size. The front is equivalent to giving you two hundred square meters of the house, how to divide the small room inside, will not exceed two hundred square, and the back is a small room add up, and finally you need to give how much space.
- The blue color represents the child View. The width and height of the child View affect their arrangement. If the width reaches the maximum FlowLayout, you need to break the line.
4. OnLayout
- The layout position starts at the upper left corner of the FlowLayout, but includes paddingTop and paddingLeft
- Using the size of the child view calculated at onMeasure, loop to place the child view in the specified position
Code 2.
1. FlowLayout onMeasure
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
clearMeasureParams()
// Width and height of the parent layout
var viewGroupWidth = MeasureSpec.getSize(widthMeasureSpec)
var viewGroupHeight = MeasureSpec.getSize(heightMeasureSpec)
// The child View needs the size of the parent control
var viewGroupNeedWidth = 0
var viewGroupNeedHeight = 0
// Subview for each row
var lineViews: MutableList<View> = ArrayList()
// The used width of each line
var lineUseWidth = 0
// The height used for each row
var lineUseHeight = 0
// Iterate over all child Views
for (i in 0 until childCount) {
val childView = getChildAt(i)
// The subview is visible
if (childView.visibility == View.VISIBLE) {
// Get the child View
val layoutParams = childView.layoutParams
// The child view's measureSpec, the parent layout's measureSpec, the parent layout's inner margin, and the child view's size (greater than 0 is the exact size, -1 is match_parent,-2 is wrap_content)
val childWidthMeasureSpec = getChildMeasureSpec(
widthMeasureSpec,
paddingLeft + paddingRight,
layoutParams.width
)
val childHeightMeasureSpec = getChildMeasureSpec(
heightMeasureSpec,
paddingTop + paddingBottom,
layoutParams.height
)
// The subview calls measure to get the exact width and height
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
/ / a newline
if (lineUseWidth + childView.measuredWidth + paddingLeft + paddingRight > viewGroupWidth) {
// Save each line of view
allViewList.add(lineViews)
lineHeightList.add(lineUseHeight)
viewGroupNeedWidth = Math.max(viewGroupNeedWidth, lineUseWidth)
viewGroupNeedHeight += lineUseHeight + verticalSpace
lineViews = ArrayList()
lineUseWidth = 0;
lineUseHeight = 0;
}
// Each line saves the subview
lineViews.add(childView)
// The width of each line
lineUseWidth += childView.measuredWidth + horizontalSpace
// Each row height
lineUseHeight = Math.max(lineUseHeight, childView.measuredHeight)
// The last line is special
if (i == childCount - 1) {
allViewList.add(lineViews)
lineHeightList.add(lineUseHeight)
viewGroupNeedWidth = Math.max(viewGroupNeedWidth, lineUseWidth)
viewGroupNeedHeight += lineUseHeight + verticalSpace
}
}
}
// remeasure the ViewGroup
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
// The width and height you really need,
// If the ViewGroup is MeasureSpec.EXACTLY, use the exact size, otherwise use the child view
// Add water padding, which is the desired size
val realWidth =
if (widthMode == MeasureSpec.EXACTLY)
viewGroupWidth
else
viewGroupNeedWidth + paddingLeft + paddingRight
val realHeight =
if (heightMode == MeasureSpec.EXACTLY)
viewGroupHeight
else
viewGroupNeedHeight + paddingTop + paddingBottom
setMeasuredDimension(realWidth, realHeight)
}
Copy the code
2. FlowLayout onLayout
override fun onLayout( changed: Boolean, l: Int, t: Int, r: Int, b: Int ) {
val lineCount = allViewList.size
// The position of the child view layout starts at the upper left corner of the ViewGroup margin
var curLeft = paddingLeft
var curTop = paddingTop
for (i in 0 until lineCount) {
// Take out each row of subviews
val lineViews = allViewList.get(i)
for (childView in lineViews) {
val left = curLeft
val top = curTop
val right = left + childView.measuredWidth
val bottom = top + childView.measuredHeight
childView.layout(left, top, right, bottom)
curLeft = right + horizontalSpace
}
/ / a newline
val lineHeight = lineHeightList.get(i)
curTop += lineHeight + verticalSpace
curLeft = paddingLeft
}
}
Copy the code