[关闭]
@natsumi 2016-06-20T14:13:23.000000Z 字数 12023 阅读 1749

学习RecyclerView的使用

Android


Android控件RecyclerView和ListView的异同
http://www.tuicool.com/articles/aeeaQ3J

Android开发之RecyclerView的使用【全解】
http://blog.csdn.net/dmk877/article/details/50816933

1. 导入support-v7包

修改gradle,在dependencies中加入recyclerview那一行,然后sync

  1. dependencies {
  2. compile fileTree(dir: 'libs', include: ['*.jar'])
  3. testCompile 'junit:junit:4.12'
  4. compile 'com.android.support:appcompat-v7:23.4.0'
  5. compile 'com.android.support:recyclerview-v7:23.4.0'
  6. }

2. 相关的布局资源文件

activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <android.support.v7.widget.RecyclerView
  6. android:id="@+id/recycler_view"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent">
  9. </android.support.v7.widget.RecyclerView>
  10. </LinearLayout>

item_home.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content">
  5. <TextView
  6. android:layout_width="match_parent"
  7. android:layout_height="50dp" />
  8. </LinearLayout>

3. recyclerView的关键代码

MainAcitivity的代码如下

  1. import android.os.Bundle;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.support.v7.widget.DefaultItemAnimator;
  4. import android.support.v7.widget.LinearLayoutManager;
  5. import android.support.v7.widget.OrientationHelper;
  6. import android.support.v7.widget.RecyclerView;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. public class MainActivity extends AppCompatActivity {
  10. private RecyclerView recyclerView;
  11. private List<String> mDatas;
  12. private MyRecyclerAdapter recycleAdapter;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
  18. initData();
  19. recycleAdapter = new MyRecyclerAdapter(MainActivity.this, mDatas);
  20. LinearLayoutManager layoutManager = new LinearLayoutManager(this);
  21. //设置布局管理器
  22. recyclerView.setLayoutManager(layoutManager);
  23. //设置为垂直布局,这也是默认的
  24. layoutManager.setOrientation(OrientationHelper.VERTICAL);
  25. //设置Adapter
  26. recyclerView.setAdapter(recycleAdapter);
  27. //设置增加或删除条目的动画
  28. recyclerView.setItemAnimator(new DefaultItemAnimator());
  29. }
  30. private void initData() {
  31. mDatas = new ArrayList<String>();
  32. for (int i = 0; i < 40; i++) {
  33. mDatas.add("item" + i);
  34. }
  35. }
  36. }

adapter的代码

  1. package blog.csdn.recyclerviewtest;
  2. import android.content.Context;
  3. import android.support.v7.widget.RecyclerView;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.ViewGroup;
  7. import android.widget.TextView;
  8. import java.util.List;
  9. /**
  10. * Created by tiantian on 16-6-17.
  11. */
  12. public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewholder> {
  13. private List<String> mDatas;
  14. private Context mContext;
  15. private LayoutInflater mInflater;
  16. public MyRecyclerAdapter(Context context, List<String> datas) {
  17. this.mContext = context;
  18. this.mDatas = datas;
  19. mInflater = LayoutInflater.from(mContext);
  20. }
  21. //重写onCreateViewHolder方法,返回一个自定义的ViewHolder
  22. @Override
  23. public MyRecyclerAdapter.MyViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
  24. View view = mInflater.inflate(R.layout.item_home, parent, false);
  25. MyViewholder holder =new MyViewholder(view);
  26. return holder;
  27. }
  28. //填充onCreateViewHolder方法返回的holder中的控件
  29. @Override
  30. public void onBindViewHolder(MyRecyclerAdapter.MyViewholder holder, int position) {
  31. holder.tv.setText(mDatas.get(position));
  32. }
  33. @Override
  34. public int getItemCount() {
  35. return mDatas.size();
  36. }
  37. public class MyViewholder extends RecyclerView.ViewHolder {
  38. TextView tv;
  39. public MyViewholder(View itemView) {
  40. super(itemView);
  41. tv=(TextView)itemView.findViewById(R.id.tv_item);
  42. }
  43. }
  44. }

4. RecyclerView增加分隔线

可以通过RecyclerView.addItemDecoration(ItemDecoration decoration)这个方法进行设置,其中它需要的参数就是我们自己定义的继承自ItemDecoration的一个对象。我们可以创建一个继承RecyclerView.ItemDecoration类来绘制分隔线,通过ItemDecoration可以让我们每一个Item从视觉上面相互分开来,例如ListView的divider非常相似的效果。当然像我们上面的例子ItemDecoration我们没有设置也没有报错,那说明ItemDecoration我们并不是强制需要使用,作为我们开发者可以设置或者不设置Decoration的。实现一个ItemDecoration,系统提供的ItemDecoration是一个抽象类,内部除去已经废弃的方法以外,我们主要实现以下三个方法:

  1. public static abstract class ItemDecoration {
  2. public void onDraw(Canvas c,RecyclerView parent,State state) {
  3. onDraw(c,parent);
  4. }
  5. public void onDrawOver(Canvas c,RecyclerView parent,State state) {
  6. onDrawOver(c,parent);
  7. }
  8. public void getItemOffsets(RectoutRect, View view,RecyclerView parent,State state) {
  9. getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent);
  10. }
  11. }

