1. 程式人生 > >【轉載】最好用的通用adapter——BaseQuickAdpater詳解

【轉載】最好用的通用adapter——BaseQuickAdpater詳解

序言

recyclerview是Android 5.0推出的,是support-v7包中的新元件,它的出現就是為了代替ListView、GridView。
recyclerview是一個很強大的,高度解耦的控制元件。然而我們使用時候卻很麻煩,基礎的adapter要繼承大量的方法進行實現,這給我們使用 帶來了大量的困擾,我們需要寫大量重複的程式碼,viewholer等等,我們要自定義點選事件,item的子控制元件的點選事件等等。

作為一個愛copy,愛逛gayhub的程式設計師,很早就發現了這個強大的庫,文件介紹的也比較詳細,但是總感覺看著不太舒服,就自己總結下,轉載一下文件(原文文件)。

框架引入

先在 build.gradle(Project:XXXX) 的 repositories 新增:

   allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }

然後在 build.gradle(Module:app) 的 dependencies 新增:

   dependencies {
               implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.31'
}

簡單的使用

public class Adapter1  extends BaseQuickAdapter<Student,BaseViewHolder>{

    Context mcontext;

    public Adapter1(Context context,int layoutResId, @Nullable List<Student> data) {
        super(layoutResId, data);
        mcontext=context;
    }

    @Override
    protected
void convert(BaseViewHolder helper, Student item) { helper.setText(R.id.student_name,item.getName()) .setText(R.id.student_age,item.getAge()+"") .setText(R.id.student_address,item.getAddress()); Glide.with(mcontext).load(item.getIcon()).into((ImageView) helper.getView(R.id.student_icon)); } }

從上文中的例項程式碼我們可以看出以下幾點:

1、使用: 首先需要繼承BaseQuickAdapter,然後BaseQuickAdapter 《Bean,BaseViewHolder》 第一個泛型Bean是資料實體型別,第二個BaseViewHolder是ViewHolder其目的是為了支援擴充套件ViewHolder。

2、賦值:可以直接使用viewHolder物件點相關方法通過傳入viewId和資料進行,方法支援鏈式呼叫。如果是載入網路圖片或自定義view可以通過viewHolder.getView(viewId)獲取該控制元件。

新增Item事件

item的點選事件和長按事件已經封裝到adapter裡面,只需要在使用的時候用就可以了。

   adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                Toast.makeText(CommonAdapterActivity.this, "點選了item", Toast.LENGTH_SHORT).show();
            }
        });

Item的長按事件

adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
                  Toast.makeText(CommonAdapterActivity.this, "長按了item", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

注意:巢狀recycleView的情況下需要使用你使用 adapter. setOnItemClickListener 來設定點選事件,如果使用recycleView.addOnItemTouchListener會累計新增的。

新增Item子控制元件點選事件

首先在adapter的convert方法裡面通過viewHolder.addOnClickListener繫結一下的控制元件id,

    @Override
    protected void convert(BaseViewHolder helper, Student item) {

        helper.setText(R.id.student_name,item.getName())
                .setText(R.id.student_age,item.getAge()+"")
                .setText(R.id.student_address,item.getAddress())
                .addOnClickListener(R.id.student_icon)
                .addOnClickListener(R.id.student_name)
                .addOnLongClickListener(R.id.student_address);

        Glide.with(mcontext).load(item.getIcon()).into((ImageView) helper.getView(R.id.student_icon));
    }

然後在設定子控制元件點選事件

  adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {

                switch (view.getId()) {

                    case R.id.student_icon:
                        //獲取其他控制元件,如下,獲取student_name
                        TextView textView=(TextView) adapter.getViewByPosition(recycleview, position, R.id.student_name);
                        Toast.makeText(CommonAdapterActivity.this, "點選"+textView.getText().toString()+"的頭像", Toast.LENGTH_SHORT).show();
                        break;

                    case R.id.student_name:
                        Toast.makeText(CommonAdapterActivity.this, "點選了名字", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        });

然後在設定子控制元件長按事件

  adapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() {
            @Override
            public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) {

                if (view.getId() == R.id.student_address)
                    Toast.makeText(CommonAdapterActivity.this, "長按了address", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

注意:設定子控制元件的事件,如果不在adapter中繫結,點選事件無法生效,因為無法找到你需要設定的控制元件。
如果需要獲取item中其他控制元件,上述程式碼有表現, adapter.getViewByPosition()方法進行初始化

TextView textView=(TextView) adapter.getViewByPosition(recycleview, position, R.id.student_name);

ps:如果有header的話需要處理一下position加上 headerlayoutcount。即如果header有的話,postion的點選postion實際上為postion+headerlayoutcount.

動畫

開啟動畫(預設為漸顯效果)

adapter.openLoadAnimation();

預設提供5種方法(漸顯、縮放、從下到上,從左到右、從右到左)

    public static final int ALPHAIN = 0x00000001;
    public static final int SCALEIN = 0x00000002;
    public static final int SLIDEIN_BOTTOM = 0x00000003;
    public static final int SLIDEIN_LEFT = 0x00000004;
    public static final int SLIDEIN_RIGHT = 0x00000005;

切換動畫

quickAdapter.openLoadAnimation(BaseQuickAdapter.ALPHAIN);

自定義動畫

quickAdapter.openLoadAnimation(new BaseAnimation() {
                            @Override
                            public Animator[] getAnimators(View view) {
                                return new Animator[]{
                                        ObjectAnimator.ofFloat(view, "scaleY", 1, 1.1f, 1),
                                        ObjectAnimator.ofFloat(view, "scaleX", 1, 1.1f, 1)
                                };
                            }
                        });

動畫預設只執行一次,如果想重複執行可設定

mQuickAdapter.isFirstOnly(false);

因為有些人不希望第一頁看到動畫,或者說希望前幾個條目載入不需要有動畫,所以可以

設定不顯示動畫數量

adapter.setNotDoAnimationCount(count);

新增頭部、尾部

mQuickAdapter.addHeaderView(getView());
mQuickAdapter.addFooterView(getView());

刪除指定view

mQuickAdapter.removeHeaderView(getView);
mQuickAdapter.removeFooterView(getView);

刪除所有

mQuickAdapter.removeAllHeaderView();
mQuickAdapter.removeAllFooterView();

默認出現了頭部就不會顯示Empty,和尾部,配置以下方法也支援同時顯示:

setHeaderAndEmpty
setHeaderFooterEmpty

預設頭部尾部都是佔滿一行,如果需要不佔滿可以配置:

setHeaderViewAsFlow
setFooterViewAsFlow

載入更多

此adapter封裝載入更多其實並不好用,因此我更建議採用傳統的根據recyclerview判斷item位置進行判斷,自動載入更多

  recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
                firstVisibleItem=linearLayoutManager.findFirstVisibleItemPosition();

                if (newState == RecyclerView.SCROLL_STATE_IDLE&&linearLayoutManager.getItemCount() >0&&lastVisibleItem + 1 == linearLayoutManager.getItemCount()) {
                        new Handler().postDelayed(() -> loadData(),300) ;
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
                firstVisibleItem=linearLayoutManager.findFirstVisibleItemPosition();
               }
        });
 LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();  
  ```

螢幕中最後一個可見子項的position 

int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
“`
//當前螢幕所看到的子項個數

  layoutManager.getChildCount();  

//當前RecyclerView的所有子項個數

 layoutManager.getItemCount();  

//RecyclerView的滑動狀態

recyclerView.getScrollState();

如果想要預載入也很簡單,只需要修改當前最下方item的postion和總條目之間的差值即可。

分組佈局

此adapter分組佈局,不是類似於那種黏性頭部的分組,而是把頭佈局作為一個item進行多佈局的封裝。
頭佈局也支援點選事件,頭佈局內部的控制元件也支援點選事件。和普通佈局一般無二。

把需要加頭佈局的實體類用一個新的實體類包裹起來,比如作者舉例的Video,用MySection 包裹了Video,這樣的MySection內部有兩個構造方法,一個是頭佈局的構造方法,一個是實體類的構造方法,舉例如下

public class MySection extends SectionEntity<Video> {
    private boolean isMore;
    public MySection(boolean isHeader, String header, boolean isMroe) {
        super(isHeader, header);
        this.isMore = isMroe;
    }

    public MySection(Video t) {
        super(t);
    }

    public boolean isMore() {
        return isMore;
    }

    public void setMore(boolean mroe) {
        isMore = mroe;
    }
}

你可以在構造方法中加上自己需要的欄位來以便給頭佈局賦值;上述程式碼加入了一個ismore欄位,用來判斷是否有展開更多欄位。

adapter需要實現BaseSectionQuickAdapter,convertHead用來繫結頭佈局資料,convert用來繫結一般資料。

public class SectionAdapter extends BaseSectionQuickAdapter<MySection, BaseViewHolder> {

    public SectionAdapter(int layoutResId, int sectionHeadResId, List data) {
        super(layoutResId, sectionHeadResId, data);
    }

    @Override
    protected void convertHead(BaseViewHolder helper, final MySection item) {
        helper.setText(R.id.header, item.header)
              .setVisible(R.id.more, item.isMore())
              .addOnClickListener(R.id.more);
    }

    @Override
    protected void convert(BaseViewHolder helper, MySection item) {
        Video video = (Video) item.t;
        switch (helper.getLayoutPosition() %
                2) {
            case 0:
                helper.setImageResource(R.id.iv, R.mipmap.m_img1);
                break;
            case 1:
                helper.setImageResource(R.id.iv, R.mipmap.m_img2);
                break;

        }
        helper.setText(R.id.tv, video.getName());
    }
}

當然資料初始化的時候要注意,同一頭佈局下的需要寫在一起:

  public static List<MySection> getSampleData() {

        List<MySection> list = new ArrayList<>();

        list.add(new MySection(true, "Section 1", true));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));

        list.add(new MySection(true, "Section 2", false));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));

        list.add(new MySection(true, "Section 3", false));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));

        list.add(new MySection(true, "Section 4", false));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));
        list.add(new MySection(new Video(HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK, CYM_CHAD)));

    }

