天天看點

RecycleView簡單使用簡介基本使用流程複雜布局層數簡化

  • 簡介
  • 基本使用流程
    • 1.導包
    • 2.設定布局管理器
    • 3.設定Adapter
    • 4. 添加分割線
    • 5. 設定動畫
    • 6.點選事件
    • 7.HeadView + FootView
    • 7.去掉滑動收尾的下拉動畫
  • 複雜布局層數簡化
    • 1. 布局要求
    • 2. 布局解析
    • 3. 布局實作
    • 4. 最終效果展示

簡介

A flexible view for providing a limited window into a large data set.

一個展示大量資料、靈活的View控件。

個人了解為ListView、GridView的官方更新版本,将ViewHolder、複用itemView等ListView優化方案吸收,更加靈活、解耦的展示大量資料。

2017.11.20 增加推薦點選事件處理、HeadView+FootView的封裝.
           

基本使用流程

1.導包

使用RecycleView需要引入v7包對應。

// 版本号跟v7包版本号一緻就行
compile 'com.android.support:recyclerview-v7:xx.x.x'
           

2.設定布局管理器

RecycleView.setLayoutManager(LayoutManager);

自帶的三個布局管理器:

StaggeredGridLayoutManager : 流式布局管理器

A LayoutManager that lays out children in a staggered grid formation.

It supports horizontal & vertical layout as well as an ability to layout children in reverse..

以交錯網格的形式布局,支援水準和垂直布局,同時支援反向。

LinearLayoutManager:線性布局管理器

similar functionality to {@link android.widget.ListView}

官方解釋:功能和ListView類似。。。

GridLayoutManager:表格布局管理器

By default, each item occupies 1 span. You can change it by providing a custom

{@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.

預設每個Item占據一個跨度(span:表格的列數既定之後,每個item的寬度),但是你可以setSpanSizeLookup()方法修改每個item的跨度。

功能類似于GridView,繼承了LinearLayoutManager,但是官方的解釋上面是重點,通過上面的功能能夠實作更加靈活的表格布局,将往常隻能嵌套使用的布局在不嵌套的情況展示。

3.設定Adapter

RecycleView.setAdapter();

adapter必須重寫方法如下:

new RecyclerView.Adapter() {

    /**
     * 根據viewType傳回想要的ViewHolder,強行綁定ListView中ViewHolder模式。
     *
     * 每個Item都會調用僅且調用一次,即每個Item的ViewHolder對象都不被其他Item(即使類型相同)複用。
     * 這裡與ListView使用ViewHolder有所差別。
     *
     * 當Item需要被複用,調用方法{@link #onBindViewHolder(RecyclerView.ViewHolder, int)}
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
        RecyclerViewAdapter1.ViewHolder viewHolder = new RecyclerViewAdapter1.ViewHolder(view);
        return viewHolder;
    }

    /**
     * 根據加載位置進行資料展示
     * 當Item被展示到螢幕上時調用,進行Item展示、更新
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    }

    /**
     * 傳回總的Item數量
     */
    @Override
    public int getItemCount() {
        return ;
    }
});
           

4. 添加分割線

RecycleView.addItemDecoration(RecyclerView.ItemDecoration);

以前添加分割線的方法不再有效。

android:divider="#ff0000"
android:dividerHeight="1dp"
           

自定義分割線

