学习MultiViewAdapter——4

来源:互联网 发布:linux java 打包 编辑:程序博客网 时间:2024/06/05 06:04

本文讲到的关于MultiViewAdapter,是学习国外牛人在GitHub上的开源项目。具体的使用方法可以看Wiki。


今天来说一说最后一块内容DataGroupManagerDataGroupManger可以是带有头的管理器,支持item的折叠与展开,之前讲的关于DataListManager这个主要处理了单个ViewType中数据的问题,库的作者为了方便人们调用添加了支持头的布局添加管理器。这里的“头”不是说“下拉刷新的那个头”。

我们先了解一下RecyclerView.adapter数据刷新的几种方法:

  1. notifyItemChanged(int position) 更新列表上指定position位置的数据
  2. notifyItemInserted(int position) 列表position位置添加一条数据,有动画效果
  3. notifyItemRemoved(int position) 列表position位置移除一条数据,有动画效果
  4. notifyItemMoved(int fromPosition, int toPosition) 列表fromPosition位置的数据移动到toPosition位置,有动画效果
  5. notifyItemRangeChanged(int positionStart, int itemCount)列表从positionStart位置到itemCount数量的列表项进行数据刷新 ,有动画效果
  6. notifyItemRangeInserted(int positionStart, int itemCount) 列表从positionStart位置到itemCount数量的列表项批量添加数据时调用,有动画效果
  7. notifyItemRangeRemoved(int positionStart, int itemCount) 列表从positionStart位置到itemCount数量的列表项批量删除数据时调用,有动画效果

了解了上面关于一些刷新的方法之后,我们就来看看DataGroupManager做了什么。

DataGroupManager

