BRVAH的BaseRecyclerViewAdapterHelper與MVVM模式優雅結合,Recyclerview如何在Databinding中快捷、方便地使用(二)

BRVAH的BaseRecyclerViewAdapterHelper與MVVM模式優雅結合(其一)
BRVAH的BaseRecyclerViewAdapterHelper與MVVM模式優雅結合(其三)
接上一章內容,如果大家沒有看過第一章的話,請先看第一章。點上面鏈接就能跳轉。

頭部和腳部

20191225102453646.gif
public class HeadFootViewModel extends BaseBindingViewModel<SimpleData> {
    @Override
    protected Map<Integer, CSBravhItemBinding> getItemBinding() {
        Map<Integer, CSBravhItemBinding> mp = new HashMap<>();
        mp.put(0, new CSBravhItemBinding(com.caesar.brvahbinding.BR.bean, R.layout.item_simple));
        return mp;
    }
 
    @Override//這個回調是頭部的item的綁定數據,也支持綁定多個數據事件
    public ArrayList<CSBravhItemBinding> getHeadBinding() {
        ArrayList<CSBravhItemBinding> heads = new ArrayList<>();
        heads.add(new CSBravhItemBinding(BR.data, R.layout.layout_head_one, new HeadOneData(), BR.action, new brvah()));
        heads.add(new CSBravhItemBinding(BR.data, R.layout.layout_head_two, new HeadTwoData()));
        return heads;
    }
 
    @Override//這個回調是腳部的item的綁定數據,也支持綁定多個數據事件
    public ArrayList<CSBravhItemBinding> getFootBinding() {
        ArrayList<CSBravhItemBinding> foots = new ArrayList<>();
        foots.add(new CSBravhItemBinding(BR.data, R.layout.layout_foot_one, new FootOneData()));
        foots.add(new CSBravhItemBinding(BR.data, R.layout.layout_foot_two, new FootTwoData()));
        return foots;
    }
 
    @Override
    public void load() {
        load(CreateData.getSimpleData());
    }
 
    @Override
    public RecyclerView.ItemDecoration onitemDecoration() {
        return new NormalLineTopHeadDecoration(30, true);
    }
 
    //某個頭部綁定的事件,告訴大家可以這樣調用
    public class brvah implements CSAction0 {
        @Override
        public void call() {
            bindingAdapter.removeAllHeaderView();
        }
    }
}

先看布局文件,很簡單,app:cs_brvah_footBinding="@{vm.footBinding}" app:cs_brvah_headBinding="@{vm.headBinding}"這2個方法,分別綁定了頭部和腳部。接著,大家看HeadFootViewModel這個類,其中getHeadBinding()這個回調,是綁定頭部的,跟普通的item綁定很像,只是少了個itemType,里面傳布局跟data,也可以綁定其他的事件。同理腳部布局數據是getFootBinding()這個回調。是不是很簡單。

空布局和下拉刷新

  <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:OnRefreshListener="@{vm.refreshListener}"
            app:refreshing="@{vm.isRefreshing}">
 
            <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:cs_brvah_Decoration="@{vm.itemDecoration}"
                app:cs_brvah_adapter="@{vm.bindingAdapter}"
                app:cs_brvah_emptyClickListener="@{vm.emptyOnClickListener}"
                app:cs_brvah_emptyResId="@{vm.emptyResId}" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
public class EmptyRefrshViewModel extends BaseBindingViewModel<SimpleData> {
 
    private int countD;
 
