Flex layout

FlexBoxlayout is an open source, scalable layout from Google that can be used in projects to greatly improve the user experience.

The compile 'com. Google. Android: flexbox: 1.0.0'Copy the code

Those of you who have a basic knowledge of the front end probably know this layout in CSS to provide maximum flexibility for the box model. Since the properties of the library in Android are the same as those in CSS, and the front-end knowledge written by Mr. Ruan is really easy to understand, most of the introduction here comes from the Flex layout tutorial.

Elements with Flex layouts are called Flex Containers, or “containers” for short. All of its child elements automatically become members of the container and are called Flex items, or “projects” for short.

By default, a container has two axes: the horizontal main axis and the vertical cross axis. This is the opposite of React Native and is consistent with the front-end CSS.

The start position of the main axis (where it intersects with the border) is called Main start, and the end position is called Main end; The start position of the cross axis is called cross start and the end position is called cross end

Items are arranged along the main axis by default. The main axis space occupied by a single project is called main size, and the cross axis space occupied by a single project is called cross size.

2. Container properties (FlexboxLayout)

The container in question is the Flex layout element above, which in Android is the control that references FlexboxLayout. That is the property supported by the FlexboxLayout control. The main attributes are:

The detailed meaning of each attribute here will not repeat, Ruan Yifeng teacher this article written super good, illustrated, very easy to understand, recommend everyone to have a look.

3. Properties of the project (introduction to subview properties)

Set the properties of a child View wrapped in FlexboxLayout. Because the FlexboxLayout layout in Android is different from the Flex layout in CSS, the values are as follows:

  • layout_order (integer)
  • layout_flexGrow (float)
  • layout_flexGrow (float)
  • layout_alignSelf
  • layout_flexBasisPercent (fraction)
  • layout_minWidth / layout_minHeight (dimension)
  • layout_maxWidth / layout_maxHeight (dimension)
  • layout_wrapBefore (boolean)

Here’s a look at the Github documentation describing these properties

3.1 layout_order

This property changes the order of the layout subviews. By default, the child elements are displayed and laid out in the same order as in the layout XML. If not specified, 1 is set to the default value (the default value in CSS is 0). The smaller the value, the higher the rank.

If you look at the diagram in the document, you can see that when you set the layout_order property of View “2” to -1, the View “2” will rank first because all other views default to 1. Similarly, Set the layout_order property of View ‘2’ to 2, which is larger than the default value of 1, so it will rank last.

3.2 layout_flexGrow

This property is similar to the layout_weight property in LinearLayout, which sets 0 to the default value if not specified. If multiple child views in the same Flex row have positive layout_flexGrow values, the remaining free space will be distributed in proportion to their declared layout_flexGrow values.

3.3 layout_flexShrink

This property defines the size of the subview, which defaults to 1, meaning that the subview will shrink if there is not enough space. If all of the subviews have a layout_flexShrink property of 1, they will all be scaled down equally when space runs out. If a project’s layout_flexShrink property is 0 and all other subviews are 1, then the project’s layout_flexShrink property is 0 and does not shrink when space is insufficient.

If you set layout_flexShrink to 0, you can start by setting the layout_flexShrink attribute to 1 for all subviews. When you add a subview, all subviews are reduced by the same ratio. The child view will be displayed at its original scale, not shrunk.

3.4 layout_alignSelf

The layout_alignSelf property allows a single child View to have a different alignment than other views, overriding the align-items property. The default value is Auto, which means that the align-items property of the parent element is inherited, and is equivalent to stretch if there is no parent element. This property can take as many as six values, all of which are identical to the align-items property except auto

3.5 layout_flexBasisPercent

The Flek-layout_FLEXBasisPercent property defines the main size of the subview before excess space is allocated. Use this property to calculate whether the main axis has extra space. Its default value is -1, which means it is not set and the size of the child View is used.

If this value is set, the length specified in layout_width (or layout_height) will be overridden by the computed value of the property. This property is only valid if the parent View length is specified (the measurement mode is MeasureSpec.EXACTLY mode). And the attribute value only accepts percentage values.

Take a look at this graph in the document: You can see that if you set this property of the middle subview to 50% or 90%, that View will take up 50% or 90% of the main axis, and then the remaining View will see if there is room for line feeds. If set to -1 by default, it will take up the given size.

3.6 layout_minWidth/layout_minHeight

This property sets the minimum width and height of the child View. In layout_flexShrink mode, how much can you shrink it to less than this value

3.7 layout_maxWidth/layout_maxHeight

This property sets the maximum width and height of the child View. In layout_flexGrow mode, no size can be larger than this value

3.8 layout_wrapBefore

This property makes it possible for a child View to force a newline, regardless of how much space is left in the main size. This is especially useful for layouts such as grid where a specific item is set. This property is not in CSS. This property is invalid when the flex_wrap property value is nowrap (no line wraps). This property ends the Boolean variable, which defaults to false, meaning no line feeds are forced

