项目地址:https://github.com/woaigmz/SmartDiffAdapter,欢迎加入:)加油!
感谢 https://github.com/CymChad/BaseRecyclerViewAdapterHelper 的大神们:)
1: 这个adapter精简了一些功能,自己项目里使用起来不会觉得额外代码过多;
2: adapter的实现 抽象出convert方法进行view和data的绑定,以范型和构造传入model(data)和viewholder(helper),convert输出 data和helper
protected abstract void convert(K helper, T item)
3: SmartDiffAdapter产用了android.support.v7.util下的DiffUtil进行局部刷新,使用rxjava切换线程,计算在子线程,操作adapter刷新数据在主线程,之后更新会做标记,通过条目改变数和总数的比例决定释放开线程
BaseSmartAdapter:支持多条目
public abstract class BaseSmartAdapter<T, K extends BaseViewHolder> extends RecyclerView.Adapter<K> {}
/**构造方法
* initialize some
* @param layoutResId 布局
* @param mData 数据
*/
public BaseSmartAdapter(int layoutResId, List<T> mData) {
if (layoutResId != 0)
this.mLayoutResId = layoutResId;
this.mData = mData == null ? new ArrayList<T>() : mData;
}
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
K baseViewHolder = null;
this.mLayoutInflater = LayoutInflater.from(parent.getContext());
baseViewHolder = onCreateDefViewHolder(parent, viewType);
return baseViewHolder;
}
@Override
public void onBindViewHolder(K holder, int position) {
convert(holder, getItem(position));
}
@Override
public void onBindViewHolder(K holder, int position, List<Object> payloads) {
if (payloads == null || payloads.isEmpty()) {
onBindViewHolder(holder, position);
} else {
Bundle payload = (Bundle) payloads.get(0);
convert(holder, payload);
}
}
protected abstract void convert(K helper, T item);
protected abstract void convert(K helper, Bundle payload);
//******************************************************************************
多条目
private MultiTypeDelegate<T> mMultiTypeDelegate;
public void setMultiTypeDelegate(MultiTypeDelegate<T> multiTypeDelegate) {
mMultiTypeDelegate = multiTypeDelegate;
}
public MultiTypeDelegate<T> getMultiTypeDelegate() {
return mMultiTypeDelegate;
}
protected int getDefItemViewType(int position) {
if (mMultiTypeDelegate != null) {
return mMultiTypeDelegate.getDefItemViewType(mData, position);
}
return super.getItemViewType(position);
}
protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
int layoutId = mLayoutResId;
if (mMultiTypeDelegate != null) {
layoutId = mMultiTypeDelegate.getLayoutId(viewType);
}
return createBaseViewHolder(parent, layoutId);
}
//**************************************************************************************************
/**
* DiffUtils 刷新数据
*
* @param newData 新数据
*/
public void refreshData(final List<T> newData) {
if (smartDiffCallBack == null) {
throw new RuntimeException("callback must be created before refresh data");
}
Observable.create(new ObservableOnSubscribe<DiffUtil.DiffResult>() {
@Override
public void subscribe(ObservableEmitter<DiffUtil.DiffResult> e) throws Exception {
BaseCallBack callBack = new BaseCallBack(mData, newData, smartDiffCallBack);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(callBack, true);
e.onNext(diffResult);
}
}).subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<DiffUtil.DiffResult>() {
@Override
public void accept(DiffUtil.DiffResult diffResult) throws Exception {
diffResult.dispatchUpdatesTo(BaseSmartAdapter.this);
mData = newData;
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
replaceData(newData);
}
});
}
使用
this work will be update to build project simplily
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.woaigmz:SmartDiffAdapter:0.1.2'
}
just like this 👇
your RecyclerView adapter:
public class MainAdapter extends BaseSmartAdapter<Student, MainViewHolder> {
public MainAdapter(int mLayoutResId) {
super(mLayoutResId);
}
@Override
protected void convert(MainViewHolder helper, Student item) {
helper.setText(R.id.student_name, item.getName())
.setText(R.id.student_sex, item.getSex())
.setText(R.id.student_age, item.getAge())
.setText(R.id.student_weight, item.getWeight())
.setText(R.id.student_score, item.getScore());
}
@Override
protected void convert(MainViewHolder helper, Bundle payload) {
for (String key : payload.keySet()) {
switch (key) {
case "name":
helper.setText(R.id.student_name, payload.getString("name"));
break;
case "sex":
helper.setText(R.id.student_sex, payload.getString("sex"));
break;
case "age":
helper.setText(R.id.student_age, payload.getString("age"));
break;
case "weight":
helper.setText(R.id.student_weight, payload.getString("weight"));
break;
case "score":
helper.setText(R.id.student_score, payload.getString("score"));
break;
}
}
}
}
use:
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
adapter = new MainAdapter(R.layout.item);
adapter.setSmartDiffCallBack(new SmartDiffCallBack<Student>() {
@Override
public boolean areItemsTheSame(Student oldItem, Student newItem) {
return oldItem.getName().equals(newItem.getName());
}
@Override
public boolean areContentsTheSame(Student oldItem, Student newItem) {
if (!oldItem.getName().equals(newItem.getName())) {
return false;
}
if (!oldItem.getSex().equals(newItem.getSex())) {
return false;
}
if (oldItem.getAge().equals(newItem.getAge())) {
return false;
}
if (oldItem.getWeight().equals(newItem.getWeight())) {
return false;
}
if (oldItem.getScore().equals(newItem.getScore())) {
return false;
}
return true;
}
@Nullable
@Override
public Bundle getChangePayload(Bundle bundle, Student oldItem, Student newItem) {
if (!oldItem.getName().equals(newItem.getName())) {
bundle.putString("name", newItem.getName());
}
if (!oldItem.getAge().equals(newItem.getAge())) {
bundle.putString("age", newItem.getAge());
}
if (!oldItem.getSex().equals(newItem.getSex())) {
bundle.putString("sex", newItem.getSex());
}
if (!oldItem.getWeight().equals(newItem.getWeight())) {
bundle.putString("weight", newItem.getWeight());
}
if (!oldItem.getScore().equals(newItem.getScore())) {
bundle.putString("score", newItem.getScore());
}
if (bundle.size() == 0) {
return null;
}
return bundle;
}
});
rv.setAdapter(adapter);
....
//the main
adapter.refreshData(studentsNew);
ViewHolder的封装简单代理模式:(接口契约--实现类)
IHolder:
public interface IHolder {
IHolder get(View itemView);
View getItemView();
<T extends View> T getView(@IdRes int viewId);
IHolder setTag(@IdRes int viewId, Object tag);
IHolder setTag(@IdRes int viewId, int key, Object tag);
@Deprecated
IHolder setOnTouchListener(@IdRes int viewId, View.OnTouchListener listener);
@Deprecated
IHolder setOnLongClickListener(@IdRes int viewId, View.OnLongClickListener listener);
@Deprecated
IHolder setOnItemClickListener(@IdRes int viewId, AdapterView.OnItemClickListener listener);
IHolder setOnItemLongClickListener(@IdRes int viewId, AdapterView.OnItemLongClickListener listener);
@Deprecated
IHolder setOnClickListener(@IdRes int viewId, View.OnClickListener listener);
IHolder setText(@IdRes int viewId, CharSequence value);
IHolder setText(@IdRes int viewId, @StringRes int strId);
IHolder setImageResource(@IdRes int viewId, @DrawableRes int imageResId);
IHolder setBackgroundColor(@IdRes int viewId, @ColorInt int color);
IHolder setBackgroundRes(@IdRes int viewId, @DrawableRes int backgroundRes);
IHolder setTextColor(@IdRes int viewId, @ColorInt int textColor);
IHolder setImageDrawable(@IdRes int viewId, Drawable drawable);
IHolder setImageBitmap(@IdRes int viewId, Bitmap bitmap);
IHolder setAlpha(@IdRes int viewId, float value);
IHolder setGone(@IdRes int viewId, boolean visible);
....... 省略
}
BaseViewHolder:
public class BaseViewHolder extends RecyclerView.ViewHolder implements IHolder {
private final SparseArray<View> views;
private View mItemView;
public BaseViewHolder(View itemView) {
super(itemView);
this.views = new SparseArray<>();
this.mItemView = itemView;
this.mItemView.setTag(this);
}
@Override
public BaseViewHolder get(View itemView) {
return (BaseViewHolder) itemView.getTag();
}
@Override
public View getItemView() {
if (mItemView == null) throw new RuntimeException("item has been gc");
return mItemView;
}
@Override
public <T extends View> T getView(@IdRes int viewId) {
View view = views.get(viewId);
if (view == null) {
view = mItemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
@Override
public BaseViewHolder setTag(@IdRes int viewId, Object tag) {
View view = getView(viewId);
view.setTag(tag);
return this;
}
@Override
public BaseViewHolder setTag(@IdRes int viewId, int key, Object tag) {
View view = getView(viewId);
view.setTag(key, tag);
return this;
}
@Override
@Deprecated
public BaseViewHolder setOnTouchListener(@IdRes int viewId, View.OnTouchListener listener) {
View view = getView(viewId);
view.setOnTouchListener(listener);
return this;
}
@Override
@Deprecated
public BaseViewHolder setOnLongClickListener(@IdRes int viewId, View.OnLongClickListener listener) {
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
....... 省略
多条目的使用:
your bean like this must be implements MultiItemEntity
public class HomePageMultiItem implements MultiItemEntity {
public int itemType;
public static final int HEAD_BANNER = 1;
public static final int NAVIGATION_BUTTON_LIST = 2;
public static final int RECOMMEND_PRODUCT_LIST = 3;
public List<BannerListBean.ImgListBean> imgList;
public List<HomePageNavigationButtonBean.HomePageButtonListBean> buttonList;
public HomePageRecommendsProductListBean.ListBean listBean;
@Override
public int getItemType() {
return itemType;
}
}
your adapter:List<HomePageMultiItem> 拿到数据的顺序决定了你多条目的顺序 you can test or view the source code of SmartDiffAdapter
thanks
public class HomePageAdapter extends BaseMultiItemQuickAdapter<HomePageMultiItem, HomePageAdapter.MyViewHolder> {
public HomePageAdapter(List<HomePageMultiItem> data) {
super(data);
addItemType(HomePageMultiItem.HEAD_BANNER, R.layout.home_page_banner);
addItemType(HomePageMultiItem.NAVIGATION_BUTTON_LIST, R.layout.home_page_navigation_button);
addItemType(HomePageMultiItem.RECOMMEND_PRODUCT_LIST, R.layout.item_home_recommend);
}
@Override
protected void convert(MyViewHolder helper, HomePageMultiItem item) {
switch (helper.getItemViewType()) {
case HomePageMultiItem.HEAD_BANNER:
helper.setBannerList(item.imgList);
break;
case HomePageMultiItem.NAVIGATION_BUTTON_LIST:
helper.setNavigationButtonList(item.buttonList);
break;
case HomePageMultiItem.RECOMMEND_PRODUCT_LIST:
HomePageRecommendsProductListBean.ListBean bean = item.listBean;
......省略
break;
}
}
public void stopBanner(ViewGroup groupView) {
......
}
static class MyViewHolder extends BaseViewHolder {
public MyViewHolder(View view) {
super(view);
}
MyViewHolder setBannerList(List<BannerListBean.ImgListBean> imgList) {
List<String> urls = new ArrayList<>();
for (BannerListBean.ImgListBean imgListBean : imgList) {
urls.add(imgListBean.address);
}
Banner banner = getView(R.id.cycleBanner);
banner.setImageLoader(new GlideImageLoader());
banner.setImages(urls);
banner.start();
return this;
}
MyViewHolder setNavigationButtonList(List<HomePageNavigationButtonBean.HomePageButtonListBean> buttonList) {
TabLayout tab = getView(R.id.tabs_home);
tab.removeAllTabs();
for (HomePageNavigationButtonBean.HomePageButtonListBean homePageButtonListBean : buttonList) {
View brand_view = View.inflate(tab.getContext(), R.layout.tablayout_brand, null);
ImageView iv_brand = brand_view.findViewById(R.id.iv_home_btn);
TextView tv_brand = brand_view.findViewById(R.id.tv_home_btn);
ImageLoader.loadImage(iv_brand, homePageButtonListBean.buttonURL);
tv_brand.setText(homePageButtonListBean.buttonName);
tab.addTab(tab.newTab().setCustomView(brand_view), false);
}
return this;
}
}
}
presenter(getdata --- this example --->HomePageMultiItem):
public class HomePagePresenter extends BasePresenter<HomePageContract.HomePageView, HomePageContract.HomePageModel> implements HomePageContract.HomePagePresenter {
@Override
public void toLoadingData() {
mView.showLoading();
//.onErrorReturn(throwable -> Collections.emptyList())保证首页正常显示,部分接口错误不做处理,如果请求失败 只返回空值
mDisposable.add(Observable.zip(mModel.getBannerList().onErrorReturn(throwable -> Collections.emptyList()), mModel.getNavigationButtonList().onErrorReturn(throwable -> Collections.emptyList()), mModel.getRecommendsProductList().onErrorReturn(throwable -> Collections.emptyList()), (bannerListBeans, buttonListBeans, recommendListBeans) -> {
List<HomePageMultiItem> items = getHomePageMultiItems(bannerListBeans, buttonListBeans, recommendListBeans);
mView.hideLoading();
return items;
}).subscribe((List<HomePageMultiItem> homePageMultiItems) ->
mView.onLoadingSucceed(homePageMultiItems), throwable ->
mView.onError(throwable.getMessage())));
}
@Override
public void refreshData() {
//由于在 zip 之前要捕捉异常并且要通过 onErrorReturn 返回空数据,所以在 doOnError 函数做错误处理
mDisposable.add(Observable.zip(mModel.getBannerList(), mModel.getNavigationButtonList(), mModel.getRecommendsProductList(), (bannerListBeans, buttonListBeans, recommendListBeans) -> {
List<HomePageMultiItem> items = getHomePageMultiItems(bannerListBeans, buttonListBeans, recommendListBeans);
return items;
}).doOnError(new ApiErrorConsumer() {
@Override
public void onFail(String code, String message) {
mView.onError("刷新失败:" + message);
}
}).onErrorReturn(throwable -> Collections.emptyList()).subscribe(homePageMultiItems ->
mView.onLoadingSucceed(homePageMultiItems)));
mView.afterRefreshData();
}
@Override
public HomePageContract.HomePageModel bindModel() {
return (HomePageContract.HomePageModel) HomePageModel.newInstance();
}
/**********************************************************************************************************************************************************/
@NonNull
private List<HomePageMultiItem> getHomePageMultiItems(List<BannerListBean.ImgListBean> bannerListBeans, List<HomePageNavigationButtonBean.HomePageButtonListBean> buttonListBeans, List<HomePageRecommendsProductListBean.ListBean> recommendListBeans) {
List<HomePageMultiItem> items = new ArrayList<>();
if (bannerListBeans != null && bannerListBeans.size() > 0) {
HomePageMultiItem item = new HomePageMultiItem();
item.itemType = HomePageMultiItem.HEAD_BANNER;
item.imgList = bannerListBeans;
items.add(item);
}
if (buttonListBeans != null && buttonListBeans.size() > 0) {
HomePageMultiItem item = new HomePageMultiItem();
item.itemType = HomePageMultiItem.NAVIGATION_BUTTON_LIST;
item.buttonList = buttonListBeans;
items.add(item);
}
if (recommendListBeans != null && recommendListBeans.size() > 0) {
for (HomePageRecommendsProductListBean.ListBean listBean : recommendListBeans) {
HomePageMultiItem item = new HomePageMultiItem();
item.itemType = HomePageMultiItem.RECOMMEND_PRODUCT_LIST;
item.listBean = listBean;
items.add(item);
}
}
return items;
}
}
最后:
BaseDiffCallBack几个回调方法的理解:
public class BaseCallBack<T> extends DiffUtil.Callback {
private final List<T> oldItems;
private final List<T> newItems;
private SmartDiffCallBack<T> callBack;
BaseCallBack(List<T> oldItems, List<T> newItems, SmartDiffCallBack<T> smartDiffCallBack) {
this.oldItems = oldItems;
this.newItems = newItems;
this.callBack = smartDiffCallBack;
}
@Override
public final int getOldListSize() {
return oldItems.size();
}
@Override
public final int getNewListSize() {
return newItems.size();
}
/**
* 用来判断 两个对象是否是相同的Item。
* 通常是实体bean的唯一主键id
* For example, if your items have unique ids, this method should check their id equality.
*
* @param oldItemPosition
* @param newItemPosition
* @return True if the two items represent the same object or false if they are different.
*/
@Override
public final boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return callBack.areItemsTheSame(oldItems.get(oldItemPosition), newItems.get(newItemPosition));
}
/**
* 这个方法仅仅在areItemsTheSame()返回true时,才调用。
*
* @param oldItemPosition
* @param newItemPosition
* @return True if the contents of the items are the same or false if they are different.Item的视觉表现是否相同。
*/
@Override
public final boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return callBack.areContentsTheSame(oldItems.get(oldItemPosition), newItems.get(newItemPosition));
}
/**
* 当{@link # areItemsTheSame(int, int)} 返回true,且{@link # areContentsTheSame(int, int)} 返回false时,DiffUtils会回调此方法,
* 去得到这个Item(有哪些)改变的payload。
*
* @param oldItemPosition
* @param newItemPosition
* @return 默认的实现是返回null
*/
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return callBack.getChangePayload(new Bundle(), oldItems.get(oldItemPosition), newItems.get(newItemPosition));
}
}
自定义回调接口,留给adapter(adapter写setXXXinterface的方法):
public interface SmartDiffCallBack<T> {
boolean areItemsTheSame(T oldItem, T newItem);
boolean areContentsTheSame(T oldItem, T newItem);
@Nullable
Object getChangePayload(Bundle bundle, T oldItem, T newItem);
}
thanks
感谢你们的关注和支持:)
网友评论