1. 程式人生 > >RecyclerView的超強輔助Graywater——點選事件

RecyclerView的超強輔助Graywater——點選事件

關於Graywater的系列文章

上一篇寫了Graywater的基礎使用,但是沒講點選事件,這一篇文章就把點選事件給補充上。還是使用的基礎實操篇中的GraywaterPrimaryDemo繼續寫。

先展示點選效果圖:

Graywater點選事件.gif

在RecyclerView中如果需要點選事件,我們通常會給RecyclerViewd.Adapter傳入一個我們自定義的介面引用,比如OnItemClickListener。然後在onBindViewHolder()方法中將需要點選事件的控制元件,比如Button,實現View的OnClickListener介面,再使用我們自定義的方法,最後在MainActivity中實現自定義的介面。

        holder.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (clickListener != null) {
                    clickListener.onClickItem(position);
                }
            }
        });

Graywater跟前者也是比較相似的,最大的不同的是Graywater自己提供了一個點選事件的介面(GraywaterAdapter.ActionListener)和一個點選事件代理(GraywaterAdapter.ActionListenerDelegate)來管理點選事件。通過在Binder類中傳入ViewHolder所持有的介面代理,在ItemBinder中實現介面的act()方法,來完成點選事件的實現。

實際上,是GraywaterAdapter.ActionListenerDelegate代理實現了View的OnClickListener介面,然後在onClick方法中在呼叫GraywaterAdapter.ActionListener的act方法。

        @Override
        public void onClick(final View v) {
            actionListener.act(model, holder, v, binders, binderIndex, obj);
        }

接下來就一步步的講解怎麼實現點選事件,點選事件也可以根據上一篇文章中程式碼的順序來寫。

1. 給需要點選事件的控制元件新增id值

每個Item都由一個RelativeLayout作為最外層的控制元件,裡面放了一個TextView和一個ImageView,給RelativeLayout新增android:id="@+id/item_layout"屬性

2. 在對應ViewHolder類中新增對應的View並建立ActionListenerDelegate的物件

在EntertainViewHolder裡面新增RelativeLayout的控制元件。

重點!需要在ViewHolder中建立代理物件,使用holder來統一管理所有的view和點選代理物件。建立ActionListenerDelegate物件時候需要也需要傳入對應的model和viewholder的泛型引數,當然這裡就是EntertainPrimitive和EntertainViewHolder。

    private GraywaterAdapter.ActionListenerDelegate<EntertainPrimitive, EntertainViewHolder>
            mActionListenerDelegate = new GraywaterAdapter.ActionListenerDelegate<>();

別忘記在最後也要新增對應的get方法。

3.在Binder類的bind方法中setOnClickListener()

Graywater官方Demo中,要想實現點選事件,就必須要在bind方法裡讓點選代理getmActionListenerDelegate執行update()方法。看Graywater原始碼可以知道update方法實際上就是把相關引數都儲存下來,然後在act方法中把相關的引用傳過去使用。

在update方法後,需要點選事件的View就可以在setOnClickListener()中傳入點選代理了,最後記得要在unbind()方法中給setOnClickListener()傳入null值做清理。

    @Override
    public void bind(@NonNull EntertainPrimitive model, @NonNull EntertainViewHolder holder, @NonNull List<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends EntertainViewHolder>>
            binders, int binderIndex, @NonNull GraywaterAdapter.ActionListener<EntertainPrimitive, EntertainViewHolder> actionListener) {
        Picasso.get().load(model.getUrls().get(binderIndex)).placeholder(R.mipmap.ic_launcher).into(holder.getImg());
        holder.getTitle().setText(model.getTitles().get(binderIndex));

        holder.getmActionListenerDelegate().update(actionListener, model, holder, binders, binderIndex, null);
        holder.getMainLayoutView().setOnClickListener(holder.getmActionListenerDelegate());
    }

    @Override
    public void unbind(@NonNull EntertainViewHolder holder) {
        holder.getMainLayoutView().setOnClickListener(null);
    }