多佈局

實體類必須實現MultiItemEntity,在設定資料的時候,需要給每一個數據設定itemType

public class MultipleItem implements MultiItemEntity {
    public static final int TEXT = 1;
    public static final int IMG = 2;
    private int itemType;

    public MultipleItem(int itemType) {
        this.itemType = itemType;
    }

    @Override
    public int getItemType() {
        return itemType;
    }
}

adapter需要繼承BaseMultiItemQuickAdapter,addItemType用來繫結itemType和item佈局。

public class MultipleItemQuickAdapter extends BaseMultiItemQuickAdapter<MultipleItem, BaseViewHolder> {

    public MultipleItemQuickAdapter(List data) {
        super(data);
        addItemType(1, R.layout.text_view);
        addItemType(2, R.layout.image_view);
    }

    @Override
    protected void convert(BaseViewHolder helper, MultipleItem item) {
        switch (helper.getItemViewType()) {
            case 1:
                helper.setImageUrl(R.id.tv, item.getContent());
                break;
            case 2:
                helper.setImageUrl(R.id.iv, item.getContent());
                break;
        }
    }

}

設定空佈局

mQuickAdapter.setEmptyView(getView());

新增拖拽、滑動刪除

拖拽和滑動刪除的回撥方法

OnItemDragListener onItemDragListener = new OnItemDragListener() {
    @Override
    public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos){}
    @Override
    public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {}
    @Override
    public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {}
}

OnItemSwipeListener onItemSwipeListener = new OnItemSwipeListener() {
    @Override
    public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {}
    @Override
    public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {}
    @Override
    public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {}
};

adapter需要繼承BaseItemDraggableAdapter

public class ItemDragAdapter extends BaseItemDraggableAdapter<String, BaseViewHolder> {
    public ItemDragAdapter(List data) {
        super(R.layout.item_draggable_view, data);
    }

    @Override
    protected void convert(BaseViewHolder helper, String item) {
        helper.setText(R.id.tv, item);
    }
}

Activity使用程式碼

mAdapter = new ItemDragAdapter(mData);

ItemDragAndSwipeCallback itemDragAndSwipeCallback = new ItemDragAndSwipeCallback(mAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemDragAndSwipeCallback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);

// 開啟拖拽
mAdapter.enableDragItem(itemTouchHelper, R.id.textView, true);
mAdapter.setOnItemDragListener(onItemDragListener);

// 開啟滑動刪除
mAdapter.enableSwipeItem();
mAdapter.setOnItemSwipeListener(onItemSwipeListener);