    @Override
    protected Map<Integer, CSBravhItemBinding> getItemBinding() {
        Map<Integer, CSBravhItemBinding> mp = new HashMap<>();
        mp.put(0, new CSBravhItemBinding(com.caesar.brvahbinding.BR.bean, R.layout.item_simple));
        return mp;
    }
    @Override//里面模擬網絡請求時,出現的情況,正常返回,數據為空,請求錯誤.
    public void load() {
        ++countD;
        if (countD % 3 == 0) {
            load(CreateData.getSimpleData());
        } else if (countD % 3 == 1) {
            load(getEmptyData());
        } else if (countD % 3 == 2) {
            load(getErrorData());
        }
 
    }
    //模擬數據返回為空
    public static Flowable<List<SimpleData>> getEmptyData() {
        CSLog.Print("調用加載空布局");
        return Flowable.create(new FlowableOnSubscribe<List<SimpleData>>() {
            @Override
            public void subscribe(FlowableEmitter<List<SimpleData>> emitter) throws Exception {
                ArrayList<SimpleData> data = new ArrayList<>();
                emitter.onNext(data);
                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER).delay(3, TimeUnit.SECONDS);
    }
    //模擬網絡請求出錯
    public static Flowable<List<SimpleData>> getErrorData() {
        CSLog.Print("調用加載錯誤");
        return Flowable.create(new FlowableOnSubscribe<List<SimpleData>>() {
            @Override
            public void subscribe(FlowableEmitter<List<SimpleData>> emitter) throws Exception {
                emitter.onError(null);
            }
        }, BackpressureStrategy.BUFFER).delay(1, TimeUnit.SECONDS);
    }
    //清空數據的點擊事件,這邊因為我的baseAdapter里面,對items做過監聽,可以看CSBindingListChangedCallBack這個類,所以,當items的數據發生
    //改變時,綁定的適配器會自動刷新,所以,如果你是使用自己的適配器的話(下面會講),別忘記刷新適配器
    public void cliear(View view) {
        items.clear();
    }
 
    @Override
    public RecyclerView.ItemDecoration onitemDecoration() {
        return new NormalLineDecoration(30, true);
    }
 
    @Override//可以重寫空布局的UI
    public int getEmptyViewRes(int type) {
        switch (type) {
            case EmptyViewType.ERROR:
                return R.layout.layout_frame_error_view;
            case EmptyViewType.LOADING:
                return R.layout.layout_frame_loading_view;
            case EmptyViewType.REFRESH:
                return R.layout.layout_frame_refresh_view;
            default:
                return R.layout.layout_frame_empty_view;
        }
    }
}

大家找到EmptyRefreshActivity這個界面,在布局文件中,我加了一個SwipeRefreshLayout下拉刷新控件,在里面,綁定了app:OnRefreshListener="@{vm.refreshListener}"和app:refreshing="@{vm.isRefreshing}",其中refreshListener這個是下拉監聽器,我的baseViewModel中,寫了監聽回調,

//下拉刷新控件的監聽器,里面調用重新加載數據的方法
    public SwipeRefreshLayout.OnRefreshListener getRefreshListener() {
        return new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                isRefreshing.set(true);
                reload();
            }
        };
    }

如果下拉了,就會調用重新加載的方法。而isRefreshing這個是刷新判斷,因為跟下拉控件綁定在一起,所以下拉的時候,可以反應刷新控件的狀態。然后在RecyclerView中,我綁定了app:cs_brvah_emptyClickListener="@{vm.emptyOnClickListener}" 和app:cs_brvah_emptyResId="@{vm.emptyResId}",第一個是綁定空布局的點擊事件,下面是baseViewModel的部分相關代碼

//空布局的點擊事件,里面做判斷,如果當前空布局不是正在加載的狀態,點擊之后,就重新獲取數據
    protected View.OnClickListener getEmptyOnClickListener() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CSLog.Print("點擊了空布局按鈕");
                if (emptyResId.get() != getEmptyViewRes(EmptyViewType.LOADING)) {
                    reload();
                    emptyResId.set(getEmptyViewRes(EmptyViewType.LOADING));
                }
            }
        };
    }

當點擊空布局時,如果當前不是正在加載的狀態,就重新獲取數據。

第二個vm.emptyResId是空布局的綁定,你可以在每個viewModel中,重寫getEmptyViewRes這個方法,就可以定制空布局了。

側滑刪除和拖動

20191225102629532.gif

好,先來講側滑刪除,大家找到SwipeActivity這個界面

public class SwipeViewModel extends BaseBindingViewModel<SimpleData> {
    private boolean isSwipe = true;
 
   。。。
 
    @Override//只要重寫這個監聽器,然后再布局中綁定,就能側滑刪除
    public OnItemSwipeListener getItemSwipeListener() {
        return new OnItemSwipeListener() {
            @Override
            public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int i) {
 
            }
 
            @Override
            public void clearView(RecyclerView.ViewHolder viewHolder, int i) {
 
            }
 
            @Override
            public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int i) {
 
            }
 
