前言
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
至此,實現了想要的效果,問題得以解決。
參考資料: