RecyclerView之如何使用多種類型Item完成複雜界面

1. 應用場景

作電商APP的朋友應該都會遇到訂單列表頁面這個需求,相似於下圖這個需求。android

有些朋友可能會使用addView的方式來實現這個需求,不過在View到達一個數量級的時候,整個頁面會很是卡。這個需求其實能夠用一個RecyclerView中getItemViewType()這個函數實現這一個需求。簡單來講就是將一個二維數組變成一維數組,這樣就能夠直接傳入Adapter中使用,咱們再根據數據結構類型的不用來判斷加載哪種Item。git

2. 實現思路

2.1 界面切割

首先咱們要將這個Item進行切分,劃分爲3個部分,以下圖所示:github

  • 第一部分爲訂單頭部信息,包括店鋪名字與訂單狀態
  • 第二部分爲商品列表信息(多個)
  • 第三部分爲訂單尾部信息,包括商品數量,價格,以及不一樣的功能按鈕(可自定義)

根據頁面切割部分編寫各自對應的layout文件,後端

  • item_order_layout_header
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:background="@drawable/bg_item_header"
    android:layout_height="30dp">

    <ImageView
        android:id="@+id/iv_shop_icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:layout_marginStart="8dp"
        android:src="@drawable/icon_tmall"
        tools:ignore="ContentDescription" />

    <TextView
        android:id="@+id/tv_shop_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="8dp"
        android:layout_toEndOf="@id/iv_shop_icon"
        android:drawableEnd="@drawable/icon_enter"
        android:drawablePadding="8dp"
        android:textColor="@color/text_main"
        android:textSize="14sp"
        android:textStyle="bold"
        tools:text="Android死丟丟旗艦店" />

    <TextView
        android:id="@+id/tv_transaction_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_centerVertical="true"
        android:layout_marginEnd="8dp"
        android:textColor="@color/colorPrimary"
        android:textSize="14sp"
        android:textStyle="bold"
        tools:text="交易成功" />

</RelativeLayout>
複製代碼
  • item_order_layout_content
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="#fff">

    <ImageView
        android:id="@+id/iv_goods_icon"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:layout_marginStart="8dp"
        tools:ignore="ContentDescription"
        tools:src="@drawable/icon_default_goods" />

    <TextView
        android:id="@+id/tv_goods_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="12dp"
        android:layout_toEndOf="@id/iv_goods_icon"
        android:maxWidth="230dp"
        android:maxLines="2"
        android:textColor="@color/text_main"
        android:textSize="16sp"
        tools:text="垃圾Android死丟丟毀我青春" />

    <TextView
        android:id="@+id/tv_goods_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="8dp"
        android:maxWidth="64dp"
        android:textColor="@color/text_main"
        android:textSize="16sp"
        tools:ignore="RelativeOverlap"
        tools:text="¥499.00" />

    <TextView
        android:id="@+id/tv_goods_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_goods_price"
        android:layout_alignParentEnd="true"
        android:layout_marginEnd="8dp"
        android:textColor="@color/text_second"
        android:textSize="16sp"
        tools:text="x1" />

</RelativeLayout>
複製代碼
  • item_order_layout_footer
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_item_footer">

    <TextView
        android:id="@+id/tv_total_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="4dp"
        android:layout_marginEnd="8dp"
        tools:text="共兩件商品 合計: ¥999.00" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_below="@id/tv_total_price"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:background="@drawable/bg_button_main"
        android:text="確認收貨"
        android:textColor="#fff" />

</RelativeLayout>
複製代碼

2.2 JSON數據再封裝

在界面切割完成之後,咱們也要根據頁面切分的狀況進行後端返回JSON數據的封裝,將二維數組變成一維數組。數組

通常服務端返回數據的數據結構以下圖左所示,須要將圖左的數據結構轉爲右邊這種給RecyclerView使用:數據結構

具體代碼實現(類OrderListDataHelper)ide

public static List<Object> getDataAfterHandle(List<OrderListModel.RecordBean> resultList) {
    // 得到服務端返回Json實體
    List<Object> dataList = new ArrayList<>();// 新建單鏈表
    OrderListHeaderModel orderHeaderModel = null;
    OrderListContentModel orderContentModel = null;
    OrderListFooterModel orderFooterModel = null;

    //遍歷每一張訂單
    for (OrderListModel.RecordBean recordBean : resultList) {

        //設置item頭部訂單狀態
        orderHeaderModel = new OrderListHeaderModel();
        //TODO 設置數據
        dataList.add(orderHeaderModel);

        //設置item訂單商品列表
        for (ProductListModel productListBean : recordBean.getProductList()) {
            orderContentModel = new OrderListContentModel();
            //TODO 設置數據
            dataList.add(orderContentModel);
        }

        //設置item訂單尾部信息
        orderFooterModel = new OrderListFooterModel();
        //TODO 設置數據
        dataList.add(orderFooterModel);
    }
    // 返回封裝好的單鏈表
    return dataList;
}
複製代碼

2.3 Adapter中適配

目前使用的是原生未封裝的Adapter,更簡潔的使用方法能夠了解一下BaseRecyclerViewAdapterHelper,使用起來更方便喲,在商品列表須要多類型Item的話也能夠參考他的Demo,寫的很完美。函數

在Adapter中,咱們首先要複寫getItemCount方法:spa

@Override
public int getItemCount() {
    return data != null ? data.size() : 0 ;
}
複製代碼

其次複寫getItemViewType方法code

@Override
public int getItemViewType(int position) {
    if (data.get(position) instanceof OrderListHeaderModel) {// 判斷數據類型是否爲頭部Item
        return ITEM_HEADER;
    } else if (data.get(position) instanceof OrderListContentModel) {// 判斷數據類型是否爲內容Item
        return ITEM_CONTENT;
    } else if (data.get(position) instanceof OrderListFooterModel) {// 判斷數據類型是否爲尾部Item
        return ITEM_FOOTER;
    }
    return ITEM_CONTENT;// 默認返回內容Item
}
複製代碼

複寫onCreateViewHolder方法,將view與viewType對應

@NonNull
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view;
    if (viewType == ITEM_HEADER) {// 返回訂單頭部view
        view = LayoutInflater.from(mContext).inflate(R.layout.mallmodule_item_order_header, parent, false);
        return new OrderViewHolderHeader(view);
    } else if (viewType == ITEM_CONTENT) {// 返回訂單商品列表view
        view = LayoutInflater.from(mContext).inflate(R.layout.mallmodule_item_order_goods, parent, false);
        return new OrderViewHolderContent(view);
    } else if (viewType == ITEM_FOOTER) {// 返回訂單尾部view
        view = LayoutInflater.from(mContext).inflate(R.layout.mallmodule_item_order_footer, parent, false);
        return new OrderViewHolderFooter(view);
    }
    // 默認爲訂單商品列表view
    return new OrderViewHolderContent(LayoutInflater.from(mContext).inflate(R.layout.mallmodule_item_order_goods, parent, false));
}
複製代碼

複寫onBindViewHolder方法,設置Model數據到View

@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
    if (holder instanceof OrderViewHolderHeader) {// 訂單頭部View數據設置
        final OrderListHeaderModel orderHeaderModel = (OrderListHeaderModel) data.get(position);
        final OrderViewHolderHeader viewHolderHeader = (OrderViewHolderHeader) holder;
        // TODO 設置Model數據到View
    } else if (holder instanceof OrderViewHolderContent) {// 訂單商品View數據設置
        final OrderListContentModel orderContentModel = (OrderListContentModel) data.get(position);
        final OrderViewHolderContent viewHolderContent = (OrderViewHolderContent) holder;
        // TODO 設置Model數據到View
        }
    } else if (holder instanceof OrderViewHolderFooter) {// 訂單尾部View數據設置
        final OrderListFooterModel orderFooterModel = (OrderListFooterModel) data.get(position);
        final OrderViewHolderFooter viewHolderFooter = (OrderViewHolderFooter) holder;
        // TODO 設置Model數據到View
    }
}
複製代碼

3. Demo地址

週末補 (無限火力連跪的話)

相關文章
相關標籤/搜索