            @Override
            public void onItemSwipeMoving(Canvas canvas, RecyclerView.ViewHolder viewHolder, float v, float v1, boolean b) {
                canvas.drawColor(ContextCompat.getColor(FramGroble.INSTANCE.getTopActivity(), R.color.colorAccent));
            }
        };
    }
//這邊是一個控制側滑開關的例子,通過獲取Controller()來控制,我已經在適配器中封裝好了
    public void onSwi(View view) {
        if (isSwipe) {
            isSwipe = false;
            bindingAdapter.getDraggableController().disableSwipeItem();
        } else {
            isSwipe = true;
            bindingAdapter.getDraggableController().enableSwipeItem();
        }
    }
 
    @Override//設置側滑方向
    public int getOnSwipeMoveFrags() {
        return ItemTouchHelper.START | ItemTouchHelper.END;
    }
}
<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:cs_brvah_SwipeMoveFrags="@{vm.swipeMoveFrags}"
            app:cs_brvah_Decoration="@{vm.itemDecoration}"
            app:cs_brvah_OnItemSwipeListener="@{vm.onItemSwipeListener}"
            app:cs_brvah_adapter="@{vm.bindingAdapter}"
 
            />

是不是也很簡單,首先看viewModel,只要你重寫了OnItemSwipeListener這個監聽回調,然后在布局文件中,將該監聽器綁定在布局中,app:cs_brvah_OnItemSwipeListener="@{vm.onItemSwipeListener}"就可以了,是不是很簡單。然后你還可以設置滑動的方向,默認是向后滑動刪除的,你可以通過getOnSwipeMoveFrags()重寫這個方法,進行設置,當然設置好了之后,別忘了在布局文件中去綁定。如果你想要設置滑動開關怎么辦,那當然也是可以的,萬能適配器通過一個Controller來進行控制,我已經封裝在適配器里了,你可以通過bindingAdapter.getDraggableController()獲取到,然后設置開關就可以,例子已經給出。

接下來是拖動功能

public class DragViewModel extends NonMultiViewModel {
 
    private boolean isSwipe = true;
 
    @Override//只要設置拖動監聽器,就可以實現拖動功能
    public OnItemDragListener getItemDragListener() {
        return new OnItemDragListener() {
            @Override
            public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int i) {
 
            }
 
            @Override
            public void onItemDragMoving(RecyclerView.ViewHolder viewHolder, int i, RecyclerView.ViewHolder viewHolder1, int i1) {
 
            }
 
            @Override
            public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int i) {
 
            }
        };
    }
//拖動開關
    public void onSwi(View view) {
        if (isSwipe) {
            isSwipe = false;
            bindingAdapter.getDraggableController().disableDragItem();
        } else {
            isSwipe = true;
            bindingAdapter.getDraggableController().enableDragItem(bindingAdapter.getItemTouchHelper(null));
        }
    }
}
<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:cs_brvah_Decoration="@{vm.itemDecoration}"
            app:cs_brvah_OnItemDragListener="@{vm.onItemDragListener}"
            app:cs_brvah_adapter="@{vm.bindingAdapter}"
            app:cs_brvah_layoutManager="@{CSBrvahLayoutManager.grid(4)}"
            app:cs_brvah_multiType="@{vm.multiTypeDelegat}"
            app:cs_brvah_spansize="@{vm.spanSizeLookup}"
 
            />

我這邊拖動的viewModel,繼承了另外一個NonMultiViewModel,因為界面是一樣的。拖動跟側滑很像,也只要重寫getItemDragListener監聽器就可以,然后在布局中,調用app:cs_brvah_OnItemDragListener="@{vm.onItemDragListener}"進行綁定。是不是超級簡單。拖動的開關控制器,跟側滑一樣,也是調用getDraggableController(),不過打開拖動功能的時候,要注意bindingAdapter.getDraggableController().enableDragItem()里面要傳一個Touch,這個touch要通過bindingAdapter.getItemTouchHelper(null)方法去獲取,因為在綁定的時候,已經生成了一個touch,可以保證唯一性。例子里面都有。

我這邊將拖動跟側滑功能合在一起,做了個例子,大家可以找到DragASwipeActivity這個類,調用方法是一模一樣的,我這里就不再重復了。

Expand可擴展的多布局