new RecyclerView.ItemDecoration() {
    /**
     * 調用在繪制Item之前
     * 一般情況兩個Draw方法選一個重寫即可
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    }

    /**
     * 調用在繪制Item之後
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
    }

    /**
     * 設定偏移量
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        // 對Item進行偏移
        outRect.set(,,,);
    }
});
           

5. 設定動畫

RecyclerView.setItemAnimator(new DefaultItemAnimator());

這裡使用預設的系統動畫。

同時調用如下方法進行Item的add和remove:

public void addItem(int position, String s){
    arrayList.add(position,s);
    notifyItemInserted(position);
}

public void remove(int position){
    arrayList.remove(position);
    notifyItemRemoved(position);
}
           

6.點選事件

方式一:由于RecycleView沒有直接給出點選事件的接口,可以将事件監聽接口放到Adapter中,在布局容器進行實作,容器中調用就好了,這裡不做實作。

方式二(推薦使用):RxJava PublishSubject實作監聽事件的傳遞。

沒有使用RxJava不能使用!!!

adapter實作:

private final PublishSubject<Model> onClickSubject = PublishSubject.create();

/**
* 點選事件 進行訂閱
*
* @author fengzhen
* @version v6.3.0, 2017/11/18 17:43
*/
public PublishSubject<Model> getOnClickSubject() {
   return onClickSubject;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    Model model = mListData.get(position);
    ViewHolder viewHolder = (ViewHolder) holder;
    viewHolder.mTvContent.setOnClickListener(v -> onClickSubject.onNext(model));
}
           

外部訂閱:

mXXXAdapter.getOnClickSubject().subscribe(new DefaultSubscriber<Model>() {
            @Override
            public void onNext(Modeldata) {
                // 點選事件處理
                showShortToast(data.getString());
            }
        });
           

好,就醬!

7.HeadView + FootView

從ListView到RecyclerView怎麼能把很常用的HeadView、FootView給忘了。

這裡直接貼封裝的一個簡單Adapter,使用直接繼承My Adapter 就可以直接用了。

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * 擴充RecyclerView.Adapter
 * 添加HeadView、FootView
 *
 * @author fengzhen
 * @version v6.3.0, 2017/11/20
 */
public abstract class RecyclerViewExtendAdapter extends RecyclerView.Adapter {
    public static final int ITEM_TYPE_HEADVIEW = ;
    public static final int ITEM_TYPE_FOOTVIEW = ;

    private View mHeadview;
    private View mFootview;

    private boolean hasHeadView = false;

    public void setHeadview(View headview) {
        hasHeadView = headview != null;
        this.mHeadview = headview;
        notifyDataSetChanged();
    }

    public void setFootview(View footview) {
        this.mFootview = footview;
        notifyDataSetChanged();
    }

    public View getmHeadview() {
        return mHeadview;
    }

    public View getmFootview() {
        return mFootview;
    }

    /**
     * 不可重寫,
     * 重寫方法{@link RecyclerViewExtendAdapter#getNormalItemViewType}實作具體子類操作
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 15:57
     */
    @Override
    public final int getItemViewType(int position) {
        if (mHeadview != null && position < ) {
            return ITEM_TYPE_HEADVIEW;
        } else if (mFootview != null && position == getItemCount() - ) {
            return ITEM_TYPE_FOOTVIEW;
        } else {
            return getNormalItemViewType(hasHeadView ? position -  : position);
        }
    }

    /**
     * 子類實作此方法,等同于直接實作{@link RecyclerView.Adapter#getItemViewType(int)} ()}
     *
     * @return The view type of this ViewHolder.
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 15:52
     */
    protected int getNormalItemViewType(int position) {
        return super.getItemViewType(position);
    }

    /**
     * 不可重寫
     * 子類實作{@link RecyclerViewExtendAdapter#getNormalItemCount()}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final int getItemCount() {
        return getNormalItemCount() + (mHeadview == null ?  : ) + (mFootview == null ?  : );
    }

    /**
     * 子類實作此方法,等同于實作{@link RecyclerView.Adapter#getItemCount()}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract int getNormalItemCount();

    /**
     * 不可重寫
     * 子類實作{@link RecyclerViewExtendAdapter#onNormalCreateViewHolder(ViewGroup, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case ITEM_TYPE_HEADVIEW:
                return new HeadViewHolder(mHeadview);
            case ITEM_TYPE_FOOTVIEW:
                return new FootViewHolder(mFootview);
            default:
                return onNormalCreateViewHolder(parent, viewType);
        }
    }

    /**
     * 子類實作此方法,等同于實作{@link RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract RecyclerView.ViewHolder onNormalCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 不可重寫
     * 子類實作{@link RecyclerViewExtendAdapter#onNormalBindViewHolder(RecyclerView.ViewHolder, int, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:10
     */
    @Override
    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int itemViewType = getItemViewType(position);
        switch (itemViewType) {
            case ITEM_TYPE_HEADVIEW:
            case ITEM_TYPE_FOOTVIEW:
                break;
            default:
                onNormalBindViewHolder(holder, hasHeadView ? position -  : position, itemViewType);
                break;
        }
    }

    /**
     * 子類實作此方法,等同于實作{@link RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
     *
     * @author fengzhen
     * @version v6.3.0, 2017/11/20 16:06
     */
    protected abstract void onNormalBindViewHolder(RecyclerView.ViewHolder holder, int position, int itemViewType);

    private class HeadViewHolder extends RecyclerView.ViewHolder {
        public HeadViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class FootViewHolder extends RecyclerView.ViewHolder {
        public FootViewHolder(View view) {
            super(view);
        }
    }
}
           

7.去掉滑動收尾的下拉動畫

設定屬性:
android:overScrollMode="never"
           

複雜布局層數簡化

1. 布局要求

實作如下的布局:

RecycleView簡單使用簡介基本使用流程複雜布局層數簡化

2. 布局解析

實作方式一: 直接硬布局,最外層用一個ScrollView,裡面用 TextView + ListView + TextView + GridView。還需要對ListView和GridView進行重寫測量方法,才能實作效果。

實作方式二: 整個作為一個ListView,Title、ListView 、GridView都看成是父ListView 的一個Item。但是對于其中的互動邏輯的代碼不要控制,寫出來肯定很不“優美”。

實作方式三: 使用RecycleView的GridLayoutManager布局,通過item type 設定,将Title、ListVite item和GridView item都作為RecycleView 的item,使用一層布局,這也是推薦的做法。

3. 布局實作

實作布局的重點在于GridLayoutManager的setSpanSizeLookup()方法。

設定布局管理器:

// 設定布局管理器
private void initLayout() {
    // 這裡的4,取所需列數的最小公倍數
    mRecyclerView1.setLayoutManager(new GridLayoutManager(this, ));
}
           

Adapter實作:

/**
 * Created by fengzhen.
 * 
 * recyclerView 擴充卡
 */
public class RecyclerViewAdapter1 extends RecyclerView.Adapter<RecyclerViewAdapter1.ViewHolder> {
    // 布局類型
    public static final int TITLE_1 = ;
    public static final int TITLE_2 = ;
    public static final int LINEAR_1 = ;
    public static final int GRID_2 = ;

    // 資料集
    private List<String> arrayList;

    public RecyclerViewAdapter1(List<String> list) {
        arrayList = list;
    }

    /**
     * 查找item布局,強制綁定ViewHolder
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 沒做類型判定 需要時判斷 viewType
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false);
        return new ViewHolder(view);
    }

    /**
     * 根據加載位置進行資料展示
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 沒做類型判定 需要時判斷 viewType
        holder.textView1.setText(arrayList.get(position));
    }

    /**
     * 實作布局的重點
     */
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        /**
         * 處理GridLayoutManager布局
         * LayoutManager列數設定為 所有可能出現的列數的最小公倍數
         * 具體的列數,傳回在總列數的比例
         *  例:可能出現的列數有:  1、3、4、7
         *      LayoutManager的列數設定為 : 1*3*4*7 = 84;
         *      對應列數的傳回值: 84、 28、21、12;
         */
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if(manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    int type = getItemViewType(position);
                    switch (type){
                        case TITLE_1:
                        case TITLE_2:
                            return ;
                        case LINEAR_1:
                            return ;
                        case GRID_2:
                            return ;
                        default:
                            return ;
                    }
                }
            });
        }
    }

    /**
     * 傳回不同的類型
     *
     * @author fengzhen
     * @version v1.0, 2017/8/23 15:47
     */
    @Override
    public int getItemViewType(int position) {
        if (position == ) {
            return TITLE_1;
        } else if (position < ) {
            return LINEAR_1;
        } else if (position == ) {
            return TITLE_2;
        } else {
            return GRID_2;
        }
    }

    /**
     * 類似listview中的方法,擷取總item數
     */
    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    /**
     * 繼承RecycleView的ViewHolder,對item控件進行初始化
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView textView1;

        public ViewHolder(View itemView) {
            super(itemView);
            textView1 = (TextView) itemView.findViewById(R.id.textView1);
        }
    }
}
           

4. 最終效果展示

RecycleView簡單使用簡介基本使用流程複雜布局層數簡化