1. 程式人生 > >BRVAH官方使用指南(持續更新)

BRVAH官方使用指南(持續更新)

brvah

本篇為github開源專案BRVAH的使用指南以及包含常見問題會第一時間更新最新的使用方法。最新版本請檢視releases,由於持續更新,建議點贊收藏,便於檢視

文章目錄

  • 框架引入
  • 使用Adapter
  • 新增Item事件
  • 新增列表載入動畫
  • 新增頭部、尾部
  • 自動載入
  • 分組佈局
  • 多佈局
  • 設定空佈局
  • 新增拖拽、滑動刪除
  • 樹形列表
  • 自定義ViewHolder
  • 擴充套件框架

框架引入

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

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

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

    dependencies {
            compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:VERSION_CODE'
    }

這裡的真實發行版本號, 替換 VERSION_CODE

注意: 一旦出現載入失敗的情況,只有兩種情況:
1. 配置沒配置好
配置沒配置好,有幾種情況:
1. 只配置了dependencies
2. 配置repositories,但是位置錯了,build.gradle(Project:XXXX) 檔案下的repositories有兩個,一個是buildscript下面的,一個是allprojects下面的,要配置到allprojects下面才是對的。
3. 版本號前面多一個v,這個是我的鍋,在2.1.2版本之前都是帶v的,之後(包含2.1.2)都不需要帶v。
2. 網路原因(這個就不解釋了)

使用Adapter

和原始的adapter相對,減少70%的程式碼量。

使用程式碼

public class QuickAdapter extends BaseQuickAdapter<Status, BaseViewHolder> {
    public QuickAdapter() {
        super(R.layout.tweet, DataServer.getSampleData());
    }

    @Override
    protected void convert(BaseViewHolder viewHolder, Status item) {
        viewHolder.setText(R.id.tweetName, item.getUserName())
                .setText(R.id.tweetText, item.getText())
                .setText(R.id.tweetDate, item.getCreatedAt())
                .setVisible(R.id.tweetRT, item.isRetweet())
                .linkify(R.id.tweetText);
                 Glide.with(mContext).load(item.getUserAvatar()).crossFade().into((ImageView) viewHolder.getView(R.id.iv));
    }
}

使用

首先需要繼承BaseQuickAdapter,然後BaseQuickAdapter<Status, BaseViewHolder>第一個泛型Status是資料實體型別,第二個BaseViewHolder是ViewHolder其目的是為了支援擴充套件ViewHolder。

賦值

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

常用方法

  • viewHolder.getLayoutPosition() 獲取當前item的position

常見問題

這些問題不是使用該庫的問題,但是經常有人問這些問題,所以特意寫出來,幫助後續遇到以下問題的開發者們。

為什麼有資料不顯示?
請檢查一下你的RecyclerView是否設定了LayoutManager。

為什麼有10條資料,只顯示1條?
請檢查一下item的佈局最外層的Layout是不是layout_height設定了match_parent.

資料狀態錯亂
這個問題無論是RecyclerView還是ListView不做處理都會出現問題,這個本質上是由於佈局重用機制導致的,解決辦法是通過資料狀態來控制控制元件的狀態,一定要設定狀態無論什麼狀態,ifelse是少不了的,如下程式碼:

  if(entity.isCheck){
    checkBox.isChecked(true);
  } else {
    checkBox.isChecked(false);
  }

新增Item事件

Item的點選事件

adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                Log.d(TAG, "onItemClick: ");
                Toast.makeText(ItemClickActivity.this, "onItemClick" + position, Toast.LENGTH_SHORT).show();
            }
        });

Item的長按事件

adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
                Log.d(TAG, "onItemLongClick: ");
                Toast.makeText(ItemClickActivity.this, "onItemLongClick" + position, Toast.LENGTH_SHORT).show();
                return false;
            }
        });

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

 @Override
    protected void convert(BaseViewHolder viewHolder, Status item) {
        viewHolder.setText(R.id.tweetName, item.getUserName())
                .setText(R.id.tweetText, item.getText())
                .setText(R.id.tweetDate, item.getCreatedAt())
                .setVisible(R.id.tweetRT, item.isRetweet())
                .addOnClickListener(R.id.tweetAvatar)
                .addOnClickListener(R.id.tweetName)
                .linkify(R.id.tweetText);

    }