20191225120822709.gif
public class ExpandViewModel extends BaseBindingViewModel<MultiItemEntity> {
 
    。。。
//當每次數據加載完成的時候,都會回調該方法,我在這邊,將列表展開
    @Override
    public void onDataLoadComplete() {
        bindingAdapter.expandAll();
    }
 
    //模擬獲取數據,這邊的數據要繼承AbstractExpandableItem
    private Flowable<List<MultiItemEntity>> getData() {
        return Flowable.create(new FlowableOnSubscribe<List<MultiItemEntity>>() {
            @Override
            public void subscribe(FlowableEmitter<List<MultiItemEntity>> emitter) throws Exception {
                ArrayList<MultiItemEntity> data = new ArrayList<>();
                for (int i = 0; i < 5; i++) {
                    ExDataGrandFa exDataGrandFa = new ExDataGrandFa("點我有驚喜", "點我啊", R.mipmap.headerandfooter_img1);
                    for (int j = 0; j < 3; j++) {
                        ExDataFather exDataFather = new ExDataFather("點我啊", "來點我啊", R.mipmap.m_img1);
                        for (int k = 0; k < 6; k++) {
                            ExDataChild exDataChild = new ExDataChild("點我消失", R.mipmap.head_img1);
                            exDataFather.addSubItem(exDataChild);
                        }
                        exDataGrandFa.addSubItem(exDataFather);
                    }
                    data.add(exDataGrandFa);
                }
                emitter.onNext(data);
                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER);
    }
 
    @Override
    public RecyclerView.ItemDecoration onitemDecoration() {
        return new NormaltemDecoration(10);
    }
 
    //最外面item的點擊事件,里面調用item的擴展和收縮.
    public class GrandAct implements CSAction1<ExDataGrandFa> {
 
        @Override
        public void call(ExDataGrandFa param) {
            if (param.isExpanded()) {
                bindingAdapter.collapse(items.indexOf(param));
            } else {
                bindingAdapter.expand(items.indexOf(param));
            }
        }
    }
 
    //第二個item的點擊事件,調用里面item的擴展和說收縮
    public class FatherAct implements CSAction1<ExDataFather> {
 
        @Override
        public void call(ExDataFather param) {
            if (param.isExpanded()) {
                bindingAdapter.collapse(items.indexOf(param));
            } else {
                bindingAdapter.expand(items.indexOf(param));
            }
        }
    }
 
    //最里面item的點擊事件,將item從本列表中刪除,記住有2個地方要刪除,一個是items,第二個是它外層的item中的數據
    public class ChildAct implements CSAction1<ExDataChild> {
        @Override
        public void call(ExDataChild param) {
            //這一部分刪除其實也不難,我是直接抄官方的,先獲取當前item的父item,將集合中item去掉,再從父item中刪掉當前item,要刪2次
            int positionAtAll = getParentPositionInAll(items.indexOf(param));
            items.remove(param);
            if (positionAtAll != -1) {
                IExpandable multiItemEntity = (IExpandable) bindingAdapter.getData().get(positionAtAll);
                multiItemEntity.getSubItems().remove(param);
            }
        }
    }
 
 
    /**
     * 該方法用于 IExpandable 樹形列表。
     * 如果不存在 Parent,則 return -1。
     *
     * @param position 所處列表的位置
     * @return 父 position 在數據列表中的位置
     */
    public int getParentPositionInAll(int position) {
        List<MultiItemEntity> data = bindingAdapter.getData();
        MultiItemEntity multiItemEntity = bindingAdapter.getItem(position);
 
        if (isExpandable(multiItemEntity)) {
            IExpandable IExpandable = (IExpandable) multiItemEntity;
            for (int i = position - 1; i >= 0; i--) {
                MultiItemEntity entity = data.get(i);
                if (isExpandable(entity) && IExpandable.getLevel() > ((IExpandable) entity).getLevel()) {
                    return I;
                }
            }
        } else {
            for (int i = position - 1; i >= 0; i--) {
                MultiItemEntity entity = data.get(i);
                if (isExpandable(entity)) {
                    return I;
                }
            }
        }
        return -1;
    }
 
    public boolean isExpandable(MultiItemEntity item) {
        return item != null && item instanceof IExpandable;
    }
 
}

布局的代碼沒什么好說的,就是常規調用,來看一下viewModel,是不是跟普通的多布局很像,也是實現了MultiItemEntity,不同的地方就是,它的item要繼承AbstractExpandableItem這個類,跟BRVAH中的一樣,其中的action是我寫的每個item的點擊事件監聽。也很簡單。

下拉刷新,上拉加載

20191225130722781.gif

下拉上拉這一部分用的還是挺多的,所以我要稍微著重講解一下,先貼代碼

public abstract class LoadMoreBindingViewModel<B> extends BaseBindingViewModel<B> {
 
    //加載更多布局,可以自定義
    public LoadMoreView loadMoreView;
    //加載更多結束
    public ObservableBoolean loadMoreEnd;
    //是否能夠加載更多
    public ObservableBoolean loadMoreEnable;
    //加載更多完成
    public ObservableBoolean loadMoreSuccess;
    //上拉加載的監聽回調
    public BaseQuickAdapter.RequestLoadMoreListener loadMoreListener;
    //當前頁數,默認從0開始,可以設置默認值
    public int mPage;
    //每頁加載返回的個數,按照這個來判斷是否已經加到頭了
    public int PageSize = 15;
    //默認從第幾頁開始加載
    public int defaultStart;
 
    public LoadMoreBindingViewModel() {
        super();
        loadMoreView = getLoadMoreView();
        loadMoreListener = getLoadMoreListener();
        loadMoreEnd = new ObservableBoolean();
        loadMoreEnable = new ObservableBoolean();
        loadMoreSuccess = new ObservableBoolean();
    }
 
 
    protected LoadMoreView getLoadMoreView() {
        return new LoadMoreView() {
            @Override
            public int getLayoutId() {
                return R.layout.view_load_more;
            }
 
            @Override
            protected int getLoadingViewId() {
                return R.id.load_more_loading_view;
            }
 
            @Override
            protected int getLoadFailViewId() {
                return R.id.load_more_load_fail_view;
            }
 
            @Override
            protected int getLoadEndViewId() {
                return R.id.load_more_load_end_view;
            }
        };
    }
 
    protected BaseQuickAdapter.RequestLoadMoreListener getLoadMoreListener() {
        return new BaseQuickAdapter.RequestLoadMoreListener() {
            @Override
            public void onLoadMoreRequested() {
                loadMore();
            }
        };
    }
 
 
    private void loadMore() {
        CSLog.Print("加載更多調用了");
        load();
    }
 
 
    @Override
    public void load() {
        load(mPage);
    }
 
    @Override
    protected void load(Flowable<List<B>> flowable) {
        if (isRefreshing.get()) {
            emptyResId.set(getEmptyViewRes(EmptyViewType.REFRESH));
        } else {
            CSLog.Print("調用了正在加載界面");
            emptyResId.set(getEmptyViewRes(EmptyViewType.LOADING));
        }
        disposable = flowable.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<List<B>>() {
                    @Override
                    public void accept(List<B> result) throws Exception {
                        setData(result);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        if (isRefreshing.get() || mPage == defaultStart) {
                            emptyResId.set(getEmptyViewRes(EmptyViewType.ERROR));
                            loadMoreEnable.set(true);
                        } else {
                            loadMoreSuccess.set(false);
                            loadMoreSuccess.notifyChange();
                        }
                        isRefreshing.set(false);
                    }
                }, new Action() {
                    @Override
                    public void run() throws Exception {
                        loadMoreEnable.set(true);
                        emptyResId.set(getEmptyViewRes(EmptyViewType.EMPTY));
                        isRefreshing.set(false);
                        mPage++;
                    }
                });
    }
 
    //重新加載,頁數清空
    @Override
    public void reload() {
        mPage = defaultStart;
        super.reload();
    }
 
    //設置默認從第幾頁開始加載
    public void setDefaultStart(int index) {
        defaultStart = index;
        mPage = index;
    }
 
 
    public void setData(List<B> dat) {
        addItems(dat);
        if (dat.size() < getPageSize()) {
            loadMoreEnd.set(mPage == defaultStart);
            loadMoreEnd.notifyChange();
        } else {
            loadMoreSuccess.set(true);
            loadMoreSuccess.notifyChange();
        }
    }
 
    public abstract void load(int mPage);
 
    public int getPageSize() {
        return PageSize;
    }
 
    public void setPageSize(int pageSize) {
        this.PageSize = pageSize;
    }
}
public class LoadMoreLineViewModel extends LoadMoreBindingViewModel<SimpleData> {
    private boolean isFirst = true;
//構造方法中,設置每頁獲取的個數,這個值是用來判斷當前網絡請求,是否是最后一頁的數據
    public LoadMoreLineViewModel() {
        super();
        setPageSize(10);
    }
 