public class DataGroupManager<H, M> extends DataListUpdateManager<M> {    private final DataItemManager<H> headerItemManager;    public DataGroupManager(RecyclerAdapter adapter, H headerItem) {        super(adapter);        headerItemManager = new DataItemManager<>(adapter, headerItem);    }    public DataGroupManager(RecyclerAdapter adapter, H headerItem,                            @NonNull PayloadProvider<M> payloadProvider) {        super(adapter, payloadProvider);        headerItemManager = new DataItemManager<>(adapter, headerItem);    }    //...省略一部分代码     @Override    void onGroupExpansionToggled() {        ...省略一部分代码    }    @Override    M getItem(int dataItemPosition) {        return super.getItem(dataItemPosition - 1);    }    @Override    int size() {        return headerItemManager.size() + (isExpanded ? super.size() : 0);    }     public final boolean add(M item) {        boolean result = add(item, false);        if (result && isExpanded) {            onInserted(getDataList().size(), 1);        }        return result;    }    ...省略了其他添加的方法}

我们看到了DataGroupManager的构造方法中直接就new DataItemManager(),也就是给布局中添加了一个header,然后这个方法中定义了一些数据的添加,删除等方法。这些方法中已经计算了在有header的时候,集合的大小与item中的position的位置关系。那么我们看一下DataItemManager这个方法。

DataItemManager

public final class DataItemManager<M> extends BaseDataManager<M> {    public DataItemManager(RecyclerAdapter baseAdapter) {        super(baseAdapter);    }    public DataItemManager(RecyclerAdapter baseAdapter, M item) {        super(baseAdapter);        getDataList().add(item);    }    public final void setItem(M item) {        if (getDataList().size() == 0) {            getDataList().add(item);            onInserted(0, 1);        } else {            getDataList().set(0, item);            onChanged(0, 1, null);        }    }    public final void removeItem() {        if (getDataList().size() > 0) {            getDataList().clear();            onRemoved(0, 1);        }    }}

很简单,继承BaseDataManager然后在构造方法中,直接把数据添加到集合中。然后提供了添加与删除Item的方法。

那么我们看看在实际中怎么使用的,先上一张效果图:

这里写图片描述

先看看实现这种效果的关键代码(完整代码可在sample中看到):

ExpandableGroupAdapter

@Override    protected void setUpAdapter() {        ExpandableGroupAdapter adapter = new ExpandableGroupAdapter(                new SimpleDividerDecoration(this, SimpleDividerDecoration.VERTICAL));        //设置折叠的模式        adapter.setExpandableMode(RecyclerAdapter.EXPANDABLE_MODE_MULTIPLE);        //设置Group折叠的模式        adapter.setGroupExpandableMode(RecyclerAdapter.EXPANDABLE_MODE_MULTIPLE);        LinearLayoutManager llm = new LinearLayoutManager(getApplicationContext());        recyclerView.addItemDecoration(adapter.getItemDecorationManager());        recyclerView.setLayoutManager(llm);        recyclerView.setAdapter(adapter);        List<Bird> birds = new ArrayList<>();        for (int i = 0; i < 10; i++) {            birds.add(new Bird("Bird " + i));        }        adapter.addBirds(birds);        List<Flower> flowers = new ArrayList<>();        for (int i = 0; i < 10; i++) {            flowers.add(new Flower(i, "Flower " + i));        }        adapter.addFlowers(flowers);        List<String> dataList = new ArrayList<>();        for (int i = 1; i <= 5; i++) {            dataList.add("Item " + i);        }        adapter.addItems(dataList);    }

因为是sample,所以直接就在Activity里面模拟数据并且添加进去,这里面有一个ExpandableGroupAdapter,我们点击去看看这个方法做了什么?

ExpandableGroupAdapter

public class ExpandableGroupAdapter extends RecyclerAdapter        implements BaseViewHolder.OnItemClickListener<Bird> {    private DataGroupManager<Header, Bird> birdsGroup;    private DataGroupManager<Header, Flower> flowerGroup;    private DataGroupManager<Header, String> expandableItemGroup;    public ExpandableGroupAdapter(ItemDecorator dividerDecoration) {        birdsGroup = new DataGroupManager<>(this, new Header("Birds"));        flowerGroup = new DataGroupManager<>(this, new Header("Flowers"));        expandableItemGroup = new DataGroupManager<>(this, new Header("Expandable Items"));        addDataManager(birdsGroup);        addDataManager(flowerGroup);        addDataManager(expandableItemGroup);        registerBinder(new ExpandableHeaderBinder());        registerBinder(new ExpandableItemBinder());        registerBinder(new BirdBinder(dividerDecoration, this));        registerBinder(new FlowerBinder(dividerDecoration));    }    ...省略了为DataGroupManager设置数据的代码}

和实现most typeView holder的写法一样,只不过是多了一个Header,根据效果图,我们需要注意的是有两种显示效果,一种是一个Item有折叠效果,还有一个在Group中有多层折叠的效果,之前说过,点击事件这些方法都在ViewHolder中实现的,所以我们重点来看一下ExpandableHeaderBinder()ExpandableItemBinder()两个类中怎么实现的该效果。

ExpandableItemBinder

public class ExpandableItemBinder extends ItemBinder<String, ExpandableItemBinder.ViewHolder> {    ...省略create,bind,canBindData方法    static class ViewHolder extends BaseViewHolder<String> {        public ViewHolder(View itemView) {            super(itemView);    ...省略找ID的代码            setItemClickListener(new OnItemClickListener<String>() {                @Override                public void onItemClick(View view, String item) {                    toggleItemExpansion();                    ivIndicator.setImageResource(                            isItemExpanded() ? R.drawable.ic_expand_less : R.drawable.ic_expand_more);                }            });        }    }}

ExpandableHeaderBinder

public class ExpandableHeaderBinder extends ItemBinder<Header, ExpandableHeaderBinder.ViewHolder> {   ...省略create,bind,canBindData方法    static class ViewHolder extends BaseViewHolder<Header> {        public ViewHolder(View itemView) {            super(itemView);            ...省略找ID的代码            setItemClickListener(new OnItemClickListener<Header>() {                @Override                public void onItemClick(View view, Header item) {                    toggleGroupExpansion();                    ivIndicator.setImageResource(                            isItemExpanded() ? R.drawable.ic_expand_less : R.drawable.ic_expand_more);                }            });        }    }}

现在发现,这两个ViewHolder中不同的仅仅是toggleItemExpansion();toggleGroupExpansion();一个是设置单个Item点击折叠的效果,另一个是嵌套Group里面的。实现这两个方法的地方在CoreRecyclerAdapter

//用来存储是否是折叠或者展开的状态final SparseBooleanArray expandedItems = new SparseBooleanArray(); private void onItemExpansionToggled(int adapterPosition) {        Log.w("jinzetao", "adapterPosition=" + adapterPosition);        switch (expandableMode) {            //只能展开一个Item,另一个Item会收起来            case EXPANDABLE_MODE_SINGLE:                if (lastExpandedIndex != -1) {                    //折叠                    expandedItems.put(lastExpandedIndex, false);                    getDataManager(lastExpandedIndex).onItemExpansionToggled(                            getItemPositionInManager(lastExpandedIndex));                }                if (lastExpandedIndex == adapterPosition) {                    //重置                    lastExpandedIndex = -1;                    return;                }                    //展开                expandedItems.put(adapterPosition, true);                //获得当前的数据Manager然后调用onChanged(position, 1, null);                // getItemPositionInManager(adapterPosition)这里是屏幕显示的position,每个ViewType都是从0开始                getDataManager(adapterPosition).onItemExpansionToggled(                        getItemPositionInManager(adapterPosition));                lastExpandedIndex = adapterPosition;                break;            //多个Item都会展开            case EXPANDABLE_MODE_MULTIPLE:                expandedItems.put(adapterPosition, !expandedItems.get(adapterPosition, false));                getDataManager(adapterPosition).onItemExpansionToggled(                        getItemPositionInManager(adapterPosition));                break;            case EXPANDABLE_MODE_NONE:            default:                break;        }    }    private void onGroupExpansionToggled(int adapterPosition) {        switch (groupExpandableMode) {            case EXPANDABLE_MODE_SINGLE:                if (lastExpandedIndex == adapterPosition) {                    return;                }                if (lastExpandedIndex != -1) {                    getDataManager(lastExpandedIndex).onGroupExpansionToggled();                }                getDataManager(adapterPosition).onGroupExpansionToggled();                lastExpandedIndex = adapterPosition;                break;            case EXPANDABLE_MODE_MULTIPLE:                expandedItems.put(adapterPosition, !expandedItems.get(adapterPosition, false));                justGetDataManager(adapterPosition).onGroupExpansionToggled();                break;            case EXPANDABLE_MODE_NONE:            default:                break;        }    }

Item与GroupItem在实现上基本一样,只是GroupItem把内部Item折叠的方法交给了子类去处理,需要用户自己实现。单个的Item中,将会更新expandedItems的状态,这个状态决定着需要折叠的数据是否显示,最后调用BaseManager中的onItemExpansionToggled,去刷新界面。简单的关于header的添加就说到这里,然后我们在说一下怎么添加“脚”也就是“加载更多”。

InfiniteLoadingHelper

这个类简单的封装了需要加载更多的逻辑,也是把加载更多这个当做了一个新的Type来处理的。和创建一个普通的ItemBinder一样,只不过多了一个关于RecyclerView滑动监听的接口,在计算出当前是最后一个position的时候,然后加载下一页。加载完成以后,然后将这个Item 移除掉。完整的代码可以在sample中找到。

public abstract class InfiniteLoadingHelper {    ...省略一部分代码    public InfiniteLoadingHelper(@LayoutRes int layoutId, int totalPageCount) {        this.itemBinder = new InfiniteLoadingBinder(layoutId);        this.totalPageCount = totalPageCount;        this.infiniteScrollListener = new InfiniteScrollListener(this);    }    public ItemBinder<String, ItemViewHolder<String>> getItemBinder() {        return itemBinder;    }    @RestrictTo(RestrictTo.Scope.LIBRARY)    public void setDataItemManager(DataItemManager<String> dataItemManager) {        canLoadMore = true;        this.dataItemManager = dataItemManager;    }      public void markCurrentPageLoaded() {        isLoading = false;        if (!canLoadMore) {            completeLoading();        }    }    private void loadNextPage() {        isLoading = true;        onLoadNextPage(currentPage++);        if (currentPage == totalPageCount) {            canLoadMore = false;        }    }    ...省略一部分代码     private static class InfiniteLoadingBinder extends ItemBinder<String, ItemViewHolder<String>> {         ...省略一部分代码     } private static class InfiniteScrollListener extends RecyclerView.OnScrollListener {        private final InfiniteLoadingHelper loadingHelper;        InfiniteScrollListener(InfiniteLoadingHelper loadingHelper) {            this.loadingHelper = loadingHelper;        }        @Override        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {            if (dy > 0 && !loadingHelper.isLoading && loadingHelper.canLoadMore) {                int totalItemCount = recyclerView.getLayoutManager().getItemCount();                int lastVisibleItemPosition = 0;                //对瀑布流的视图进行了特殊的处理                if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {                    int[] lastVisibleItemPositions =                            ((StaggeredGridLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPositions(                                    null);                    lastVisibleItemPosition = lastVisibleItemPositions[lastVisibleItemPositions.length - 1];                } else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {                    lastVisibleItemPosition =                            ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();                }                if (lastVisibleItemPosition + 1 >= totalItemCount) {                    loadingHelper.loadNextPage();                }            }        }    }}

那么使用的时候,传给InfiniteLoadingHelper布局,count,然后设置滑动监听,就可以用了。

infiniteLoadingHelper = new InfiniteLoadingHelper(R.layout.item_loading_footer, 10) {            @Override            public void onLoadNextPage(int page) {                currentPage = page + 1;                handler.postDelayed(runnable, 2000);            }        };        adapter.setInfiniteLoadingHelper(infiniteLoadingHelper);        recyclerView.addOnScrollListener(infiniteLoadingHelper.getScrollListener());        recyclerView.addItemDecoration(adapter.getItemDecorationManager());        recyclerView.setLayoutManager(llm);        recyclerView.setAdapter(adapter);        loadData();private void loadData() {        List<Flower> flowers = new ArrayList<>();        for (int i = 1; i <= 10; i++) {            flowers.add(new Flower(i, "Flower " + (i + currentPage * 10)));        }        adapter.addFlowers(flowers);        infiniteLoadingHelper.markCurrentPageLoaded();    }

效果图如下:

这里写图片描述

好了,这个库就说到这里了,学到的东西也不少。

  • 解耦Adapter,让每个ViewHolder实现重复使用。(这个我觉得是业务逻辑的问题)
  • 不写if-else实现多ViewType
  • 封装ItemDecorator让每个ItemBinder都能设置自己的
  • DiffUtil的使用
  • Item的一些常用逻辑的封装
  • 一些注解的使用

连续下了一个星期的冰雨,外面的温度降到了12摄氏度。
于是人们都穿起了厚厚的羽绒衣。
打代码的时候,连胳膊都抬不起来了。
ヾ(。ꏿ﹏ꏿ)ノ゙

原创粉丝点击