1. 程式人生 > >讓偷懶更徹底——用Butterknife 來為recyclerview 打造通用介面卡(上)

讓偷懶更徹底——用Butterknife 來為recyclerview 打造通用介面卡(上)

背景

隨著recyclerview 的越來越普及,其高度的易用性,讓我們越來越愛不釋手,當然網上也出現了很多類似的通用介面卡,讓我們更加方便的使用它,今天我們這裡介紹一種新的recyclerview的通用介面卡的實現思路——把recyclerview和ButterKnife結合起來使用(ps:因為公司開發一直使用butterknife,才有了這種想法)。

首先貼上我的實現效果:

這裡寫圖片描述

程式碼用法使用:

ModelRecyclerAdapter adapter = new ModelRecyclerAdapter(MyImageViewHolder.class, datas);
        recyclerView.setAdapter(adapter);

其中datas就是我們的資料,當然為了通用是泛型的,這邊是傳入的一個String的list,最關鍵的是我的MyViewHolder.class類了,他就是我們的核心點了,在這個類裡面封裝了我們所有資料展示和點選事件。
首先是item佈局程式碼,我這邊為了簡單,就用了一個imageview,
在其中我用Picasso去載入了一張圖片,然後給每個位置設定了點選效果展示當前position。
R.layout.item_list:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp" android:layout_height="120dp" android:background="@android:color/black" android:paddingLeft="8dp" android:paddingTop="8dp">
<ImageView android:id="@+id/iv_item1" android:layout_width="match_parent" android:layout_height
="match_parent" android:background="@android:color/white" android:padding="12dp" android:scaleType="centerCrop" />
</LinearLayout>

我們繫結事件的viewholder類:

 /**
     * 我們的實際使用中的viewhoder
     * 用註釋item的佈局檔案,在這個類的實現中,我們可以繫結點選事件,更新資料
     */
    @RecyclerItemViewId(R.layout.item_list)
    public static class MyViewHolder extends ModelRecyclerAdapter.ModelViewHolder<String> {
        @BindView(R.id.iv_item1)
        ImageView imageView;

        /**
         * 可以對itemview的任何一個view繫結監聽,這裡只是以onclick為例,當然也可以繫結onTouch,onLongClick等
         */
        @OnClick(R.id.iv_item1)
        void onclick() {
            Toast.makeText(imageView.getContext(), position + " 點選~", Toast.LENGTH_SHORT).show();
        }

        public int position;


        public MyViewHolder(View itemView) {
            super(itemView);
        }

        /**
         * 繫結我們的資料
         *
         * @param item    這是資料
         * @param adapter adapter 物件
         * @param context context物件
         * @param positon 當前位置
         */
        @Override
        public void convert(String item, ModelRecyclerAdapter adapter, Context context, int positon) {
            this.position = positon;
            Picasso.with(context).load(item).into(imageView);
        }
    }

程式碼很清晰有木有!?我們只需要在這個類裡面完成佈局的繫結,資料的繫結,監聽的繫結,然後設定給recyclerview的時候只需2行程式碼即可!教練,這波我要偷懶!

設計思路分析:

我們在為recyclerview定製adpter的時候,必須繼承自Recyclerview.Adapter<>而這個泛型中需要傳入Recyclerview.viewholder的實現類,那麼這個viewholder類是什麼呢?其實這個類在構造的時候,需要傳入一個itemview,這個view就相當於我們使用listview和gridview中的convertview,而在listview中,我們複用的單位是convertview,而在recyclerview中,我們複用的單位是這個viewholder,itemview就是其中一個成員變數,下面我們首先用原始的方式寫出我們demo裡面那個圖片例項展示:

public class MyHolder extends RecyclerView.ViewHolder {

        /**
         * 構造方法,傳入view即我們listview的convertview
         *
         * @param itemView
         */
        public MyHolder(View itemView) {
            super(itemView);
        }

    }

    public class MyAdapter extends RecyclerView.Adapter<MyHolder> {
        List<String> datas = new ArrayList<>();

        public MyAdapter(List<String> data) {
            this.datas = data;
        }

        @Override
        public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
            MyHolder holder = new MyHolder(itemView);//完成對holder的建立,中間互用邏輯RecyclerView幫我們完成了
            return holder;
        }

        @Override
        public void onBindViewHolder(MyHolder holder, final int position) {
            //這裡完成資料的繫結與事件的監聽
            ImageView imageView = (ImageView) holder.itemView.findViewById(R.id.iv_item1);
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getContext(), "點選 " + position, Toast.LENGTH_SHORT).show();
                }
            });
            Picasso.with(holder.itemView.getContext()).load(datas.get(position)).into(imageView);
        }

        @Override
        public int getItemCount() {
            return datas.size();
        }
    }

由此可見,viewholder類才是我們的核心出發點,我只要在這個類裡面完成我們的資料繫結,監聽,即可,所以我們現在這麼寫:

ModelAdapter.ModelviewHolder:

 /**
     * hodler抽象類,支援任何資料型別
     *
     * @param <T>
     */
    public static abstract class ModelViewHolder<T> extends RecyclerView.ViewHolder {

        public ModelViewHolder(View itemView) {
            super(itemView);
        }

        /**
         * 這個是我們真正在實際使用的類中的繫結資料的方法
         *
         * @param item    bean型別
         * @param adapter adpter物件
         * @param context context物件
         * @param positon 位置
         */
        public abstract void convert(T item, ModelRecyclerAdapter adapter, Context context, int positon);
    }

下面是我們的modeladapter類的核心程式碼:

/**
 * RecyclerView 通用介面卡第一版
 * Created by cd5160866 on 16/5/10.
 */
public class ModelRecyclerAdapter<T> extends RecyclerView.Adapter<ModelRecyclerAdapter.ModelViewHolder> {
    protected Context mContext;
    /**
     * 通過註釋的方式加入的佈局item的layoutId
     */
    protected int mLayoutId;
    /**
     * viewholder的實現類類名
     */
    private Class<? extends ModelViewHolder> viewHolderClass;
    /**
     * 資料 即我們的任何型別的bean
     */
    protected List<T> mDatas = new ArrayList<>();

    public ModelRecyclerAdapter(Class<ModelViewHolder> viewHolderClass, List<T> Datas) {
        this.viewHolderClass = viewHolderClass;
        this.mLayoutId = viewHolderClass.getAnnotation(RecyclerItemViewId.class)//獲取我們的layoutid,我們的類註釋後面的部分
                .value();
        mDatas = Datas;
    }

    @Override
    public ModelViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        ModelViewHolder viewHolder = null;
        if (mContext == null)
            mContext = parent.getContext();

        try {
            View converView = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false);
            viewHolder = viewHolderClass.getConstructor(View.class).newInstance(converView);
            ButterKnife.bind(viewHolder, converView);//將viewhodler於我們的view繫結起來
        } catch (Exception e) {
            e.printStackTrace();
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ModelViewHolder holder, int position) {
        holder.convert(mDatas.get(position), this, mContext, position);//這裡更新資料
    }
    @Override
    public int getItemCount() {
        return mDatas.size();
    }
}

首先我們看這個ModelAdapter的第20行,也就是我們的構造的方法,這個方法接收兩個引數,其中第二個引數不用多說,當然是我們的資料,第一個引數也就是我們的ModelViewhodler的實現類,adpter會根據這類來建立viewholder,再看第22行,這一行就是我們獲取我們的itemId的核心程式碼,這個佈局id不是通過構造方法傳進來的,那是怎麼獲得的呢?沒錯!就是java註解(Annotation),至於為何用註解,分析完成後我們就知道了,下面我們新建一個Annotation:

@Retention(RUNTIME)
public @interface RecyclerItemViewId {
    int value();//我們註解後面使用id值
}

關於註解的用法,可以參考:深入理解java註解
所以在最文章最開頭我們自己實現的那個ModelViewHolder的實現類上面就是這麼寫:

 @RecyclerItemViewId(R.layout.item_list)
    public static class MyImageViewHolder extends ModelRecyclerAdapter.ModelViewHolder<String> {
         //.....
    }

然後我們再回過頭來看ModelRecyclerAdapter的第22行,我們也就明白了是啥意思了,

 this.mLayoutId = viewHolderClass.getAnnotation(RecyclerItemViewId.class).value();

我們通過傳入的我們自己的實現類名去獲取我們自定義的註解,再去獲取註解的值,也就是我們的layoutId,拿到id以後,當然就是我們在onCreateViewHolder方法中去建立我們的itemview,也就是第34行,再往下看第35行,在這一行我們當然的思路就是要用itemview去建立我們的viewholder例項,因為我們自己的Modelviewhodler是個抽象類,無法直接new,我們當然只能通過它的子類來建立例項,即我們只能通過我們傳進來的實現類的類名去獲取他的構造方法,當然viewholder本身構造需要view作為引數,所以這裡我們需要獲取它帶View.class的構造方法,所以綜合起來,程式碼就是這麼寫:

