RecyclerView替换ListView(包含SwipeRefreshLayout)
来源:互联网 发布:hype淘宝哪家店是正品 编辑:程序博客网 时间:2024/05/08 03:37
在android.support.v7包中,提供了一个新控件RecyclerView,是Android 5.0 materials design中的组件之一,用来替换ListView、GridView。本文主要讲解RecycleView的基本用法,以及如何配合SwipeRefreshLayout实现下拉刷新,上拉加载的功能。SwipeRefreshLayout是android.support.v4包中提供的。
一、RecyclerView的基本介绍
这是RecycleView使用的四个基本步骤,看起来是比ListView麻烦点,但是它的功能更加强大。//设置适配器m_recyclerView.setAdapter(m_adapter);//设置布局方式m_recyclerView.setLayoutManager(m_layoutManager);//设置分割线m_recyclerView.addItemDecoration(decoration);//设置Item切换的动画m_recyclerView.setItemAnimator(new DefaultItemAnimator());
1、RecycleView.LayoutManager
LinearLayoutManager 相当于ListView;
GridLayoutManager 相当于GridView;
StaggeredGridLayoutManager 相当于瀑布流。
这三种LayoutManager都有含有orientation参数的构造方法,这个参数用来判断RecycleView是竖直的还是水
平的。所有用RecycleView来实现水平的ListView和GridView更加方便。
2、RecycleView.ItemDecoration
相对于ListView设置分割线这方面而言,RecycleView没有提供分割线,但是提供了ItemDecoration这个抽象
类给我们自定义分割线的样式。
以下提供几种分割线的实现方式:
(1)设置每个Item与周边的边距,RecycleView的父布局的颜色就成了分割线的颜色。(以上一种LayoutManager
通用)
public class SpacesItemDecoration extends RecyclerView.ItemDecoration { private int space; public SpacesItemDecoration(int space) { this.space = space; } /** * 设置每个Item与周边的间距 */ @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.left = space; outRect.right = space; outRect.bottom = space; if (parent.getChildAdapterPosition(view) == 0 || parent.getChildAdapterPosition(view) == 2 || parent.getChildAdapterPosition(view) == 1) { outRect.top = space; } }}
(2)可以看到通过读取系统主题中的 android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。以下是LinearLayoutManager作为LayoutManager时的方式。public class DividerListItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerListItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } }}
(3)GridLayoutManager作为LayoutManager时,分割线的处理。public class DividerGridItemDecoration extends RecyclerView.ItemDecoration{ private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部 if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且横向滚动 { // 如果是最后一行,则不需要绘制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } }}
参考http://blog.csdn.net/lmj623565791/article/details/450595873、RecycleView.ItemAnimator
这也是个抽象类,用来处理RecycleView的Item删除和插入时的动画,不过SDK给我们提供了一个默认的实现类DefaultItemAnimator。
4、到了RecycleView四个步骤中最重要的一步了,setAdapter。与ListView和GridView一样,也需要设置适配器,不过这里并不是继承BaseAdapter了,而是RecycleView.Adapter。之前ListView的适配器中ViewHolder的使用不是强制性的,在RecycleView中已经是必须得写的了。
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> { private Context context; public MyRecyclerAdapter(List<String> list, Context context) { this.context = context; } @Override public MyRecyclerAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.content_main, null); MyViewHolder myViewHolder = new MyViewHolder(view); return myViewHolder; } @Override public void onBindViewHolder(final MyRecyclerAdapter.MyViewHolder holder, final int position) { holder.textView.setText("第"+position+"个Item"); } @Override public int getItemCount() { return 30; } class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; CardView cardView; public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView); } }}
重写这个适配器,主要实现三个方法、写一个ViewHolder。(1)onCreateViewHolder 创建ViewHolder
(2)onBindViewHolder 将ViewHolder与数据源绑定
(3)getItemCount Item的总是
实现以上四步,RecycleView的基本使用就掌握了,效果图如下:
二、RecycleView功能进阶。
1、点击事件、长按事件
RecycleView没有提供OnClickListener和OnLongClickListener,需要我们自己实现。
在自定义的Adapter中定义一个接口,代码如下:
private OnItemClickListener onItemClickListener;public interface OnItemClickListener { void OnItemClick(View view, int position); void OnItemLongClick(View view, int position);}public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener;}
在onBindViewHolder方法中调用接口中的方法if (onItemClickListener != null) { holder.textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); onItemClickListener.OnItemClick((((MyViewHolder) holder).textView), pos); } }); holder.textView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = holder.getLayoutPosition(); onItemClickListener.OnItemLongClick((((MyViewHolder) holder).textView), pos); return false; } });}
在MainActivity中使用时,直接调用Adapter中的方法,实现这个接口。m_adapter.setOnItemClickListener(new MyRecyclerAdapter.OnItemClickListener() { @Override public void OnItemClick(View view, int position) { Toast.makeText(MainActivity.this, "点击了" + position, Toast.LENGTH_SHORT).show(); } @Override public void OnItemLongClick(View view, int position) {Toast.makeText(MainActivity.this, "长按了" + position, Toast.LENGTH_SHORT).show();
}});
2、删除与插入
在自定义的Adapter中定义两个方法,如下:
public void addItem(int position) { m_listData.add(position, "Insert One"); notifyItemInserted(position);}public void deleteItem(int position) { m_listData.remove(position); notifyItemRemoved(position);}
刷新适配器的方法调用上面notifyItemInserted(position)和notifyItemRemoved(position),这样才能执行之前设置的Item的Animation三、RecycleView与SwipeRefreshLayout配合实现下拉刷新、加载更多。
1、下拉刷新
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /></android.support.v4.widget.SwipeRefreshLayout>
//设置下拉刷新进度条的颜色m_swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);m_swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { m_swipeRefreshLayout.postDelayed(new Runnable() { @Override public void run() { m_swipeRefreshLayout.setRefreshing(false); m_adapter.addItem(1); } }, 1000); }});
直接这样使用,RecycleView的滚动和SwipeRefreshLayout的下拉冲突,造成每次下拉都会触发onRefreshListener。需要判断,只有当RecycleView滚动到顶部时,下拉才触发刷新的事件。
//通过对RecycleView的滚动状态进行监听,当第0个位置的Item的顶部的坐标大于或等于0时,将SwipeRefreshView设置成可以刷新的,反之设置成不可刷新的m_recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //设置下拉刷新的事件 int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop(); m_swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); }});
在OnScrollStateChanged中的那两句代码写的很好,判断当RecycleView的第一个Item的顶部的坐标等于0时将SwipeRefreshLayout设置成Enabled,反之不触发滚动事件。2、加载更多
在ListView中有addFootView方法,RecycleView没有,所以需要我们自己判断。
在Adapter中有getItemViewType(position)方法,可以判断每个Item的类型。
@Overridepublic int getItemViewType(int position) { if (position + 1 == getItemCount()) { return TYPE_FOOTER; } else { return TYPE_ITEM; }}
完整的Adapter类如下:package com.example.newviewuser;import android.content.Context;import android.support.v7.widget.CardView;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * Created by Administrator on 2016/1/25. */public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private Random random = new Random(); private List<String> m_listData; private Context context; private List<Integer> mHeights; private static final int GENERAL_VIEW = 0; //普通Item private static final int FOOTER_VIEW = 1; //最底部的Item //上拉加载更多 public static final int PULLUP_LOAD_MORE=0; //正在加载中 public static final int LOADING_MORE=1; //上拉加载更多状态-默认为0 private int load_more_status=0; private OnItemClickListener onItemClickListener; public interface OnItemClickListener { void OnItemClick(View view, int position); void OnItemLongClick(View view, int position); } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public MyRecyclerAdapter(List<String> list, Context context) { m_listData = list; this.context = context; mHeights = new ArrayList<Integer>(); for (int i = 0; i < m_listData.size(); i++) { mHeights.add((int) (100 + Math.random() * 300)); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == GENERAL_VIEW) { View view = LayoutInflater.from(context).inflate(R.layout.content_main, null); MyViewHolder myViewHolder = new MyViewHolder(view); return myViewHolder; } else if (viewType == FOOTER_VIEW) { View view = LayoutInflater.from(context).inflate(R.layout.footer_layout, null); FootViewHolder footViewHolder = new FootViewHolder(view); return footViewHolder; } return null; } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { if (holder instanceof MyViewHolder) { (((MyViewHolder) holder).textView).setText("第"+position+"个Item"); if (onItemClickListener != null) { (((MyViewHolder) holder).textView).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); onItemClickListener.OnItemClick((((MyViewHolder) holder).textView), pos); } }); (((MyViewHolder) holder).textView).setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = holder.getLayoutPosition(); onItemClickListener.OnItemLongClick((((MyViewHolder) holder).textView), pos); return false; } }); } }else if (holder instanceof FootViewHolder){ FootViewHolder footViewHolder = (FootViewHolder) holder; switch (load_more_status){ case PULLUP_LOAD_MORE: footViewHolder.textView.setText("上拉加载更多..."); break; case LOADING_MORE: footViewHolder.textView.setText("正在加载更多数据..."); break; } } } @Override public int getItemCount() { return m_listData.size() + 1; } @Override public int getItemViewType(int position) { if (position + 1 == getItemCount()) { return FOOTER_VIEW; } else { return GENERAL_VIEW; } } public void addItem(int position) { m_listData.add(position, "Insert One"); if (mHeights.size() < m_listData.size()) { mHeights.add((int) (100 + Math.random() * 300)); } notifyItemInserted(position); } public void deleteItem(int position) { m_listData.remove(position); notifyItemRemoved(position); } public void addMoreItem(List<String> newDatas) { m_listData.addAll(newDatas); notifyDataSetChanged(); } /** * //上拉加载更多 * PULLUP_LOAD_MORE=0; * //正在加载中 * LOADING_MORE=1; * //加载完成已经没有更多数据了 * NO_MORE_DATA=2; * @param status */ public void changeMoreStatus(int status){ load_more_status=status; notifyDataSetChanged(); } class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; CardView cardView; public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView); } } class FootViewHolder extends RecyclerView.ViewHolder { TextView textView; public FootViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.text_view_footer); } }}
在RecycleView滚动的监听中判断,当滚动底部时,显示加载更多的布局,改变FootView的状态。m_recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); //设置下拉刷新的时间 int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop(); m_swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0); if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem +1 == m_adapter.getItemCount()) { m_adapter.changeMoreStatus(MyRecyclerAdapter.LOADING_MORE); new Handler().postDelayed(new Runnable() { @Override public void run() { List<String> addString = new ArrayList<String>(); addString.add("加载更多增加的数据"); m_adapter.addMoreItem(addString); m_adapter.changeMoreStatus(MyRecyclerAdapter.PULLUP_LOAD_MORE); m_adapter.notifyDataSetChanged(); } },2500); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); }});
下载源码- RecyclerView替换ListView(包含SwipeRefreshLayout)
- RecyclerView的使用(Android开发必备,替换掉ListView)
- RecyclerView列表组件(用于替换传统的ListView组件)
- 横向Listview,viewpager,RecyclerView中与swiperefreshlayout滑动冲突
- SwipeRefreshLayout与ListView,GridView,RecyclerView ,ScrollView 冲突问题总结
- ScrollView、SwipeRefreshLayout、ListView、RecyclerView等控件解决滑动冲突
- Android SwipeRefreshLayout 包含ListView 上拉刷新 下拉加载
- Android listView 替换技术 RecyclerView And CardView
- RecyclerView android5.0之后替换listview、gridview
- RecyclerView+SwipeRefreshLayout使用细节
- SwipeRefreshLayout+RecyclerView下拉刷新
- SwipeRefreshLayout+RecyclerView 下拉刷新
- SwipeRefreshLayout 与RecyclerView 使用
- recyclerview + 置顶 + swiperefreshlayout
- Android--RecyclerView,SwipeRefreshLayout相关
- android RecyclerView+SwipeRefreshLayout
- RecyclerView和SwipeRefreshLayout
- Android5.0推出的替换listView的控件recyclerview
- 两个UIViewController之间的翻转可以用动画效果翻转(上下,左右)--跨界面之间的视图切换
- iOS开发趋势:Native与H5+JS 解决方案
- Android Studio之版本管理工具Git (图文教程)
- nltk(3)——语料库
- 线程数
- RecyclerView替换ListView(包含SwipeRefreshLayout)
- ListView中convertView和ViewHolder的工作原理
- HDU-4908-BestCoder Sequence【思维题】
- IE 不支持单引号(')的实体名称(&apos;)
- jsp fn标签的用法
- log4j和p6spy日志管理
- LVS资源整合
- WebSevice--xml解析
- Spring各个版本的下载地址