然後在設定

 adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public boolean onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                Log.d(TAG, "onItemChildClick: ");
                Toast.makeText(ItemClickActivity.this, "onItemChildClick" + position, Toast.LENGTH_SHORT).show();
                return false;
            }
        });

Item子控制元件的長按事件
步驟同上使用方法不同。
adapter中繫結方法將addOnClickListener改成addOnLongClickListener.
設定點選事件方法setOnItemChildClickListener改成setOnItemChildLongClickListener

注意:設定子控制元件的事件,如果不在adapter中繫結,點選事件無法生效,因為無法找到你需要設定的控制元件。

如果需要在點選事件中獲取其他子控制元件可以使用:

getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId) 

注意:如果有header的話需要處理一下position加上 headerlayoutcount。

新增列表載入動畫

開啟動畫(預設為漸顯效果)
adapter.openLoadAnimation();
預設提供5種方法(漸顯、縮放、從下到上,從左到右、從右到左)

public static final int ALPHAIN = 0x00000001;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SCALEIN = 0x00000002;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_BOTTOM = 0x00000003;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_LEFT = 0x00000004;
    /**
     * Use with {@link #openLoadAnimation}
     */
    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);

首次到介面的item都依次執行載入動畫

由於進入介面的item都是很多的速度進來的所以不會出現滑動顯示的依次執行動畫效果,這個時候會一起執行動畫,如果覺得這樣的效果不好可以使用setNotDoAnimationCount設定第一屏item不執行動畫,但是如果需要依次執行動畫可以重寫startAnim讓第一個螢幕的item動畫延遲執行即可。

@Override
    protected void startAnim(Animator anim, int index) {
        super.startAnim(anim, index);
        if (index < count)
        anim.setStartDelay(index * 150);
    }

新增頭部、尾部

新增

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

刪除指定view

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

刪除所有

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

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

setHeaderAndEmpty
setHeaderFooterEmpty

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

setHeaderViewAsFlow
setFooterViewAsFlow

自動載入

上拉載入

// 滑動最後一個Item的時候回撥onLoadMoreRequested方法
setOnLoadMoreListener(RequestLoadMoreListener);

預設第一次載入會進入回撥,如果不需要可以配置:

mQuickAdapter.disableLoadMoreIfNotFullPage();

回撥處理程式碼

mQuickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
            @Override public void onLoadMoreRequested() {
                mRecyclerView.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (mCurrentCounter >= TOTAL_COUNTER) {
                            //資料全部載入完畢
                            mQuickAdapter.loadMoreEnd();
                        } else {
                            if (isErr) {
                                //成功獲取更多資料
                                mQuickAdapter.addData(DataServer.getSampleData(PAGE_SIZE));
                                mCurrentCounter = mQuickAdapter.getData().size();
                                mQuickAdapter.loadMoreComplete();
                            } else {
                                //獲取更多資料失敗
                                isErr = true;
                                Toast.makeText(PullToRefreshUseActivity.this, R.string.network_err, Toast.LENGTH_LONG).show();
                                mQuickAdapter.loadMoreFail();

                            }
                        }
                    }

                }, delayMillis);
            }
        }, mReyclerView);

載入完成(注意不是載入結束,而是本次資料載入結束並且還有下頁資料)

mQuickAdapter.loadMoreComplete();

載入失敗

mQuickAdapter.loadMoreFail();

載入結束

mQuickAdapter.loadMoreEnd();

注意:如果上拉結束後,下拉重新整理需要再次開啟上拉監聽,需要使用setNewData方法填充資料。

開啟或關閉載入(一般用於下拉的時候做處理,因為上拉下拉不能同時操作)

mQuickAdapter.setEnableLoadMore(boolean);

預載入

// 當列表滑動到倒數第N個Item的時候(預設是1)回撥onLoadMoreRequested方法
mQuickAdapter.setPreLoadNumber(int);

設定自定義載入佈局

mQuickAdapter.setLoadMoreView(new CustomLoadMoreView());
public final class CustomLoadMoreView extends LoadMoreView {

    @Override public int getLayoutId() {
        return R.layout.view_load_more;
    }

