美文网首页
简易聪明的adapter-SmartDiffAdapter

简易聪明的adapter-SmartDiffAdapter

作者: 半只温柔 | 来源:发表于2018-05-05 12:09 被阅读0次

    项目地址: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
    感谢你们的关注和支持:)

    相关文章

      网友评论

          本文标题:简易聪明的adapter-SmartDiffAdapter

          本文链接:https://www.haomeiwen.com/subject/apkjrftx.html