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不做處理都會出現問題,這個本質上是由於佈局重用機制導致的,解決辦法是通過資料狀態來控制控制元件的狀態,一定要設定狀態無論什麼狀態,if
和else
是少不了的,如下程式碼:
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。
本文章由於持續更新,建議點贊收藏,便於檢視。也歡迎大家提出更多建議,我就會第一時間看到後回覆,持續到什麼時候?只要還沒去領盒飯,我就會持續的。