View converView = LayoutInflater.from(context).inflate(mLayoutId, parent, false);
            viewHolder = viewHolderClass.getConstructor(View.class).newInstance(converView);

說到最後,當然是我們的butterKnife了,只需一行程式碼,把我們的viewholder和convertview繫結起來:

ButterKnife.bind(viewHolder, converView);//將viewhodler於我們的view繫結起來

至此,我們就能向一開始一樣,在viewholder這個類裡面對itemview中的任何一個view進行點選,長按,觸控等各種監聽,demo為了簡單直觀,只寫了一個onclick事件:

   /**
         * 可以對itemview的任何一個view繫結監聽,這裡只是以onclick為例,當然也可以繫結onTouch,onLongClick等
         */
        @OnClick(R.id.iv_item1)
        void onclick() {
            Toast.makeText(imageView.getContext(), position + " 點選~", Toast.LENGTH_SHORT).show();
        }

再回過頭來看我們的adapter的程式碼,去看看45行,也就是onBindViewHolder方法,當然,在這裡我們的完成資料的繫結與更新:

@Override
    public void onBindViewHolder(ModelViewHolder holder, int position) {
        holder.convert(mDatas.get(position), this, mContext, position);//這裡更新資料
    }

這裡呼叫了我們的viewholder這個抽象方法,我們因此在我們的自己實現的viewholder類裡面去實現這個方法完成對我們資料的繫結,比如,我的demo裡面依舊是用Picasso去載入一直圖片~至此,我們的ModelRecyclerAdapter核心邏輯完成,此外我們再給他封裝一些方法,方便他新增資料和刪除資料~所以完整程式碼如下:

最終完成! ModelRecyclerAdapter:

/**
 * RecyclerView 通用介面卡第一版
 * Created by cd5160866 on 16/5/10.
 */
public class ModelRecyclerAdapter<T> extends RecyclerView.Adapter<ModelRecyclerAdapter.ModelViewHolder> {
    protected Context mContext;
    /**
     * 通過註釋的方式加入的佈局item的layoutId
     */
    protected int mLayoutId;
    /**
     * viewholder的實現類類名
     */
    private Class<? extends ModelViewHolder> viewHolderClass;
    /**
     * 資料 即我們的任何型別的bean
     */
    protected List<T> mDatas = new ArrayList<>();

    public ModelRecyclerAdapter(Class<ModelViewHolder> viewHolderClass) {
        this.viewHolderClass = viewHolderClass;
        this.mLayoutId = viewHolderClass.getAnnotation(RecyclerItemViewId.class)
                .value();
    }

    public ModelRecyclerAdapter(Class<ModelViewHolder> viewHolderClass, List<T> Datas) {
        this.viewHolderClass = viewHolderClass;
        this.mLayoutId = viewHolderClass.getAnnotation(RecyclerItemViewId.class)//獲取我們的layoutid,我們的類註釋後面的部分
                .value();
        mDatas = Datas;
    }

    @Override
    public ModelViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        ModelViewHolder viewHolder = null;
        if (mContext == null)
            mContext = parent.getContext();

        try {
            View converView = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false);
            viewHolder = viewHolderClass.getConstructor(View.class).newInstance(converView);
            ButterKnife.bind(viewHolder, converView);//將viewhodler於我們的view繫結起來
        } catch (Exception e) {
            e.printStackTrace();
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ModelViewHolder holder, int position) {
        holder.convert(mDatas.get(position), this, mContext, position);//這裡更新資料
    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    public void add(int positon, T data) {
        mDatas.add(positon, data);
        notifyItemInserted(positon);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                notifyDataSetChanged();
            }
        }, 500);

    }

    public void add(T data) {
        mDatas.add(data);
        notifyDataSetChanged();

    }

    public void add(List<T> data) {
        mDatas.clear();
        mDatas.addAll(data);
        notifyDataSetChanged();
    }

    public void remove(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                notifyDataSetChanged();
            }
        }, 500);
    }

    public void replace(int position, T data) {
        mDatas.remove(position);
        mDatas.add(position == 0 ? position : position - 1, data);
        notifyDataSetChanged();
    }

    public void addAll(List<T> datas) {
        mDatas.addAll(datas);
        notifyDataSetChanged();
    }

    public void clear() {
        mDatas.clear();
        notifyDataSetChanged();
    }

    public List<T> getItems() {
        return mDatas;
    }

    /**
     * hodler抽象類,支援任何資料型別
     *
     * @param <T>
     */
    public static abstract class ModelViewHolder<T> extends RecyclerView.ViewHolder {

        public ModelViewHolder(View itemView) {
            super(itemView);
        }

        /**
         * 這個是我們真正在實際使用的類中的繫結資料的方法
         *
         * @param item    bean型別
         * @param adapter adpter物件
         * @param context context物件
         * @param positon 位置
         */
        public abstract void convert(T item, ModelRecyclerAdapter adapter, Context context, int positon);
    }
}