    @Override
    protected Map<Integer, CSBravhItemBinding> getItemBinding() {
        Map<Integer, CSBravhItemBinding> mp = new HashMap<>();
        mp.put(0, new CSBravhItemBinding(com.caesar.brvahbinding.BR.bean, R.layout.item_simple));
        return mp;
    }
//模擬加載數據,模擬加載成功/失敗和結束
    @Override
    public void load(int mPage) {
        CSLog.Print("當前加載的頁:" + mPage);
        if (mPage == 2) {
            if (isFirst) {
                isFirst = false;
                load(getErr());
            } else {
                load(getSimp());
            }
        } else if (mPage == 3) {
            load(getFsa());
        } else {
            load(getSimp());
        }
    }
 
 
    public Flowable<List<SimpleData>> getSimp() {
        return Flowable.create(new FlowableOnSubscribe<List<SimpleData>>() {
            @Override
            public void subscribe(FlowableEmitter<List<SimpleData>> emitter) throws Exception {
                ArrayList<SimpleData> data = new ArrayList<>();
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img1));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img2));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img3));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img2));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img1));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img3));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img2));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img1));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img3));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img2));
                emitter.onNext(data);
                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER).delay(3, TimeUnit.SECONDS);
    }
 
 
    public Flowable<List<SimpleData>> getErr() {
        return Flowable.create(new FlowableOnSubscribe<List<SimpleData>>() {
            @Override
            public void subscribe(FlowableEmitter<List<SimpleData>> emitter) throws Exception {
                emitter.onError(null);
            }
        }, BackpressureStrategy.BUFFER).delay(3, TimeUnit.SECONDS);
    }
 
    public Flowable<List<SimpleData>> getFsa() {
        return Flowable.create(new FlowableOnSubscribe<List<SimpleData>>() {
            @Override
            public void subscribe(FlowableEmitter<List<SimpleData>> emitter) throws Exception {
                ArrayList<SimpleData> data = new ArrayList<>();
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img1));
                data.add(new SimpleData("這貨是個標題", "這貨是個內容加描述", R.mipmap.animation_img2));
                emitter.onNext(data);
                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER).delay(3, TimeUnit.SECONDS);
    }
 
    @Override
    public RecyclerView.ItemDecoration onitemDecoration() {
        return new NormalLineDecoration(30, true);
    }
 
}
 <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:OnRefreshListener="@{vm.refreshListener}"
            app:refreshing="@{vm.isRefreshing}">
 
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_show"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:padding="5dp"
                app:cs_brvah_Decoration="@{vm.itemDecoration}"
                app:cs_brvah_emptyResId="@{vm.emptyResId}"
                app:cs_brvah_emptyClickListener="@{vm.emptyOnClickListener}"
                app:cs_brvah_adapter="@{vm.bindingAdapter}"
                app:cs_brvah_loadMoreEnd="@{vm.loadMoreEnd}"
                app:cs_brvah_loadMoreEnable="@{vm.loadMoreEnable}"
                app:cs_brvah_loadMoreListener="@{vm.loadMoreListener}"
                app:cs_brvah_loadMoreSuccess="@{vm.loadMoreSuccess}"
                app:cs_brvah_loadMoreView="@{vm.loadMoreView}"
 
                />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

