Adapter的封装使用

    在开发中不管是LitView还Recycleview都需要设置item,但是很多时候item不是固定,而是根据服务器的返回的数据动态配置的在一个Recycleview中肯能有多种形式,例如正文形式,图集形式,短视频形式,文本形式,以及广告插入,专题、通栏、banner图、图布局和根布局等多种形式。有的还需要下拉刷新、上拉加载等定义的形式等等。本篇文章将围绕着这些问题而展开,封装一个强大的Adapter来适配Adapter的各种配置,支持下拉刷新、上拉加载,支持加载中布局,加载更多布局,加载失败布局,加载空数据布局。



理念

    不管是头布局,脚布局,图文布局,还是轮播图等,把每一种类型的布局都看作一个Item的类型,这样,每次添加一个内容的时候都可以把它当作Recycleview的一个Item,只需要给数据集合中添加一个Item,然后刷新数据,这个添加根据服务器返回数据是动态添加,因此可以动态适配各种布局类型。


分析

    在Adapter中有三大核心类:

  • getItemViewType : 根据position返回的一个int值,代表该position下的ViewHolder类型
  • onCreateViewHolder :创建对应ViewType的viewholder
  • onBinderViewHolder :绑定数据到对应的ViewHolder
    因此封装也是围绕着这三大核心类而展开。

Item中方法抽取

    每个Item当作一种类型的布局就需要把他们共有的方法进行向上抽取,把getItemType,onCreateViewHolder,onBinderViewHolder抽取为接口。
    /**
     * item中每个条目的必要方法
     */
    public interface Item {

        /**
         * 获取viewType
         * @return
         */
        public  int getItemType();

        /**
         * 创建ViewHolder
         * @param parent
         * @param viewType
         * @return
         */
        public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

        /**
         * 数据绑定
         * @param holder
         * @param position
         */
        public  void onBindViewHolder(BaseViewHolder holder, int position);
    }


创建基类BaseItem

    基类中主要是为了让其他类进行继承实现一些基本的共有操作,需要实现Item接口。这里主要有两点:

    1、构造方法中传递数据参数,有参构造表示服务器返回数据集合,提供给子类绑定数据使用,无参构造表示该item类型布局中不需要参数,即静态布局。
    2、创建onCreateViewHolder中的布局,交个给子类加载每种布局。
        public abstract class BaseItem<T> implements Item {
            public T data;

            public BaseItem(T data) {
                this.data = data;
            }
            public BaseItem() {
            }

            @Override
            public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View view =  LayoutInflater.from(parent.getContext()).inflate(inflateViewId(), parent, false);
                return new BaseViewHolder(view);
            }

            @Override
            public void onBindViewHolder(BaseViewHolder holder, int position) {

            }

            /**
             * 创建viewholder的View
             */
            public abstract int inflateViewId();
        }
    3、子类实现

    如果子类为静态固定布局,则使用无参构造,否则使用有参构造。如下静态布局和动态布局:

    public class FooterItem  extends BaseItem<DataBean.ResultBean.ContentBean>{

        @Override
        public int getItemType() {
            return HolderType.FooterItem.getValue();
        }


        @Override
        public void onBindViewHolder(BaseViewHolder holder, int position) {

        }

        @Override
        public int inflateViewId() {
            return R.layout.item_list_footer;
        }
    }
    public class TextItem extends BaseItem<DataBean.ResultBean.ContentBean> {
        public TextItem(DataBean.ResultBean.ContentBean data) {
            super(data);
        }

        @Override
        public int getItemType() {
            return HolderType.TextItem.getValue();
        }

        @Override
        public void onBindViewHolder(BaseViewHolder holder, int position) {
            holder.setText(R.id.text_content2,data.getText());
        }

        @Override
        public int inflateViewId() {
            return R.layout.item_list_content2;
        }
    }

BaseViewHolder封装

    ViewHolder主要用来承载每个item布局中的控件的绑定和数据的设置。这里进行了基本的常用数据绑定的封装,如:获取布局,获取图片,设置文字等,设置图片统一使用Picasso进行设置,便于管理。

    1、构造方法
        public BaseViewHolder(View itemView) {
            super(itemView);
            views = new SparseArray<>();
            mItemView = itemView;
        }
    2、View获取
         public View getItemView() {
             return mItemView;
         }
    3、获取控件
        public View getView(int resId) {
            return retrieveView(resId);
        }
        private <T extends View> T retrieveView(int viewId) {
            View view = views.get(viewId);
            if (view == null) {
                view = mItemView.findViewById(viewId);
                views.put(viewId, view);
            }
            return (T) view;
        }
    4、图片设置
        public void setImageUrl(int resId, String url) {
            ImageView imageView = getImageView(resId);
            if (imageView == null) {
                throw new ExceptionInInitializerError("ImageView id is not exist");
            }
            Picasso.with(mItemView.getContext()).load(url).into(imageView);
        }
     5、文字设置
         public void setText(int resId, CharSequence text) {
             getTextView(resId).setText(text);
         }


BaseAdapter

    BaseAdapter继承自RecyclerView.Adapter<BaseViewHolder>,重写了onCreateViewHolder、onBindViewHolder、getItemCount、getItemViewType四个方法,提供了数据设置的方法和集合操作的方法,包括设置数据、获取数据、添加集合、移除集合等等。

  • onCreateViewHolder:返回每个item的ViewHolder
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        for (int i = 0; i < getItemCount(); i++) {
            if (viewType == mData.get(i).getItemType()) {
                return mData.get(i).onCreateViewHolder(parent, viewType);
            }
        }
        throw new RuntimeException("onCreateViewHolder viewType is wrong");
    }
  • onBindViewHolder:对每个Item进行数据的绑定
    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        mData.get(position).onBindViewHolder(holder, position);
    }
  • getItemViewType:返回对应的Item的数据类型
    @Override
    public int getItemViewType(int position) {
        return mData.get(position).getItemType();
    }

SimpleAdapter

    以上内容基本上封装了Adapter的操作,能够适应各种item的要求,为了更加完美,加上加载中,数据为空,数据错误,加载更多四中item,都继承自BaseItem。SimpleAdapter继承自BaseAdapter,在构造方法中初始化了四个布局,提供了一系列显示和隐藏的方法,详情看源码,在使用的时候继承自他就可以。


下拉刷新

    下拉刷新使用Google原生控件SwipeRefreshLayout,需要Recycleview的父控件设置为SwipeRefreshLayout,就可以实现下拉刷新效果。

    1、进度条的颜色设置

    setColorSchemeResources(@ColorRes int... colorResIds)

    2、下拉监听

    mRefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
           @Override
           public void onRefresh() {
               //停止刷新进度
               mRefreshlayout.setRefreshing(false);
               //执行下拉刷新操作

           }
       });


上拉加载

    上拉加载用于实现分页的功能,数据的加载不是一次性的,而是分条加载的,当Recycleview下拉到底侧的时候,就会显示加载更多再次请求数据用于显示。在RecyclerView中,提供了一个监听接口:RecyclerView.OnScrollListener我们可以通过这个接口来创建我们上拉加载更多的功能。他提供了两个方法:onScrolled,onScrollStateChanged。

    1、onScrolled

    当Recycleview滑动的时候调用,dx表示x轴滑动的距离,dy表示y轴滑动的距离,可以根据dy判断是否是底部向上拉取操作。 

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        //手指正向上滚动
        if (dy > 0) {
            isSlidingToLast = true;
        } else {
            isSlidingToLast = false;
        }
    }

    2、onScrollStateChanged

    Recycleview状态变化时候回调,一共三个状态:

  • SCROLL_STATE_IDLE:当前的recycleView不滑动(滑动已经停止时)
  • SCROLL_STATE_DRAGGING: 当前的recycleView被拖动滑动
  • SCROLL_STATE_SETTLING:当前的recycleView滚动到某个位置的过程,但没有被触摸滚动

    (1)获取最后一个可见的item位置

    int lastVisibleItem =mLayoutManager().findLastVisibleItemPosition();

    (2)获取Recycleview所有的item个数

    int itemCount = mLayoutManager.getItemCount();

     (3)条件判断

    最后一个可见的item位置 = 总item数 -1

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        //如果为null,第一次运行,确定布局类型
        int mLastVisiblePosition = -1;
        if (layoutManager instanceof LinearLayoutManager) {
            mLastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
        } else if (layoutManager instanceof GridLayoutManager) {
            mLastVisiblePosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
            int[] lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
            staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
            mLastVisiblePosition = findMax(lastPositions);
        } else {
            throw new RuntimeException("LayoutManager should be init");
        }

        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        int itemCount = manager.getItemCount();

        if (newState == RecyclerView.SCROLL_STATE_SETTLING && mLastVisiblePosition == itemCount - 1
                && isSlidingToLast && canShowLoadMore()) {
            //展示加载更多进度条
            showLoadMore();
            //进行加载更多分页数据请求
            onLoadingMore();
        }
    }


BaseFragment

    BaseFragment主要对fragment进行封装,包含了下拉刷新上拉加载,数据处理的逻辑,使用起来更加方便,子类子需要继承他,重写他的抽象方法就可以了,不需要额外重写其他生命周期的方法。

    public class MyFragment extends BaseFragment<DataBean.ResultBean.ContentBean> {
        /**
         * 下拉刷新逻辑处理
         */
        @Override
        protected void onPullRefresh() {

        }

        /**
         * 上拉加载逻辑处理
         */
        @Override
        protected void onLoadingMore() {

        }

        /**
         * 初始化数据,访问网络,网络请求
         */
        @Override
        protected void initData() {

        }

        /**
         * 对网络请求的数据进行处理,返回Adapter所有要传递参数的集合
         * @param list
         * @return
         */
        @Override
        protected List<Item> getItem(List<DataBean.ResultBean.ContentBean> list) {
            return null;
        }
    }

源码和demo地址:https://github.com/yoonerloop/ConfigItemAdapter点击打开链接

测试服务端json数据:

{
    "code": "200",
    "msg": "success",
    "result": {
        "banner": {
            "info": "年底大促销",
            "url": [
                "https://www.baidu.com/img/bd_logo1.png",
                "https://www.baidu.com/img/bd_logo1.png",
                "https://www.baidu.com/img/bd_logo1.png"
            ]
        },
        "content": [
            {
                "des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
                "image": "http://img.taopic.com/uploads/allimg/120727/201995-120HG1030762.jpg",
                "type": "1"
            },
            {
                "des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
                "image": "http://img02.tooopen.com/images/20150507/tooopen_sy_122395947985.jpg",
                "type": "1"
            },
            {
                "text": "亚瑟从出生开始就被尤瑟王托付给魔法师梅林抚养,梅林便偷偷将襁褓中的亚瑟带离廷塔杰尔城堡,来到一个隐秘的地方将亚瑟抚养成人,亚瑟王与梅林关系十分良好,传说中梅林可以随意改变自己容貌的年轻或年老,不过他大部分时间都以年迈的老者示人。",
                "type": "2"
            },
            {
                "des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
                "image": "http://img07.tooopen.com/images/20170316/tooopen_sy_201956178977.jpg",
                "type": "1"
            },
            {
                "type": "3",
                "url": [
                    "https://www.baidu.com/img/bd_logo1.png",
                    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320357&di=49be8eb330e4cafb461b337cecba1e2c&imgtype=0&src=http%3A%2F%2Fpic21.nipic.com%2F20120525%2F7512072_140351788322_2.jpg",
                    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320352&di=671e9cbfeeffcb20af986fe7dee91af5&imgtype=0&src=http%3A%2F%2Fdown1.sucaitianxia.com%2Fpsd02%2Fpsd181%2Fpsds40246.jpg",
                    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320352&di=671e9cbfeeffcb20af986fe7dee91af5&imgtype=0&src=http%3A%2F%2Fdown1.sucaitianxia.com%2Fpsd02%2Fpsd181%2Fpsds40246.jpg"
                ]
            }
        ]
    }
}



猜你喜欢

转载自blog.csdn.net/yoonerloop/article/details/79267341