最近公司有個(gè)外賣項(xiàng)目,里面有個(gè)添加商品的功能钦奋,類似web表單那種衰倦,添加商品時(shí)可添加套餐選項(xiàng)袒炉,頁面如下圖
add_goods.png
首先說一下這個(gè)頁面要完成的基本功能:
- 選擇商品類型,如果是套餐樊零,展開列表我磁,如果是非套餐類型的,收起列表驻襟;
- 可動(dòng)態(tài)添加和刪除整個(gè)配選項(xiàng)目夺艰;
- 可動(dòng)態(tài)添加和刪除配選項(xiàng)目里的選項(xiàng);
- 刪除配選項(xiàng)目時(shí)沉衣,不是真正把對(duì)應(yīng)的數(shù)據(jù)刪掉郁副,而是把數(shù)據(jù)源里的package_remove字段變?yōu)?,因?yàn)檫@個(gè)商品在服務(wù)器數(shù)據(jù)庫里還是存在的豌习,只是在這個(gè)套餐里沒有了而已存谎,所以肯定不是直接就把數(shù)據(jù)庫數(shù)據(jù)刪除掉拔疚;
之前踩過的坑
- 剛拿到這個(gè)頁面的時(shí)候,用了ScrollView里面嵌套一個(gè)Fragment既荚,F(xiàn)ragment里面是個(gè)RecyclerView列表稚失,RecyclerView的Item里面又嵌套一個(gè)RecyclerView,最終功能是現(xiàn)實(shí)了固以,但是感覺很難拓展和維護(hù)墩虹,也有一堆嵌套滑動(dòng)帶來的問題,最后還是重構(gòu)了代碼憨琳,換了一種實(shí)現(xiàn)方案诫钓。
新的實(shí)現(xiàn)方案
-
整個(gè)頁面用一個(gè)RecyclerView完成,商品的基本信息是HeaderView篙螟,套餐信息對(duì)應(yīng)List菌湃,有3種類型,最下面的新增配選項(xiàng)目是FooterView,如下圖
- HeaderView header_view.jpg
- item_type1 item_type1.jpg
- item_type2 item_type2.jpg
- item_type3 item_type3.jpg
- FooterView footer_view.jpg
- HeaderView
用代理模式來實(shí)現(xiàn)Bean,維護(hù)兩份數(shù)據(jù)遍略,一份真正的數(shù)據(jù)源惧所,提交到后臺(tái)的,一份顯示在界面上的數(shù)據(jù)源,只要維護(hù)好兩份數(shù)據(jù)對(duì)應(yīng)的關(guān)系就可以了绪杏,在界面輸入的時(shí)候下愈,已經(jīng)同步修改了兩處的數(shù)據(jù)
public class GoodsPackageModel implements Parcelable {
private String package_id;
private String package_name;
private String package_type;
private int package_remove;
private List<GoodsPackageTagModel> package_tags;
public String getPackage_id() {
return package_id;
}
public void setPackage_id(String package_id) {
this.package_id = package_id;
}
public String getPackage_name() {
return package_name;
}
public void setPackage_name(String package_name) {
this.package_name = package_name;
}
public String getPackage_type() {
return package_type;
}
public void setPackage_type(String package_type) {
this.package_type = package_type;
}
public int getPackage_remove() {
return package_remove;
}
public void setPackage_remove(int package_remove) {
this.package_remove = package_remove;
}
public List<GoodsPackageTagModel> getPackage_tags() {
return package_tags;
}
public void setPackage_tags(List<GoodsPackageTagModel> package_tags) {
this.package_tags = package_tags;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.package_id);
dest.writeString(this.package_name);
dest.writeString(this.package_type);
dest.writeInt(this.package_remove);
dest.writeTypedList(this.package_tags);
}
public GoodsPackageModel() {
}
protected GoodsPackageModel(Parcel in) {
this.package_id = in.readString();
this.package_name = in.readString();
this.package_type = in.readString();
this.package_remove = in.readInt();
this.package_tags = in.createTypedArrayList(GoodsPackageTagModel.CREATOR);
}
public static final Creator<GoodsPackageModel> CREATOR = new Creator<GoodsPackageModel>() {
@Override
public GoodsPackageModel createFromParcel(Parcel source) {
return new GoodsPackageModel(source);
}
@Override
public GoodsPackageModel[] newArray(int size) {
return new GoodsPackageModel[size];
}
};
@Override
public String toString() {
return "GoodsPackageModel{" +
"package_id='" + package_id + '\'' +
", package_name='" + package_name + '\'' +
", package_type='" + package_type + '\'' +
", package_remove=" + package_remove +
", package_tags=" + package_tags +
'}';
}
}
public class GoodsMultiItemBasicTab implements MultiItemEntity {
public static final int BASIC = 1;
private GoodsPackageModel item;
public GoodsMultiItemBasicTab(GoodsPackageModel item) {
this.item = item;
}
public String getName() {
return item.getPackage_name();
}
public void setName(String name) {
item.setPackage_name(name);
}
public String getType() {
return item.getPackage_type();
}
public void setType(String type) {
item.setPackage_type(type);
}
@Override
public int getItemType() {
return BASIC;
}
public GoodsPackageModel getGoodsPackageModel() {
return item;
}
}
- 用
BaseRecyclerViewAdapterHelper
來實(shí)現(xiàn)多類型的item,用起來特別簡單
class GoodsPackageAdapter(mContext: Context, mData: MutableList<MultiItemEntity>) : BaseMultiItemQuickAdapter<MultiItemEntity, BaseViewHolder>(mData) {
init {
addItemType(GoodsMultiItemBasicTab.BASIC, R.layout.item_list_tab_basic)
addItemType(GoodsMultiItemDeleteTab.TABS, R.layout.item_list_tab_delete)
addItemType(GoodsMultiItemAddTab.ADD, R.layout.item_list_tab_add)
}
override fun convert(helper: BaseViewHolder, item: MultiItemEntity) {
when (helper.itemViewType) {
//名字和類型
GoodsMultiItemBasicTab.BASIC -> {
}
//選項(xiàng)卡類型
GoodsMultiItemDeleteTab.TABS -> {
}
//添加選項(xiàng)卡
GoodsMultiItemAddTab.ADD -> {
}
}
}
}
- 維護(hù)兩份數(shù)據(jù)的關(guān)系蕾久,比如刪除一個(gè)配選項(xiàng)目势似,真實(shí)的數(shù)據(jù)源對(duì)應(yīng)的package_remove置1,顯示的數(shù)據(jù)源直接刪除對(duì)應(yīng)項(xiàng)僧著。
/**
* 刪除套餐
*/
private fun deletePackage(position: Int) {
LogUtil.d("刪除整個(gè)套餐---" + position)
val basicModel = mAdapter.data[position] as GoodsMultiItemBasicTab
//真實(shí)
val goodsPackageModel = basicModel.goodsPackageModel
goodsPackageModel.package_remove = 1
//顯示
mShowData.remove(basicModel)
// 要用這種迭代器的方式履因,不然可能會(huì)報(bào)異常
val iterator = mShowData.iterator()
while (iterator.hasNext()) {
val entity = iterator.next()
if (entity is GoodsMultiItemDeleteTab) {
if (entity.goodsPackageModel === goodsPackageModel) {
iterator.remove()
}
}
if (entity is GoodsMultiItemAddTab) {
if (entity.goodsPackageModel === goodsPackageModel) {
iterator.remove()
}
}
}
val count = goodsPackageModel.package_tags.size + 2
mAdapter.notifyItemRangeRemoved(position + 1, count)
}
- 最后分享一下,踩過的一個(gè)坑盹愚,列表的item里面如果有EditText栅迄,輸入的內(nèi)容,滑動(dòng)列表的時(shí)候可能會(huì)數(shù)據(jù)錯(cuò)亂皆怕,要怎么有效的避免呢毅舆?查閱大量資料之后發(fā)現(xiàn)是因?yàn)榻裹c(diǎn)導(dǎo)致,解決方案如下:
val tagNumWatcher = object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun afterTextChanged(editable: Editable) {
if (editNum.hasFocus()) {//判斷當(dāng)前EditText是否有焦點(diǎn)在
model.foodNo = editable.toString()
if (!TextUtils.isEmpty(editable.toString().trim())) {
//搜索操作
listener?.onSearchNameByNum(model.foodNo)
}
}
}
}
editNum.onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
if (hasFocus) {
(view as EditText).addTextChangedListener(tagNumWatcher)
} else {
(view as EditText).removeTextChangedListener(tagNumWatcher)
}
}
最后獻(xiàn)上源碼鏈接:
RecyclerWebListDemo