預設不支援多個不同的 ViewType 之間進行拖拽,如果開發者有所需求:

重寫ItemDragAndSwipeCallback裡的onMove()方法,return true即可

樹形列表

例子:三級選單

// if you don't want to extent a class, you can also use the interface IExpandable.
// AbstractExpandableItem is just a helper class.
public class Level0Item extends AbstractExpandableItem<Level1Item> {...}
public class Level1Item extends AbstractExpandableItem<Person> {...}
public class Person {...}
adapter需要繼承BaseMultiItemQuickAdapter

public class ExpandableItemAdapter extends BaseMultiItemQuickAdapter<MultiItemEntity, BaseViewHolder> { 
    public ExpandableItemAdapter(List<MultiItemEntity> data) {    
        super(data);
        addItemType(TYPE_LEVEL_0, R.layout.item_expandable_lv0);   
        addItemType(TYPE_LEVEL_1, R.layout.item_expandable_lv1);    
        addItemType(TYPE_PERSON, R.layout.item_text_view);
    }
    @Override
    protected void convert(final BaseViewHolder holder, final MultiItemEntity item) {
        switch (holder.getItemViewType()) {
        case TYPE_LEVEL_0:
            ....
            //set view content
           holder.itemView.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   int pos = holder.getAdapterPosition();
                   if (lv0.isExpanded()) { 
                       collapse(pos);
                   } else {
                       expand(pos);
                   }
           }});
           break;
        case TYPE_LEVEL_1:
           // similar with level 0
           break;
        case TYPE_PERSON:
           //just set the content
           break;
    }
}

開啟所有選單:

adapter.expandAll();

刪除某一個item(新增和修改的思路是一樣的)

// 獲取當前父級位置

 int cp = getParentPosition(person);

// 通過父級位置找到當前list,刪除指定下級

 ((Level1Item)getData().get(cp)).removeSubItem(person);

// 列表層刪除相關位置的資料

 getData().remove(holder.getLayoutPosition());

// 更新檢視

 notifyDataSetChanged();

自定義ViewHolder
需要繼承BaseViewHolder

 public class MovieViewHolder extends BaseViewHolder 

然後修改adapter的第二個泛型為自定義的ViewHolder

public class DataBindingUseAdapter extends BaseQuickAdapter<Movie, DataBindingUseAdapter.MovieViewHolder>

注意:需要單獨建一個外部類繼承BaseViewHolder,否則部分機型會出現ClassCastException,如果是內部類的構造方法要是public,定義的那個類也最好是public。

混淆

keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers  class **$** extends com.chad.library.adapter.base.BaseViewHolder {
     <init>(...);
}

相關推薦

轉載最好通用adapter——BaseQuickAdpater

序言 recyclerview是Android 5.0推出的,是support-v7包中的新元件,它的出現就是為了代替ListView、GridView。 recyclerview是一個很強大的,高度解耦的控制元件。然而我們使用時候卻很麻煩,基礎的adap

轉載linux下的mount命令

文件的 flag 自動加載 網絡文件系統 解決問題 選項 lock home 多個參數 以下內容來自:http://blog.csdn.net/clozxy/article/details/5299054 http://linux.chinaunix.net/techdo

轉載 c++中static的用法

ostream 並不會 style 轉載 程序員 都是 note 每次 reference 出處: http://blog.csdn.net/majianfei1023/article/details/45290467 C 語言的 static 關鍵字有三種(具體來說是

轉載Maven依賴中的scope

lan 無需 而已 ref targe 周期 包含 配置 com Maven的一個哲學是慣例優於配置(Convention Over Configuration), Maven默認的依賴配置項中,scope的默認值是compile,項目中經常傻傻的分不清,直接默認了。今天梳

TestNGTestNG例執行方法

一、直接在eclipse內部執行 這種方式比較簡單,就是直接右鍵一個test檔案然後選擇以testNG執行,或者選擇xml檔案執行,基本執行方法見帖子:https://mp.csdn.net/mdeditor/83243822# 二、命令列方式 除了直接再eclipse內部執行外

轉載BlockingQueue(阻塞佇列)