    /**
     * 如果返回true,資料全部載入完畢後會隱藏載入更多
     * 如果返回false,資料全部載入完畢後會顯示getLoadEndViewId()佈局
     */
    @Override public boolean isLoadEndGone() {
        return true;
    }

    @Override protected int getLoadingViewId() {
        return R.id.load_more_loading_view;
    }

    @Override protected int getLoadFailViewId() {
        return R.id.load_more_load_fail_view;
    }

    /**
     * isLoadEndGone()為true,可以返回0
     * isLoadEndGone()為false,不能返回0
     */
    @Override protected int getLoadEndViewId() {
        return 0;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_40">

    <LinearLayout
        android:id="@+id/load_more_loading_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="horizontal">

        <ProgressBar
            android:id="@+id/loading_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="?android:attr/progressBarStyleSmall"
            android:layout_marginRight="@dimen/dp_4"
            android:indeterminateDrawable="@drawable/sample_footer_loading_progress"/>

        <TextView
            android:id="@+id/loading_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp_4"
            android:text="@string/loading"
            android:textColor="#0dddb8"
            android:textSize="@dimen/sp_14"/>
    </LinearLayout>

    <FrameLayout
        android:id="@+id/load_more_load_fail_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone">


        <TextView
            android:id="@+id/tv_prompt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#0dddb8"
            android:text="@string/load_failed"/>

    </FrameLayout>

</FrameLayout>

下拉載入(符合聊天軟體下拉歷史資料需求)
設定開啟開關

 mAdapter.setUpFetchEnable(true);

設定監聽

mAdapter.setUpFetchListener(new BaseQuickAdapter.UpFetchListener() {
            @Override
            public void onUpFetch() {
                startUpFetch();
            }
        });

private void startUpFetch() {
        count++;
        /**
         * set fetching on when start network request.
         */
        mAdapter.setUpFetching(true);
        /**
         * get data from internet.
         */
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAdapter.addData(0, genData());
                /**
                 * set fetching off when network request ends.
                 */
                mAdapter.setUpFetching(false);
                /**
                 * set fetch enable false when you don't need anymore.
                 */
                if (count > 5) {
                    mAdapter.setUpFetchEnable(false);
                }
            }
        }, 300);
    }

開始載入的位置

mAdapter.setStartUpFetchPosition(2);

分組佈局

實體類必須繼承SectionEntity

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

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

adapter構造需要傳入兩個佈局id,第一個是item的,第二個是head的,在convert方法裡面載入item資料,在convertHead方法裡面載入head資料

public class SectionAdapter extends BaseSectionQuickAdapter<MySection> {
     public SectionAdapter(int layoutResId, int sectionHeadResId, List data) {
        super(layoutResId, sectionHeadResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, MySection item) {
        helper.setImageUrl(R.id.iv, (String) item.t);
    }
    @Override
    protected void convertHead(BaseViewHolder helper,final MySection item) {
        helper.setText(R.id.header, item.header);

        helper.setOnClickListener(R.id.more, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context,item.header+"more..",Toast.LENGTH_LONG).show();
            }
        });
    }

多佈局

實體類必須實現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;
    }
}

在構造裡面addItemType繫結type和layout的關係

public class MultipleItemQuickAdapter extends BaseMultiItemQuickAdapter<MultipleItem, BaseViewHolder> {

    public MultipleItemQuickAdapter(List data) {
        super(data);
        addItemType(MultipleItem.TEXT, R.layout.text_view);
        addItemType(MultipleItem.IMG, R.layout.image_view);
    }

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

}

如果考慮到在GridLayoutManager複用item問題可以配置:

  multipleItemAdapter.setSpanSizeLookup(new BaseQuickAdapter.SpanSizeLookup() {
            @Override
            public int getSpanSize(GridLayoutManager gridLayoutManager, int position) {
                return data.get(position).getSpanSize();
            }
        });

設定空佈局

// 沒有資料的時候預設顯示該佈局
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

擴充套件框架

由於adapter本身能力有限,我們又不想耦合view層所以有些需求是現實不了,於是合作了一些優秀開源庫,為開發者提供更多可能性。以下擴充套件框架都是有結合BRVAH的demo。

本文章由於持續更新,建議點贊收藏,便於檢視。也歡迎大家提出更多建議,我就會第一時間看到後回覆,持續到什麼時候?只要還沒去領盒飯,我就會持續的。