宣告一下的是第59行的add方法和83行的remove方法是先呼叫notifyItemRemoved(position)是為了保留插入動畫和刪除動畫效果,但是這個貌似會讓position錯位,因此再延時去調動notifyDataSetChanged()保證位置正常。

至此我們也就明白了,我們建立我們的recyclerAdapter的時候,只需要在構造方法中傳入viewholder的實現類即可,我們在這個類裡面完成了對item佈局的繫結,事件的監聽,資料的繫結,一個類解決所有問題,確實是剩下了不少事,程式碼也能精簡不少。
具體使用方法,大家有興趣的可以參考demo。

整篇文章跨度較大,包含了java註解,recyclerView,Butterknife,其是網上有很多通用介面卡的實現方式,今天這裡只是一種實現思路,至此分析完成,有問題請隨時溝通,下篇,我將繼續在此基礎上,為我們的Recyclerview簡潔的新增Header,footer和emptyview,祝賀大家寒假愉快,新年加油!↖(^ω^)↗

相關連結:

相關推薦

偷懶徹底——Butterknife recyclerview 打造通用介面卡()

背景 隨著recyclerview 的越來越普及,其高度的易用性,讓我們越來越愛不釋手,當然網上也出現了很多類似的通用介面卡,讓我們更加方便的使用它,今天我們這裡介紹一種新的recyclerview的通用介面卡的實現思路——把recyclerview和Butt

RecyclerView打造通用Adapter RecyclerView更加好

一、概述 記得好久以前針對ListView類控制元件寫過一篇打造萬能的ListView GridView 介面卡,如今RecyclerView異軍突起,其Adapter的用法也與ListView類似,那麼我們也可以一步一步的為其打造通用的Adapter

RecyclerView打造萬能介面卡,點選事件,5.0水波紋點選效果

一.前言 最近使用到RecyclerView,RecyclerView使用詳解戳這裡,由於使用過張鴻洋大神的ListView萬能Adapter,感覺RecyclerView的Adapter編寫還是太麻煩了,而且沒有點選事件,ok,參考ListView的萬能Ad

[自己動手]Editplus

提醒:本文最後更新於 2755 天前,文中所描述的資訊可能已發生改變,請謹慎使用。 用過Editplus的同學基本都會對它的輕巧與強大讚不絕口。我這樣用了3年以上的,早已是離不開它了。無論是在幾萬行文本里執行復雜正則替換;還是一次開啟幾百個文字修改編碼;或者在結構層次很複雜的目錄中批量查詢,它都能輕

python抓取“煎蛋網”面的美女圖片,尺度很大哦!哈哈

each file like http add 寫入 header 。。 num 廢話不多說,先上代碼: import urllib.request import re #獲得當前頁面的頁數page_name def get_pagenum(url): req

使用ItemDecorationRecyclerView打造帶懸停頭部的分組列表

package jiuyuhulian.com.merchantstoreApp.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; impor

【Android 仿微信通訊錄 導航分組列表-下】自定義ViewRecyclerView打造右側索引導航欄IndexBar

