zoukankan      html  css  js  c++  java
  • [Android]使用RecyclerView替代ListView(二)

    以下内容为原创,转载请注明:

    来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4242541.html

    以前写过一篇“[Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染http://www.cnblogs.com/tiantianbyconan/p/3992843.html)”,用于在有很多不同类型不同布局的item的时候怎么去较好的进行view的绑定和数据的渲染,但是这个是针对ListView写的。这次我针对RecyclerView也重新实现了一遍。

    接下来演示下怎么去渲染不同类型的item,并且使它支持下拉刷新,滚动到底部显示加载进度条显示。

    以下所有的封装都在AndroidBucket项目中:https://github.com/wangjiegulu/AndroidBucket/tree/master/src/com/wangjie/androidbucket/support/recyclerview

     

    使用的方式如下:

     1 final View footerView = LayoutInflater.from(context).inflate(R.layout.recycler_view_item_type_footer, null);
     2 //        不知道为什么在xml设置的“android:layout_width="match_parent"”无效了,需要在这里重新设置
     3         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
     4         footerView.setLayoutParams(lp);
     5 
     6         recyclerView.setHasFixedSize(true);
     7 
     8         final ABaseLinearLayoutManager layoutManager = new ABaseLinearLayoutManager(context);
     9         layoutManager.setOnRecyclerViewScrollLocationListener(recyclerView, new OnRecyclerViewScrollLocationListener() {
    10             @Override
    11             public void onTopWhenScrollIdle(RecyclerView recyclerView) {
    12                 Logger.d(TAG, "onTopWhenScrollIdle...");
    13             }
    14 
    15             @Override
    16             public void onBottomWhenScrollIdle(RecyclerView recyclerView) {
    17                 Logger.d(TAG, "onBottomWhenScrollIdle...");
    18                 footerView.setVisibility(View.VISIBLE);
    19                 ThreadPool.go(new Runtask<Object, Object>() {
    20                     @Override
    21                     public Object runInBackground() {
    22                         ABThreadUtil.sleep(3000);
    23                         return null;
    24                     }
    25 
    26                     @Override
    27                     public void onResult(Object result) {
    28                         super.onResult(result);
    29                         refreshLayout.setRefreshing(false);
    30                         footerView.setVisibility(View.GONE);
    31                     }
    32                 });
    33             }
    34         });
    35         layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    36         layoutManager.getRecyclerViewScrollManager().addScrollListener(recyclerView, new OnRecyclerViewScrollListener() {
    37             @Override
    38             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    39             }
    40 
    41             @Override
    42             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    43                 refreshLayout.setEnabled(layoutManager.findFirstCompletelyVisibleItemPosition() == 0);
    44             }
    45         });
    46         recyclerView.setLayoutManager(layoutManager);
    47 
    48         initData();
    49 
    50         adapter = new PersonTypeAdapter(context, personList, null, footerView);
    51         adapter.setOnRecyclerViewListener(this);
    52         recyclerView.setAdapter(adapter);
    53 
    54         refreshLayout.setColorSchemeColors(Color.BLUE, Color.RED, Color.YELLOW, Color.GREEN);
    55         refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    56             @Override
    57             public void onRefresh() {
    58                 ThreadPool.go(new Runtask<Object, Object>() {
    59                     @Override
    60                     public Object runInBackground() {
    61                         ABThreadUtil.sleep(3000);
    62                         return null;
    63                     }
    64 
    65                     @Override
    66                     public void onResult(Object result) {
    67                         super.onResult(result);
    68                         refreshLayout.setRefreshing(false);
    69                         footerView.setVisibility(View.GONE);
    70                     }
    71                 });
    72             }
    73         });

    如上述代码:

    Line1:从布局文件中inflate出一个View实例,这个View实例,下面会被用于作为下载提示的footer。

    Line8:生成一个ABaseLinearLayoutManager实例,显然这个类是继承LinearLayoutManager之后扩展的,详见:https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/layoutmanager/ABaseLinearLayoutManager.java,这个类对滑动的监听进行了扩展,可以监听滑动到顶部或者底部的事件

    Line9~34:设置滑动到顶部或底部的监听器,然后一旦滑动到底部则加载更多数据。

    Line36~45:也是设置滑动监听器,滑动过程中如果不是处在第一个item,如果是,则就可以下拉使用SwipeRefreshLayout进行刷新,如果不是则,仅用SwipeRefreshLayout。之所以需要做这个处理,是因为Google事件处理的一个bug--。

    Line50:使用了一个PersonTypeAdapter,这个类继承了ABRecyclerViewTypeExtraViewAdapter这个类继承RecyclerView.Adapter实现了对不同type渲染数据的封装。

    接下来重点分析下ABRecyclerViewTypeExtraViewAdapter这个类(这个类在平常使用时不需要关注):

      1 /**
      2  * Author: wangjie
      3  * Email: tiantian.china.2@gmail.com
      4  * Date: 1/22/15.
      5  */
      6 public abstract class ABRecyclerViewTypeExtraViewAdapter extends RecyclerView.Adapter<ABRecyclerViewTypeExtraHolder> {
      7     private static final int TYPE_HEADER_VIEW = 0x7683;
      8     private View headerView;
      9     private static final int TYPE_FOOTER_VIEW = 0x7684;
     10     private View footerView;
     11     private int extraCount;
     12 
     13     protected ABRecyclerViewTypeExtraViewAdapter(View headerView, View footerView) {
     14         this.headerView = headerView;
     15         this.footerView = footerView;
     16         extraCount += hasHeaderView() ? 0 : 1;
     17         extraCount += hasFooterView() ? 0 : 1;
     18     }
     19 
     20     public boolean hasHeaderView() {
     21         return null != headerView;
     22     }
     23 
     24     public boolean hasFooterView() {
     25         return null != footerView;
     26     }
     27 
     28     public int innerPositionToRealItemPosition(int innerPosition) {
     29         return hasHeaderView() ? innerPosition - 1 : innerPosition;
     30     }
     31 
     32     @TargetApi(Build.VERSION_CODES.DONUT)
     33     @Override
     34     public ABRecyclerViewTypeExtraHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
     35         ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = getAdapterTypeRender(viewType);
     36         ABRecyclerViewTypeExtraHolder holder = render.getReusableComponent();
     37         holder.itemView.setTag(R.id.ab__id_adapter_item_type_render, render);
     38         render.fitEvents();
     39         return holder;
     40     }
     41 
     42     @TargetApi(Build.VERSION_CODES.DONUT)
     43     @Override
     44     public void onBindViewHolder(ABRecyclerViewTypeExtraHolder holder, int innerPosition) {
     45         ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> render = (ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder>) holder.itemView.getTag(R.id.ab__id_adapter_item_type_render);
     46         /**
     47          * 计算该item在list中的index(不包括headerView和footerView)
     48          */
     49         int realItemPosition = innerPositionToRealItemPosition(innerPosition);
     50         render.fitDatas(realItemPosition);
     51         /**
     52          * 重新设置item在list中的index(不包括headerView和footerView)
     53          */
     54         holder.setRealItemPosition(realItemPosition);
     55     }
     56 
     57     /**
     58      * 通过类型获得对应的render(不包括headerView和footerView)
     59      *
     60      * @param type
     61      * @return
     62      */
     63     public abstract ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRenderExcludeExtraView(int type);
     64 
     65     /**
     66      * 获取item的数量(不包括headerView和footerView)
     67      *
     68      * @return
     69      */
     70     public abstract int getItemCountExcludeExtraView();
     71 
     72     /**
     73      * 通过realItemPosition得到该item的类型(不包括headerView和footerView)
     74      *
     75      * @param realItemPosition
     76      * @return
     77      */
     78     public abstract int getItemViewTypeExcludeExtraView(int realItemPosition);
     79 
     80     public ABAdapterTypeRender<ABRecyclerViewTypeExtraHolder> getAdapterTypeRender(int type) {
     81         switch (type) {
     82             case TYPE_HEADER_VIEW:
     83                 return new ABRecyclerViewTypeExtraRender(headerView);
     84             case TYPE_FOOTER_VIEW:
     85                 return new ABRecyclerViewTypeExtraRender(footerView);
     86             default:
     87                 return getAdapterTypeRenderExcludeExtraView(type);
     88         }
     89     }
     90 
     91     @Override
     92     public int getItemCount() {
     93         return getItemCountExcludeExtraView() + extraCount;
     94     }
     95 
     96     @Override
     97     public int getItemViewType(int innerPosition) {
     98         if (null != headerView && 0 == innerPosition) { // header
     99             return TYPE_HEADER_VIEW;
    100         } else if (null != footerView && getItemCount() - 1 == innerPosition) { // footer
    101             return TYPE_FOOTER_VIEW;
    102         }
    103         return getItemViewTypeExcludeExtraView(innerPositionToRealItemPosition(innerPosition));
    104     }
    105 }

    如上述代码所示:

    因为我们的需求是需要添加“加载进度条”,所以需要像ListView那样,添加一个FooterView。可是坑爹的是,RecyclerView不提供addheaderView()和addFooterView()方法,所以只能我们自己去实现了,方法当然是使用不同type来区分类型。虽然headerView这里没有用到,但是也顺带实现下好了。

    这里我们使用的Holder是ABRecyclerViewTypeExtraHolder,这个类待会分析。

    headerView和footerView这里使用构造方法传入,并缓存headerView和footerView。在onCreateViewHolder中,我们通过不同type,生成相应的render。并把render绑定到holder的itemView上面,因为既然现在复用的是holder,那我的render也要实现复用的话,也绑定在holder里面吧。然后调用render的fitEvents方法,来实现render里面的事件绑定。

    onBindViewHolder方法中,通过holder,取出render,然后注意Line49~54,里面执行了innerPositionToRealItemPosition()方法对innerPosition到RealItemPosition的转换,这里的innerPosition代表内部的position,因为这里可能会添加了headerView,一旦添加了headerView,那position跟数据源List中的index就不匹配了,这样的话绑定点击事件后,通过holder.getPositon()得到的position就不是index了,所以不能写“list.get(position)...”了。这里的realItemPosition就代表数据源对应的index 。所以我们要把innerPosition转换为realItemPosition,方法很简单,innerPosition-1=realItemPosition(这里的减去1实际上就是减去了headerView)。其实holder中本来就缓存了当前使用了这个holder的item的position,但是因为有了headerView的存在,position就不等于realItemPosition了,所以我们还需要缓存realItemPosition。所以代码Line46~54诞生了。

    getItemCountExcludeExtraView()方法需要子类实现,返回数据源中的数据数量,然后再加上extraCount即是getItemCount的值。

    getItemViewType()方法先执行了header类型和footer类型的逻辑,然后再让自类去实现getItemViewTypeExcludeExtraView()来执行其他类型的逻辑。

    至于ABRecyclerViewTypeExtraRenderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraRender.java

    部分的实现可以查看

    [Android]使用AdapterTypeRender对不同类型的item数据到UI的渲染http://www.cnblogs.com/tiantianbyconan/p/3992843.html

    实现的原理大同小异了。

    然后分析下ABRecyclerViewTypeExtraHolderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/extra/ABRecyclerViewTypeExtraHolder.java这个类,代码如下:

     1 /**
     2  * Author: wangjie
     3  * Email: tiantian.china.2@gmail.com
     4  * Date: 1/22/15.
     5  */
     6 public class ABRecyclerViewTypeExtraHolder extends ABRecyclerViewHolder {
     7     public ABRecyclerViewTypeExtraHolder(View itemView) {
     8         super(itemView);
     9     }
    10 
    11     /**
    12      * 保存当前position(list index,不包括headerView和footerView)
    13      */
    14     private int realItemPosition;
    15 
    16     public int getRealItemPosition() {
    17         return realItemPosition;
    18     }
    19 
    20     protected void setRealItemPosition(int realItemPosition) {
    21         this.realItemPosition = realItemPosition;
    22     }
    23 
    24 }

    它继承了ABRecyclerViewHolderhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/adapter/ABRecyclerViewHolder.java),只是保存了一个刚刚讲到的realItemPosition对象。

    所以我们再贴下ABRecyclerViewHolder的代码:

     1 /**
     2  * Author: wangjie
     3  * Email: tiantian.china.2@gmail.com
     4  * Date: 1/19/15.
     5  */
     6 public class ABRecyclerViewHolder extends RecyclerView.ViewHolder {
     7     private static final String TAG = ABRecyclerViewHolder.class.getSimpleName();
     8     private SparseArray<View> holder = null;
     9 
    10     public ABRecyclerViewHolder(View itemView) {
    11         super(itemView);
    12     }
    13 
    14     /**
    15      * 获取一个缓存的view
    16      *
    17      * @param id
    18      * @param <T>
    19      * @return
    20      */
    21     public <T extends View> T obtainView(int id) {
    22         if (null == holder) {
    23             holder = new SparseArray<>();
    24         }
    25         View view = holder.get(id);
    26         if (null != view) {
    27             return (T) view;
    28         }
    29         view = itemView.findViewById(id);
    30         if (null == view) {
    31             Logger.e(TAG, "no view that id is " + id);
    32             return null;
    33         }
    34         holder.put(id, view);
    35         return (T) view;
    36     }
    37 
    38     /**
    39      * 获取一个缓存的view,并自动转型
    40      *
    41      * @param id
    42      * @param <T>
    43      * @return
    44      */
    45     public <T> T obtainView(int id, Class<T> viewClazz) {
    46         View view = obtainView(id);
    47         if (null == view) {
    48             return null;
    49         }
    50         return (T) view;
    51     }
    52 
    53 }

    然后发现,它的作用是在于使用SparseArray来缓存findViewById后的控件。这样做的好处是,这个holder可以适用于任何的RecyclerView.Adapter中。只要通过obtainView()方法就能得到itemView中的具体的view对象,如下代码所示:

    1 Person person = adapter.getList().get(position);
    2 holder.obtainView(R.id.recycler_view_test_item_person_name_tv, TextView.class).setText(person.getName());=
    3 holder.obtainView(R.id.recycler_view_test_item_person_age_tv, TextView.class).setText(person.getAge() + "岁");

    示例代码:

    https://github.com/wangjiegulu/RecyclerViewSample

    [Android]使用RecyclerView替代ListView(一)

    http://www.cnblogs.com/tiantianbyconan/p/4232560.html

    [Android]使用RecyclerView替代ListView(三) 

    http://www.cnblogs.com/tiantianbyconan/p/4268097.html

  • 相关阅读:
    依赖反转Ioc和unity,autofac,castle框架教程及比较
    webform非表单提交时防xss攻击
    tfs分支操作
    防火墙入站出站规则配置
    前端流程图jsplumb学习笔记
    Js闭包学习笔记
    word中加入endnote
    Rest概念学习
    DRF的版本、认证、权限
    博客园自动生成目录
  • 原文地址:https://www.cnblogs.com/tiantianbyconan/p/4242541.html
Copyright © 2011-2022 走看看