Looking at this diagram in the document, views “5” and “6” that set the layout_wrapBefore property to true will force a wrap regardless of how much space is left in front

This concludes the basic properties of flexboxLayout.

And then combine that with recycleView.

4. High Energy: Combined with RecyclewView

Flexbox can be used as a LayoutManager(Flexbox LayoutManager) in RecyclerView, which means you can use Flexbox in a scrollable container with lots of items to improve performance. Example:

RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView If you want to set a separate item, you can set it in the Adapter. Set the example code to:

I’m setting each item to have a weight (equivalent to the weight property of the Linearlayout), so I’m assigning the item’s width proportionally instead of the fixed width and height that I set in my layout. Look at the effect:

Does it feel like a keyboard? And I did it with very little code change.

1

Finally, after seeing so much, back to the original problem, now know how to write the grid layout of the middle extension similar to wechat? Let’s start with a quick analysis,

  • We should set the axis direction to horizontal, that is, default flexDirection: “row” ‘
  • You can wrap a line, flexWrap: “wrap.”
  • The subview at the axis is challenged as being centered (this step justifies it being outgrown from the center to both sides), which justifies content: “center.”
  • The child View is centered in the direction of the cross axis, that is, alignItems: “center”
  • The width and height of the child View are fixed

And the width and height of the item should be adapted according to the screen. It’s so easy.

Experiment 2

In practical application, there is also a very common kind of classification selection layout, such as netease in the picture and short book, this layout with our protagonist today is not easy to achieve? You don’t have to set any special properties, and it wraps over one line. The code is as follows:

3

Take project requirements as an example, as shown in the figure below:

                                                        

In the first requirement map, there is a “+” sign at the end of brand filtering. When the desired effect is selected according to the brand, the “+” sign will be displayed next to each other. If the display is not complete, it will be displayed on a new line.

FooterView is a “+” button, this button will be displayed in a new line, not next to it, if more than one line and then a newline display.

There are two problems,

Error will be reported after the RecyclerView is used according to the basic usage, the log is as follows:

ClassCastException: Android. Support. V7. Widget. RecyclerView $LayoutParams always be cast to com. Google. Android. Flexbox. FlexItem.

The FooterView we added cannot be converted to FlexlItem. Find the solution online, see FlexboxLayoutManager for more details.

First, you need to rewrite FlexboxLayoutManager, as shown in the following code example:

import com.google.android.flexbox.FlexboxLayoutManager

class MyFlexboxLayoutManager : FlexboxLayoutManager {
    constructor(context: Context) : super(context)

    constructor(context: Context, flexDirection: Int) : super(context, flexDirection)

    constructor(context: Context, flexDirection: Int, flexWrap: Int) : super(context, flexDirection, flexWrap)


    / * * * will LayoutParams into new FlexboxLayoutManager LayoutParams * /
    override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams {
        return when (lp) {
            //TODO:May need to be adapted, especially to handle the width of "+"
            is RecyclerView.LayoutParams -> LayoutParams(lp)
            is ViewGroup.MarginLayoutParams -> LayoutParams(lp)
            else -> LayoutParams(lp)
        }
    }
}
Copy the code

Second problem: display problem. Our FooterView (” + “sign) will appear on a different line, not next to it.

So thinking, in the above code, RecyclerView. The calculation method of LayoutParams LayoutParams (lp) also needs to be rewritten.

In access to information and source code found more trouble, finally with the help of users (FlexboxLayoutManager step pit), in another way of thinking, with RecyclerView layout to achieve, suddenly bright.

The idea is as follows: Add a flag to the data entity class, such as isAdd or not, and display different item layouts according to the value.

class CameraVehicleBrandAdapter : MyBaseMultiItemAdapter<CameraVehicleBrandEntity>() {

    init {
        addItemType(CameraVehicleBrandEntity.ITEM_TYPE_NORMAL, R.layout.home_recycle_item_camera_vehicle_brand_selected)
        addItemType(CameraVehicleBrandEntity.ITEM_TYPE_ADD, R.layout.home_recycle_item_camera_vehicle_brand_add)
    }

    override fun convert(helper: BaseViewHolder, item: CameraVehicleBrandEntity) {
        super.convert(helper, item)
        if (item.isAdd) {
            helper.addOnClickListener(R.id.ivAddBrand)
            return
        }
        helper.setText(R.id.tvBrandName, item.brandName)
        helper.addOnClickListener(R.id.ivDelete)
    }
}
Copy the code

Call method:

  // Brand selection
rvBrand.layoutManager = FlexboxLayoutManager(context)
rvBrand.adapter = brandAdapter
Copy the code

conclusion

So with all that said, when do we use this layout? I have three main types of scenarios in mind:

  1. This is similar to LinearLayout, but you can wrap lines automatically
  2. Like a grid, but there are always one or two items that are arranged differently
  3. It is similar to waterfall flow, but there are always one or two items that are different from others

Of course, these scenes with RecycleView will be more smooth smooth.

The demo address: