App实战:多类型列表写法(RecyclerView)

多类型列表写法(RecyclerView)

还是先上个图

主要实现功能:

  • 一个列表,多种类型展示
  • 上拉加载,有三种底部布局:加载中、加载失败、全部加载
  • 下拉刷新,会有一个提示刷新完成动画

多类型

主要是Adapter中的写法,继承RecyclerView.Adapter复写四个方法:

//创建viewholder,主要是做一些findview的操作
onCreateViewHolder
//绑定数据,把item传递给holder
onBindViewHolder
//多类型的关键,返回的是item的类型
getItemViewType
//返回列表中item的个数
getItemCount

getItemViewType

技巧:把itemViewType跟对象绑定,并且用布局文件id来代替。要注意的是有一个足布局FooterView:

@Override
public int getItemViewType(int position)
{
    //当position处于底部时,足布局显示
    if (position == mData.size())
    {
        return TYPE_FOOTER;
    }
    return mData.get(position).getItemType(mTypeFactory);
}

getItemCount

照顾到有一个足布局:

@Override
public int getItemCount()
{
    //当mData为空集合时,不展示footerView
    return mData == null || mData.isEmpty() ? 0 : mData.size() + 1;
}

onCreateViewHolder

上面有提到将布局文件id传递给viewType:

@NonNull
@Override
public BaseMultiHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
    View itemView = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
    //足布局单独处理
    if (viewType == TYPE_FOOTER)
    {
        return new FooterHoder(itemView);
    }
    //mTypeFactory不多讲了,可以参考代码
    return mTypeFactory.createViewHolder(itemView, viewType);
}

onBindViewHolder

把每一个item的数据传递给holder,其中需要对足布局进行单独处理:

@Override
public void onBindViewHolder(@NonNull BaseMultiHoder holder, int position)
{
    mHolder = holder;
    if (position < mData.size())
    {
        holder.bind(mData.get(position));
    } else
    {
        switch (currentState)
        {
            case LOADING:
                ((FooterHoder) holder).showLoad();
                break;
            case LOAD_COMPLETE:
                ((FooterHoder) holder).showComplete();
                break;
            case LOAD_FAILED:
                ((FooterHoder) holder).showFailed();
                break;
            default:

                break;
        }
    }
}

上拉加载

亮点利用Rxjava实现上拉加载,PublishProcessor来做订阅和发布。

private PublishProcessor paginator = PublishProcessor.create();

private void subscribeForData()
    {
        mSwipeRefreshLayout.setRefreshing(true);
        Disposable disposable = paginator
                //如果消费者无法处理数据,则 onBackpressureDrop 就把该数据丢弃了。
                //Read more: http://blog.chengyunfeng.com/?p=981#ixzz5Jd08zeDx
                .onBackpressureDrop()
                //顺序执行
                .concatMap(new Function>>()
                {
                    @Override
                    public Publisher> apply(Integer page) throws Exception
                    {
                        loading = true;
                        return dataFromNetword(page);
                    }
                }).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer>()
                {
                    @Override
                    public void accept(final List multiItems) throws Exception
                    {
                        //page=1时,需要清空数据源
                        if (pageNumber == 1)
                        {
                            mMultiAdapter.clear();
                            refreshTvAnim();
                        }
                        loading = false;
                        mMultiAdapter.addItems(multiItems);
                        if (mSwipeRefreshLayout.isRefreshing())
                        {
                            mSwipeRefreshLayout.setRefreshing(false);
                        }
                    }
                }, new Consumer()
                {
                    @Override
                    public void accept(Throwable throwable) throws Exception
                    {
                        //注意当发生onError时,onNext就不在起作用了。这个时候需要重新subscribe
                        Toast.makeText(App.getAppContext(), throwable.getMessage(), Toast.LENGTH_SHORT).show();
                    }
                });
        compositeDisposable.add(disposable);
        paginator.onNext(pageNumber);
    }

订阅好了之后,每次触发上拉的时候执行paginator.onNext(pageNumber);。上拉触发条件:recyclerview滑动到底部。

private void setUpLoadMoreListener()
{
    mRecyvlerView.addOnScrollListener(new RecyclerView.OnScrollListener()
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            super.onScrolled(recyclerView, dx, dy);
            mTotalItemCount = mManager.getItemCount();
            mLastVisibleItemPosition = mManager.findLastVisibleItemPosition();
           //当不在加载,且最后一个可见position+预见大于或等于总item时
            if (!loading && mTotalItemCount <= (mLastVisibleItemPosition + PRE_VISIBLE))
            {
                //当足布局状态是loading时
                if (mMultiAdapter.getCurrentState() == MultiAdapter.LOADING)
                {
                    pageNumber++;
                    //加载数据,PublishProcessor发送数据。
                    //我们可以根据这个特性来实现RxBus。下篇博客再做记录。
                    paginator.onNext(pageNumber);
                    loading = true;
                }
            }
        }
    });
}

下拉刷新,动画

可以首先来看下我的布局文件xml:


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyvler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    android.support.v4.widget.SwipeRefreshLayout>

    //这个就是那个刷新完成的控件,其实就是一个textview
    <TextView
        android:id="@+id/refresh_com_tv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="刷新完成"
        android:textColor="@color/white"
        android:textSize="14sp" />
FrameLayout>

动画实现方式我采用的是通过改变textview的高度来实现。有一个展开和一个收缩动画,中间有一个1s的间隔时间。

/**
 * 刷新完成动画
 */
private void refreshTvAnim()
{
    final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mRefreshComTv.getLayoutParams();

    //展开动画
    ValueAnimator animator = ValueAnimator.ofInt(0, 105).setDuration(500);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
    {
        @Override
        public void onAnimationUpdate(ValueAnimator animation)
        {
            int animatedValue = (int) animation.getAnimatedValue();
            layoutParams.height = animatedValue;
            mRefreshComTv.setLayoutParams(layoutParams);
        }
    });
    animator.addListener(new AnimatorListenerAdapter()
    {
        @Override
        public void onAnimationEnd(Animator animation)
        {
            super.onAnimationEnd(animation);
            Flowable.just(1).delay(1, TimeUnit.SECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer()
                    {
                        @Override
                        public void accept(Integer integer) throws Exception
                        {
                            //收缩动画
                            ValueAnimator shrinkAnim = ValueAnimator.ofInt(105, 0).setDuration(500);
                            shrinkAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
                            {
                                @Override
                                public void onAnimationUpdate(ValueAnimator animation)
                                {
                                    int animatedValue = (int) animation.getAnimatedValue();
                                    layoutParams.height = animatedValue;
                                    mRefreshComTv.setLayoutParams(layoutParams);
                                }
                            });
                            shrinkAnim.start();
                        }
                    });
        }
    });
    animator.start();
}

总结

  • DiffUtil工具使用
  • 接口编程
  • Rxjava中PublishProcessor使用
  • 更改LayoutParams来实现动画
  • recyclerview的多类型一次的复习
  • 设计模式:工厂模式、策略模式

参考

https://medium.com/@ruut_j/a-recyclerview-with-multiple-item-types-bce7fbd1d30e

Github Demo如果对你有帮助,请star吧。

上一篇博客Android Studio一些使用技巧

你可能感兴趣的:(App实战:多类型列表写法(RecyclerView))