FlexBoxlayout在項目的使用總結

前言

FlexBoxlayout是Google推出的開源的可伸縮性佈局,在項目中也會經場使用,大大提高了用戶的體驗。

本文主要記錄了 在項目中使用RecyclerView(多佈局item)+FlexBoxLayout時遇到的問題做個總結,希望對你有所幫助。

以自身的項目需求爲例,如圖所示:
在這裏插入圖片描述在這裏插入圖片描述
在”品牌篩選“、”客戶分組“中用了三處可伸縮性佈局。

首先回顧一下相關概念和屬性,已熟悉的同學可以直接看用法。

什麼是FlexboxLayout

看一下Github對這個庫的介紹:

FlexboxLayout is a library project which brings the similar capabilities of CSS Flexible Box Layout Module to Android.

意思是:FlexboxLayout是一個Android平臺上與CSS的 Flexible box 佈局模塊 有相似功能的庫。

使用方式跟LinearLayout、Relativelayout相似,不同的是相關屬性。使用也更爲靈活,此處不在贅述到底有哪些屬性。詳情請點擊flexbox_layout官方開源地址

如何使用FlexboxLayout

有兩種方式:

  • 第一種

    <com.google.android.flexbox.FlexboxLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:flexWrap="wrap"
        app:alignItems="stretch"
        app:alignContent="stretch" >
    
        <TextView
            android:id="@+id/textview1"
            android:layout_width="120dp"
            android:layout_height="80dp"
            app:layout_flexBasisPercent="50%"/>
    
        <TextView
            android:id="@+id/textview2"
            android:layout_width="80dp"
            android:layout_height="80dp"
            app:layout_alignSelf="center"/>
    
        <TextView
            android:id="@+id/textview3"
            android:layout_width="160dp"
            android:layout_height="80dp"
            app:layout_alignSelf="flex_end"/>
    </com.google.android.flexbox.FlexboxLayout>
    
  • 第二種

    FlexboxLayout flexboxLayout = (FlexboxLayout) findViewById(R.id.flexbox_layout);
    flexboxLayout.setFlexDirection(FlexDirection.ROW);
    
    View view = flexboxLayout.getChildAt(0);
    FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams) view.getLayoutParams();
    lp.order = -1;
    lp.flexGrow = 2;
    view.setLayoutParams(lp);
    

    介紹完基本使用後,下面說重點。

FlexboxLayout+RecyclerView的使用(重點)

在RecyclerView中,我已經知道 layoutManager支持了三種佈局管理器有LinearLayoutManager(線性佈局管理器)、GridLayoutManager(網格佈局管理器)、StaggeredGridLayoutManager (瀑布流佈局管理器)。

注:layoutManager:RecyclerView 會根據 Layout Manager 提供的視圖來填充自身。

使用方法爲:

RecyclerView recyclerView = (RecyclerView) context.findViewById(R.id.recyclerview);
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexDirection(FlexDirection.COLUMN);
layoutManager.setJustifyContent(JustifyContent.FLEX_END);
recyclerView.setLayoutManager(layoutManager);

或者改變FlexboxLayoutManager中的屬性值,方法比如:

mImageView.setImageDrawable(drawable);
ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
if (lp instanceof FlexboxLayoutManager.LayoutParams) {
    FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) lp;
    flexboxLp.setFlexGrow(1.0f);
    flexboxLp.setAlignSelf(AlignSelf.FLEX_END);
}

使用FlexBoxLayoutManager的優點是,它可以回收屏幕外的視圖,以便在用戶滾動時對顯示的視圖進行重用,而不是對每個視圖進行填充,這樣消耗的內存要少得多,特別是當FlexBox容器中包含的項目數很大時。

如瀑布流效果
https://github.com/google/flexbox-layout/raw/master/assets/flexbox-layoutmanager.gif

FlexboxLayout+RecyclerView的完美結合使用,繼承了各自己的優點。

項目中使用總結

最後總結一下在項目中RecyclerView使用多佈局時遇到的問題。

在第一張需求圖中,品牌篩選時 最後有一個”+“號,想要的效果時 根據品牌選擇後,這個”+“號會緊挨着顯示,如果顯示不完,就換行顯示。

剛開始考慮的是的在RecyclerView添加FooterView,FooterView爲一個“+”的按鈕,這個按鈕會換一行顯示,不能緊挨着顯示,如果超過一行再換行顯示。

此時會遇到兩個問題,

按照RecyclerView中的基本用法使用後 會報錯,日誌如下:

ClassCastException: android.support.v7.widget.RecyclerView$LayoutParams cannot be cast to com.google.android.flexbox.FlexItem。

我們添加的FooterView,無法轉換成FlexlItem。網上找到解決方案,詳情見FlexboxLayoutManager 踩坑

一是需要重寫FlexboxLayoutManager,代碼示例如下:

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)


    /**
     * 將LayoutParams轉換成新的FlexboxLayoutManager.LayoutParams
     */
    override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams {
        return when (lp) {
            //TODO:可能需要適配,特殊處理"+"的寬度
            is RecyclerView.LayoutParams -> LayoutParams(lp)
            is ViewGroup.MarginLayoutParams -> LayoutParams(lp)
            else -> LayoutParams(lp)
        }
    }
}

第二個問題:顯示問題。此時會出現我們的FooterView(”+“號)換一行顯示,不能緊挨着顯示。

於是陷入思考,在上述代碼中,RecyclerView.LayoutParams的計算方法LayoutParams(lp)也需要重寫。

在查閱資料和源碼後發現比較麻煩,最後在網友的幫助下(FlexboxLayoutManager 踩坑),換一種思路,用RecyclerView多佈局來實現,豁然開朗。

思路如下:在數據實體類中添加一個標誌,如是否是添加標誌isAdd,根據該值的是不同顯示不同的item佈局。

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)
    }
}

調用方法:

  //品牌篩選
rvBrand.layoutManager = FlexboxLayoutManager(context)
rvBrand.adapter = brandAdapter

至此,實現了想要的效果,問題得以解決。

參考資料:

0.flexbox_layout官方開源地址

1.Android可伸縮佈局-FlexboxLayout(支持RecyclerView集成)

2.FlexboxLayoutManager 踩坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章