好,大家先找到LoadMoreLineActivity界面,仔細的同學就會發現,這邊的viewModel我繼承的是LoadMoreBindingViewModel,而LoadMoreBindingViewModel又是繼承BaseBindingViewModel。

我們先看LoadMoreBindingViewModel,里面,新增加了一些屬性,有LoadMoreView loadMoreView,這個是上拉加載的動畫,可以用戶自己定制,只要重寫getLoadMoreView方法就可以了。bservableBoolean loadMoreEnd這個是判斷是否已經加載結束。其中最關鍵的是新增的屬性mPage,加載更多是有頁數的,所以mPage這個值,就表示當前加載的是第幾頁,默認0,也可以設置從第1頁開始加載。PageSize這個屬性是判斷是否已經加載到尾部,如果你加載回來的個數,小于這個值,就表示已經加載到頭了。defaultStart這個值,是判斷初始是從第幾頁開始加載。

好,接下來我們來看看LoadMoreLineViewModel這個類,其實也簡單,就是一些常規的調用,不過原先重寫的load()回調中,里面加了一個int mpage參數,這個就是當前加載的頁數。然后數據請求加載的方式,跟原先的是一樣的。

最后來看一下布局文件的代碼。下拉刷新控件的綁定我上面已經講過了,這邊就不講了,主要是列表的綁定,如果你是想要你的布局支持加載更多的話,其中,app:cs_brvah_loadMoreEnd="@{vm.loadMoreEnd}" app:cs_brvah_loadMoreEnable="@{vm.loadMoreEnable}" app:cs_brvah_loadMoreListener="@{vm.loadMoreListener}" app:cs_brvah_loadMoreSuccess="@{vm.loadMoreSuccess}"app:cs_brvah_loadMoreView="@{vm.loadMoreView}"