update()方法所需要傳入的引數基本上就是bind方法中的引數,需要注意的是最後一個Object引數,這個Object引數是用來傳遞我們想要傳遞的值的,類似Intent傳值的作用一樣。一般都用不到,可以直接傳null。

holder.getmActionListenerDelegate().update(actionListener, model, holder, binders, binderIndex, null);

4.ItemBinder類實現GraywaterAdapter.ActionListener介面,並實現act()方法

EntertainItemBinder需要實現GraywaterAdapter.ActionListener介面,然後就可以在act方法中具體實現我們的點選事件了。

在GraywaterAdapter.ActionListener介面中需要傳入model的泛型引數和viewholder的泛型引數,在EntertainItemBinder中,就傳入對應的EntertainPrimitive和EntertainViewHolder。

看一下act方法的實現,其實就是類似RecyclerView.Adapter中的實現:

    @Override
    public void act(@NonNull EntertainPrimitive model, @NonNull EntertainViewHolder holder, @NonNull View v, @NonNull List<GraywaterAdapter.Binder<? super EntertainPrimitive, ? extends
            EntertainViewHolder>> binders, int binderIndex, @Nullable Object obj) {
        switch (v.getId()) {
            case R.id.item_layout:
                onItemClickListener.onClickItem(model.getTitles().get(binderIndex));
                break;
        }
    }

5.寫介面傳入,MainActivity實現介面

一般在寫點選事件時,我們一般會習慣自定義一個點選事件介面,這裡也不例外,我自定義了一個OnItemClickListener介面。讓MainActivity實現了這個介面,並將介面傳入PrimitiveAdapter中。

public interface OnItemClickListener {
    void onClickItem(String name);
}

MainActivity實現了onClickItem方法,就可以使用傳過來的name引數做相應的業務處理了。

看一下OnItemClickListener引用的傳遞順序。

首先是MainActivity

mPrimitiveAdapter = new PrimitiveAdapter(this);    //因為MainActivity實現了OnItemClickListener介面,所以傳入this
mRecyclerView.setAdapter(mPrimitiveAdapter);

再是PrimitiveAdapter的構造方法,將OnItemClickListener傳給EntertainItemBinder。

    public PrimitiveAdapter(OnItemClickListener listener) {
        register(new EntertainViewHolderCreator(), EntertainViewHolder.class);  //將creator和對應的viewholder繫結

        EntertainBinder entertainBinder = new EntertainBinder();
        EntertainItemBinder entertainItemBinder = new EntertainItemBinder(entertainBinder, listener);
        register(EntertainPrimitive.class, entertainItemBinder, entertainItemBinder);  //將itemBinder和指定的資料型別繫結
    }

注意構造方法中的第二個register()方法的第三個引數也是entertainItemBinder,從原始碼可知,要求傳入的引數是GraywaterAdapter.ActionListener,也就是在ItemBinder實現該介面後就傳入ItemBinder,否則就傳入null。

    protected void register(@NonNull final MT modelType,
                            @NonNull final ItemBinder<? extends T, ? extends VH> parts,
                            @Nullable final ActionListener<? extends T, ? extends VH> listener) {
        mItemBinderMap.put(modelType, parts);
        mActionListenerMap.put(modelType, listener);
    }

Graywater有5個基本變數,mItemBinderMap和mActionListenerMap就是其中兩個,這兩個變數都是Map型別。Graywater中5個基本變數除開mItems不是字典型別,其他4個都是這種字典型別。這是Graywater的核心,以字典的方式,將model對映到viewholder上。register就是在匹配對映關係,將viewholder和viewholdercreator對映起來。將model對映到ItemBinder上,將model對映到Actionlistener上,由此來組成第一篇文章中所展示的對映網狀關係,並通過對映關係,來快速查詢所需要的類。

原理圖1.png

到這裡,基本上點選事件就講完了,有什麼錯誤或說的不清楚的地方歡迎大家指正。

如果對你有幫助的話,點贊、評論、讚賞都是對我的鼓勵,也是支援我寫下去的動力,謝謝!