本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 一 概述 在上篇文章(http://blog.csdn.net/zxt0601/article/details/52355199)裡,我們用ItemDecoration為Recy

字典和列表性能哪家強,一百萬隨機字符你揭曉!

tro 哈希表 並發 快速 random模塊 符號 基礎 運行時 散列表 Python中有兩個非常常用的數據結構,列表和字典。在做數據存儲的時候,到底二者哪家強,字典還是列表,還是差不多呢。與其猜測,不如我們用數據說話! 思路: 生成一個很大的文本文件分別用列表和字典來存儲

AOP你的JS程式碼變得有可維護性吧

此文已由作者吳佳祥授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 好吧我承認這是篇任務。 最近看到個訊息,ES2017已經定稿了,心想,我去,還完全沒了解ES2016呢,ES8就定稿了,out了,這可咋辦,趕緊Google(Baidu)去! 不過從ES6(2015)之後,tc39的

聲音清晰,PR去掉視頻中的噪音

tieba name pen ... 最好 gray false author lis Premiere V7.0沒有支持當前文件類型的輸入源的問題 25 我是新手,把DVD影片放到電腦裏,顯示的是VOB文件,導入類型顯示的是所有支持格式,於是我就點擊了VOB文件,但是說

智能公交電子站牌你報告實時交通信息,出行方便

智能交通 智慧城市 智能公交站牌 電子公交站牌 近幾年,城市公交大力推進信息化、電子化建設,如構建無人售票系統,實行IC卡收費。隨著經濟的發展,人民生活水平的提高,人民對公共交通出行的要求也越來越高,乘客關註的不僅僅是能否順利出行,而是更多地關心公交車對於到站時間、車輛運行狀況等信息的實時發布

你的Ubuntu

linux 註:以下內容在Ubuntu LTS 16.04下可用。 一、刪除guest賬戶 刪除guest賬戶可以在一定程度上使自己的電腦更加安全,也可以避免登錄賬號時選錯賬戶帶來的不便。 執行命令打開配置文件vi /usr/share/lightdm/lightdm.c

什麽要兩次調encodeURI解決亂碼問題

d+ 方法 coder gbk 輸入 轉化 自動 gpo 轉換 .encodeURL函數主要是來對URI來做轉碼,它默認是采用的UTF-8的編碼.. UTF-8編碼的格式:一個漢字來三個字節構成,每一個字節會轉換成16進制的編碼,同時添加上%號. 假設頁面端輸入的中文是一個

《代碼大全》閱讀筆記-2-隱喻充分地理解軟件開發

概念 使用 -m 其他 閱讀 body 一個 根據 logs 隱喻的價值絕不應該被低估。隱喻的有點在於其預期的效果:能被所有的人理解。不必要的溝通和誤解也因此大為降低,學習與教授更為快速。實際上,隱喻是對概念進行內在化和抽象的一種途徑,它讓人在更高的層面上思考問題,從而避

cglib包類產生動態代理類對象

方法 source uil owa pac 類對象 進行 desc clas 一:在JDK裏也有動態代理的類和接口,是Proxy和InvocationHandler,但是Proxy只能為接口產生代理類,借助InvocationHandler的實現類來完成對類對象的代理;

是否買房說明決策樹算法的使用-AI機器學習

人工智能 深度學習 我們以是否買房為例子給大家介紹一下決策樹算法的使用,數據集如下(僅做演示,不代表真實情況)地段近地鐵面積單價(萬)是否購買三環是608是三環是808否三環否607是三環否807否五環是607是五環是807否五環否606是五環否806是六環是606是六環是805.5是六環否605否六

小程序購物車下架商品實時顯示,只戶體驗!

組件 刪除 提示 設置 配送 促銷 門店 開放 關閉 單商戶小程序V1.8.4版本更新說明更新時間:2018年9月10號 一、 更新功能清單1、新增僅支持自提功能設置,用戶下單僅可選到店自提!2、小程序前臺優化購物車頁面,已刪除或已下架的商品,其狀態展示在購物車提醒用戶;

聖經訓練演算法,針對不同受眾將文字轉換不同風格

為了尋找提高計算機文字翻譯能力的靈感,達特茅斯學院的研究人員求助於聖經。其結果是對不同版本的聖經文字進行訓練的演算法,可以將書面語言轉換為不同的風格,以適應不同的受眾。 在像英語和西班牙語這樣的語言之間翻譯文字的網際網路工具是廣泛可用的。建立樣式轉換工具(將文字保持在相同的語言中,但轉換樣

Adaptive Execution如何Spark SQL高效

1 背  景 Spark SQL / Catalyst 和 CBO 的優化,從查詢本身與目標資料的特點的角度儘可能保證了最終生成的執行計劃的高效性。但是 執行計劃一旦生成,便不可更改,即使執行過程中發現後續執行計劃可以進一步優化,也只能按原計劃執行; CBO 基於統計資訊生成最優

Adaptive Execution如何Spark SQL高效

文章目錄 背景 動態設定 Shuffle Partition Spark Shuffle 原理 原有 Shuffle 的問題 自動設定 Shuffle Partition 原理 使用與優化方法 動態調整執行計劃