注意:該隨筆內容完全引自http://wsmajunfeng.iteye.com/blog/1629354,寫的很好,非常感謝,複製過來算是個積累,怕以後找不到。 一. 前言   在新增的Concurrent包中,BlockingQueue很好的解決了多執行緒中,如何高效安全“傳輸”資

轉載Linux下安裝軟體命令

【轉載日期】2018.09.11 【轉載標題】Linux下安裝軟體命令詳解 ----------------------------------------------------------------------------------------------

轉載LoadRunner效能測試入門教程

第一章:效能測試基礎1-1. 大話效能測試效能測試使用自動化測試工具對產品按一定的效能指標進行測試,解決效能瓶頸,給使用者最好的體驗。效能測試的時代背景,作用BAT網站(baidu),軟體(T),遊戲......金融( 銀行,證券系統),教育(教務系統),交通(12306)效能測試基本流程 效能測試需求分析

轉載LoadRunner戶行為模擬器 《第三篇》

導入數據 得到 call 顯示 java val 位置 stat winrunner 轉載出處:https://www.cnblogs.com/kissdodog/p/3759270.html 用戶行為模擬器簡稱VU,VU通過運行VU腳本模擬了用戶對軟件的操作行為。VU

轉載Windows10fiddler抓包Android應用(解決手機設定代理後無法上網,設定只抓app包)

1、環境準備 1、電腦上安裝fiddler 2、手機和電腦在同一個區域網內 2、設定 1.fiddler>Tools>Fiddler Options>

轉載理解戶模式和內核模式(譯)

manager new load 導致 針對 兩種 upload 底層 ref 理解用戶模式和內核模式(譯) ###理解用戶模式和內核模式 本文出處: http://blog.codinghorror.com/understanding-user-and-kernel-

轉載字元與正則表示式

字元匹配 .:匹配任意單個字元 *:匹配其前面一個字元出現任意次 ?:匹配其前面的字元1次或0次 +:匹配其前面一個字元出現至少一次(在擴充套件正則表示式中) 位置匹配 ^:錨定行首 $:錨定行尾 \<或\b:錨定詞首,其後面的任意字元必須作為單

轉載個人戶如何為自己的網站及應用選配雲服務器

pac 並行 lsp 及其 lan 內存 高效 服務 strong 最近幾年雲服務器越來越普及,除了安全性要求特別高的業務之外,很多個人開發者以及企業都會選擇購買一臺雲服務器作為自己的服務器,這樣可以省去自己購買服務器的昂貴費用以及服務器機房維護的人員費用。下面就來介紹下

SpringSpring MVC原理及配置

進行 return sub sca scrip uil 線程安全 松耦合 必須 1.Spring MVC概述: Spring MVC是Spring提供的一個強大而靈活的web框架。借助於註解,Spring MVC提供了幾乎是POJO的開發模式,使得控制器的開發和測試更加簡

LinuxLinux定時任務Crontab命令

星期幾 ima 默認 定時 最好 表示 時間 path 配置文件 linux 系統則是由 cron (crond) 這個系統服務來控制的。Linux 系統上面原本就有非常多的計劃性工作,因此這個系統服務是默認啟動的。另 外, 由於使用者自己也可以設置計劃任務,所以, Lin

C語言文件操作

pri void rfi 識別 archive format 隨機 stat 文本文 轉自:http://www.cnblogs.com/likebeta/archive/2012/06/16/2551780.html C語言中沒有輸入輸出語句,所有的輸入輸出功能都用

AndroidAndroid六種布局

spec rec 默認 bottom ron ado 居中 右下角 控制 這篇就對LinearLayout、RelativeLayout、自定義ViewGroup、FrameLayout、TableLayout、AbsoluteLayout六種布局進行詳細的講解。 1

C++拷貝構造函數

簡單的 之間 其他 創建 變量 tac 動態分配空間 data 產生 一. 什麽是拷貝構造函數 首先對於普通類型的對象來說,它們之間的復制是很簡單的,例如: int a = 100; int b = a; 而類對象與普通對象不同,類對象內部結構一般較為復雜,

shellLinux shell for 循環

linux shell編程 for循環 運維 for 循環格式 for i in 條件 do 內容 done 實例循環1到10並打印 #!/bin/bash - for i in `seq 10` do echo $i done 版權所有:arppinging

java的動態代理機制

bar 同時 @override returns 復制 exce ins com hello 在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴註入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