又因为当我们RecyclerView在进行绘制的时候会进行绘制Decoration,那么会去调用onDraw和onDrawOver方法,那么这边我们其实只要去重写onDraw和getItemOffsets这两个方法就可以实现啦。然后LayoutManager会进行Item布局的时候,会去调用getItemOffset方法来计算每个Item的Decoration合适的尺寸,下面我们来具体实现一个Decoration,DividerItemDecoration.java

  1. package blog.csdn.recyclerviewtest;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Canvas;
  5. import android.graphics.Rect;
  6. import android.graphics.drawable.Drawable;
  7. import android.support.v7.widget.LinearLayoutManager;
  8. import android.support.v7.widget.RecyclerView;
  9. import android.view.View;
  10. public class DividerItemDecoration extends RecyclerView.ItemDecoration {
  11. private static final int[] ATTRS = new int[]{
  12. android.R.attr.listDivider
  13. };
  14. public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
  15. public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
  16. private Drawable mDivider;
  17. private int mOrientation;
  18. public DividerItemDecoration(Context context, int orientation) {
  19. final TypedArray a = context.obtainStyledAttributes(ATTRS);
  20. mDivider = a.getDrawable(0);
  21. a.recycle();
  22. setOrientation(orientation);
  23. }
  24. public void setOrientation(int orientation) {
  25. if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
  26. throw new IllegalArgumentException("invalid orientation");
  27. }
  28. mOrientation = orientation;
  29. }
  30. @Override
  31. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  32. if (mOrientation == VERTICAL_LIST) {
  33. drawVertical(c, parent);
  34. } else {
  35. drawHorizontal(c, parent);
  36. }
  37. }
  38. public void drawVertical(Canvas c, RecyclerView parent) {
  39. final int left = parent.getPaddingLeft();
  40. final int right = parent.getWidth() - parent.getPaddingRight();
  41. final int childCount = parent.getChildCount();
  42. for (int i = 0; i < childCount; i++) {
  43. final View child = parent.getChildAt(i);
  44. final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
  45. .getLayoutParams();
  46. final int top = child.getBottom() + params.bottomMargin;
  47. final int bottom = top + mDivider.getIntrinsicHeight();
  48. mDivider.setBounds(left, top, right, bottom);
  49. mDivider.draw(c);
  50. }
  51. }
  52. public void drawHorizontal(Canvas c, RecyclerView parent) {
  53. final int top = parent.getPaddingTop();
  54. final int bottom = parent.getHeight() - parent.getPaddingBottom();
  55. final int childCount = parent.getChildCount();
  56. for (int i = 0; i < childCount; i++) {
  57. final View child = parent.getChildAt(i);
  58. final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
  59. .getLayoutParams();
  60. final int left = child.getRight() + params.rightMargin;
  61. final int right = left + mDivider.getIntrinsicHeight();
  62. mDivider.setBounds(left, top, right, bottom);
  63. mDivider.draw(c);
  64. }
  65. }
  66. @Override
  67. public void getItemOffsets(Rect outRect, View view, RecyclerView parent,RecyclerView.State state) {
  68. if (mOrientation == VERTICAL_LIST) {
  69. outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
  70. } else {
  71. outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
  72. }
  73. }
  74. }

然后在MainActivity的onCreate方法里加上
recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,LinearLayoutManager.VERTICAL));

4.1 改变分隔线样式

见下文3.3
http://blog.csdn.net/dmk877/article/details/50816933

5. 其他布局的recyclerView

见下文3.3
http://blog.csdn.net/dmk877/article/details/50816933

6. RecyclerView增加和删除的动画

(包括RecyclerView.Adapter中刷新的几个方法的对比)