是必須要加的,判斷加載狀態,加載監聽,加載時的動畫等等,直接照寫就完事。

  @BindingAdapter(value = {"cs_brvah_loadMoreEnd"})
    public static void onLoadMoreEnd(RecyclerView recyclerView, ObservableBoolean loadMoreEnd) {
        CSLog.Print("加載結束調用:" + loadMoreEnd.get());
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        if (adapter instanceof BaseQuickAdapter) {
            CSLog.Print("進入加載結束調用:" + loadMoreEnd.get());
            ((BaseQuickAdapter) adapter).loadMoreEnd(loadMoreEnd.get());
        }
    }
 
    @BindingAdapter(value = {"cs_brvah_loadMoreEnable"})
    public static void onLoadMoreEnable(RecyclerView recyclerView, ObservableBoolean loadMoreEnable) {
        CSLog.Print("是否加載更多:" + loadMoreEnable.get());
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        if (adapter instanceof BaseQuickAdapter) {
            CSLog.Print("進入是否加載更多:" + loadMoreEnable.get());
            ((BaseQuickAdapter) adapter).setEnableLoadMore(loadMoreEnable.get());
        }
    }
 
    @BindingAdapter(value = {"cs_brvah_loadMoreSuccess"})
    public static void onLoadMoreSuccess(RecyclerView recyclerView, ObservableBoolean loadMoreSuccess) {
        CSLog.Print("是否加載成功:" + loadMoreSuccess.get());
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        if (adapter instanceof BaseQuickAdapter) {
            CSLog.Print("進入是否加載成功:" + loadMoreSuccess.get());
            if (loadMoreSuccess.get()) {
                ((BaseQuickAdapter) adapter).loadMoreComplete();
            } else {
                ((BaseQuickAdapter) adapter).loadMoreFail();
            }
        }
    }
 
 
    @BindingAdapter(value = {"cs_brvah_emptyResId", "cs_brvah_emptyClickListener"})
    public static void onEmptyView(RecyclerView recyclerView, ObservableInt emptyResId, View.OnClickListener clickListener) {
        CSLog.Print("加載空布局調用");
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        if (adapter instanceof BaseQuickAdapter) {
            if (emptyResId != null) {
                CSLog.Print("進入加載空布局");
                ((BaseQuickAdapter) adapter).setEmptyView(emptyResId.get(), (ViewGroup) recyclerView.getParent());
            }
            if (clickListener != null && ((BaseQuickAdapter) adapter).getEmptyView() != null) {
                CSLog.Print("進入加載空布局監聽事件");
                ((BaseQuickAdapter) adapter).getEmptyView().setOnClickListener(clickListener);
            }
 
        }
    }

上面這些是CSBrvahBindingAdapter中,databinding綁定的代碼。其實歸根到底,還是調用了BRVAH的方法。

哇,又寫了這么多了,那再起一頁吧。
BRVAH的BaseRecyclerViewAdapterHelper與MVVM模式優雅結合(其一)
BRVAH的BaseRecyclerViewAdapterHelper與MVVM模式優雅結合(其三)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容