添加布局文件res/menu/main.xml,其中两个图片文件还需要自行添加到drawable目录:ic_menu_delete和actionbar_add_icon

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <menu xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. tools:context="com.example.reclerviewpractice.MainActivity" >
  6. <item
  7. android:id="@+id/id_action_add"
  8. android:icon="@drawable/actionbar_add_icon"
  9. android:orderInCategory="100"
  10. android:title="add"
  11. app:showAsAction="ifRoom"/>
  12. <item
  13. android:id="@+id/id_action_delete"
  14. android:icon="@drawable/ic_menu_delete"
  15. android:orderInCategory="100"
  16. android:title="delete"
  17. app:showAsAction="ifRoom"/>
  18. <item
  19. android:id="@+id/id_action_gridview"
  20. android:orderInCategory="100"
  21. android:title="GirdView"
  22. app:showAsAction="never"/>
  23. <item
  24. android:id="@+id/id_action_listview"
  25. android:orderInCategory="100"
  26. android:title="ListView"
  27. app:showAsAction="never"/>
  28. <item
  29. android:id="@+id/id_action_horizontalGridView"
  30. android:orderInCategory="100"
  31. android:title="HorizontalGridView"
  32. app:showAsAction="never"/>
  33. </menu>

在MainActivity中重写这两个函数

  1. @Override
  2. public boolean onCreateOptionsMenu(Menu menu) {
  3. getMenuInflater().inflate(R.menu.main,menu);
  4. return super.onCreateOptionsMenu(menu);
  5. }
  6. @Override
  7. public boolean onOptionsItemSelected(MenuItem item) {
  8. switch (item.getItemId()){
  9. case R.id.id_action_add:
  10. recycleAdapter.addData(1);
  11. break;
  12. case R.id.id_action_delete:
  13. recycleAdapter.removeData(1);
  14. }
  15. return super.onOptionsItemSelected(item);
  16. }

recyclerViewAdapter中增加的两个方法:

  1. public void addData(int position) {
  2. mDatas.add(position, "Insert One");
  3. notifyItemInserted(position);
  4. notifyItemRangeChanged(position, mDatas.size());
  5. }
  6. public void removeData(int position) {
  7. mDatas.remove(position);
  8. notifyItemRemoved(position);
  9. notifyItemRangeChanged(position, mDatas.size());
  10. }

这里需要说一下RecyclerView.Adapter中刷新数据的几个方法,一共有这么几个方法

  1. public final void notifyDataSetChanged()
  2. public final void notifyItemChanged(int position)
  3. public final void notifyItemRangeChanged(int positionStart, int itemCount)
  4. public final void notifyItemInserted(int position)
  5. public final void notifyItemMoved(int fromPosition, int toPosition)
  6. public final void notifyItemRangeInserted(int positionStart, int itemCount)
  7. public final void notifyItemRemoved(int position)
  8. public final void notifyItemRangeRemoved(int positionStart, int itemCount)

6. 给RecyclerView的Item添加点击事件

到这里还有一点从文章开头到现在我们都没有提及,就是Item的点击事件RecyclerView监听事件处理在ListView使用的时候,该控件给我们提供一个onItemClickListener监听器,这样当我们点击Item的时候,会回调相关的方法,以便我们方便处理Item点击事件。对于RecyclerView来讲,非常可惜的是,该控件没有给我们提供这样的内置监听器方法,不过我们可以进行改造实现,可以这样实现Item的点击事件的监听,在我们的adapter中增加如下的接口和方法

  1. public interface OnItemClickListener {
  2. void onClick(int position);
  3. void onLongClick(int position);
  4. }
  5. public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
  6. this.mOnItemClickListener = onItemClickListener;
  7. }

修改onBindViewHolder方法

  1. //填充onCreateViewHolder方法返回的holder中的控件
  2. @Override
  3. public void onBindViewHolder(MyRecyclerAdapter.MyViewholder holder, final int position) {
  4. holder.tv.setText(mDatas.get(position));
  5. if(mOnItemClickListener!=null){
  6. holder.itemView.setOnClickListener(new View.OnClickListener() {
  7. @Override
  8. public void onClick(View v) {
  9. mOnItemClickListener.onClick(position);
  10. }
  11. });
  12. holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
  13. @Override
  14. public boolean onLongClick(View v) {
  15. mOnItemClickListener.onLongClick(position);
  16. return true;
  17. }
  18. });
  19. }
  20. }

关于onLongClick的返回值,参考http://www.tuicool.com/articles/QZVZ7fy
按钮点击时间中有一个临界值,当我单下按钮的时间等于这个临界值的时候,点击事件和长点击事件会同时触发。如果返回值为true的话这个点击事件会被长点击独占,否则相反。将onLongClick的返回值设置为true(默认是false),即可避免这个问题的发生。

在MainActivity的onCreate方法中加入

  1. //设置点击事件
  2. recycleAdapter.setOnItemClickListener(new MyRecyclerAdapter.OnItemClickListener() {
  3. @Override
  4. public void onClick(int position) {
  5. Toast.makeText(MainActivity.this,"click "+position+" item",Toast.LENGTH_SHORT).show();
  6. }
  7. @Override
  8. public void onLongClick(int position) {
  9. Toast.makeText(MainActivity.this,"long click "+position+" item",Toast.LENGTH_SHORT).show();
  10. }
  11. });
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注