什么是RecyclerView
RecyclerView是Android 5.0 materials design中的组件之一,相应的还有CardView、Palette等。看名字我们就能看出一点端倪,没错,它主要的特点就是复用。我们知道,Listview中的Adapter中可以实现ViewHolder的复用。RecyclerView提供了一个耦合度更低的方式来复用ViewHolder,并且可以轻松的实现ListView、GridView以及瀑布流的效果。
RecyclerView的用法
首先我们要gradle的依赖库中添加 compile 'com.android.support:recyclerview-v7:21.+' 。如果是eclipse直接导入android-support-v7-recyclerview.jar就可以了。
1 /** 2 * 设置Adapter 3 */ 4 mRecyclerView.setAdapter(mListAdapter); 5 /** 6 * 设置布局管理器 7 */ 8 mRecyclerView.setLayoutManager(linearLayoutManager); 9 /** 10 * 设置item分割线 11 */ 12 mRecyclerView.addItemDecoration(itemDecoration); 13 /** 14 * 设置item动画 15 */ 16 mRecyclerView.setItemAnimator(new DefaultItemAnimator());
使用RecyclerView,基本上要上面四步。相比ListView只需设置Adapter而言,RecyclerView的使用看起来似乎要复杂一些。但是它的可定制性更高了,你可以自己定制自己的分割线样式或者是item的的动画。
下面我们看下如何使用RecyclerView简单实现ListView的效果。
activity:
1 package com.bbk.lling.recyclerview; 2 3 import android.support.v7.app.ActionBarActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.DefaultItemAnimator; 6 import android.support.v7.widget.LinearLayoutManager; 7 import android.support.v7.widget.RecyclerView; 8 import android.view.Menu; 9 import android.view.MenuItem; 10 import android.view.View; 11 import android.widget.Toast; 12 13 import java.util.ArrayList; 14 import java.util.List; 15 16 /** 17 * @Class: ListLayoutActivity 18 * @Description: RecycleView实现listview的功能 19 * @author: lling(www.liuling123.com) 20 * @Date: 2015/10/29 21 */ 22 public class ListLayoutActivity extends ActionBarActivity { 23 24 private RecyclerView mRecyclerView; 25 private ListAdapter mListAdapter; 26 private List<String> mDatas; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_list_layout); 32 initData(); 33 mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); 34 mListAdapter = new ListAdapter(this, mDatas); 35 mListAdapter.setOnItemClickListener(new ListAdapter.OnItemClickListener() { 36 @Override 37 public void onItemClick(View view, int position) { 38 Toast.makeText(ListLayoutActivity.this, "Click" + mDatas.get(position), Toast.LENGTH_SHORT).show(); 39 } 40 41 @Override 42 public void onItemLongClick(View view, int position) { 43 mListAdapter.remove(position); //remove the item 44 Toast.makeText(ListLayoutActivity.this, "LongClick" + mDatas.get(position), Toast.LENGTH_SHORT).show(); 45 } 46 }); 47 mRecyclerView.setAdapter(mListAdapter); 48 /** 49 * 设置布局管理器,listview风格则设置为LinearLayoutManager 50 * gridview风格则设置为GridLayoutManager 51 * pu瀑布流风格的设置为StaggeredGridLayoutManager 52 */ 53 mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 54 // 设置item分 55 mRecyclerView.addItemDecoration(new ListItemDecoration(this, LinearLayoutManager.VERTICAL)); 56 // 设置item动画 57 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 58 59 } 60 61 @Override 62 public boolean onCreateOptionsMenu(Menu menu) { 63 getMenuInflater().inflate(R.menu.menu_list_layout, menu); 64 return true; 65 } 66 67 @Override 68 public boolean onOptionsItemSelected(MenuItem item) { 69 switch (item.getItemId()) { 70 case R.id.add_first: 71 mListAdapter.add(0, "add first"); 72 break; 73 case R.id.add_last: 74 mListAdapter.add(mListAdapter.getItemCount(), "add last"); 75 break; 76 case R.id.remove_first: 77 String value = mListAdapter.remove(0); 78 Toast.makeText(ListLayoutActivity.this, "remove:" + value, Toast.LENGTH_SHORT).show(); 79 break; 80 case R.id.remove_last: 81 String value1 = mListAdapter.remove(mListAdapter.getItemCount()-1); 82 Toast.makeText(ListLayoutActivity.this, "remove:" + value1, Toast.LENGTH_SHORT).show(); 83 break; 84 case R.id.horizontal: 85 mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); 86 mRecyclerView.addItemDecoration(new ListItemDecoration(this, LinearLayoutManager.HORIZONTAL)); 87 break; 88 case R.id.vertical: 89 mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 90 mRecyclerView.addItemDecoration(new ListItemDecoration(this, LinearLayoutManager.VERTICAL)); 91 break; 92 } 93 return super.onOptionsItemSelected(item); 94 } 95 96 /* ==========This Part is not necessary========= */ 97 98 /** 99 * Create datas 100 */ 101 protected void initData() { 102 mDatas = new ArrayList<String>(); 103 for (int i = 0; i < 100; i++) { 104 mDatas.add(String.valueOf(i)); 105 } 106 } 107 108 /* ==========This Part is not necessary========= */ 109 }
activity布局:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" > 5 6 <android.support.v7.widget.RecyclerView 7 android:id="@+id/recyclerview" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" /> 10 11 </RelativeLayout>
Adapter:
1 package com.bbk.lling.recyclerview; 2 3 import android.annotation.SuppressLint; 4 import android.content.Context; 5 import android.support.v7.widget.RecyclerView; 6 import android.util.Log; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.TextView; 11 12 import java.util.List; 13 14 /** 15 * @Class: ListAdapter 16 * @Description: 数据适配器 17 * @author: lling(www.liuling123.com) 18 * @Date: 2015/10/29 19 */ 20 public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ItemViewHolder> { 21 22 private List<String> mDatas; 23 private LayoutInflater mInflater; 24 private OnItemClickListener mOnItemClickListener; 25 26 public ListAdapter(Context context, List<String> mDatas) { 27 this.mDatas = mDatas; 28 mInflater = LayoutInflater.from(context); 29 } 30 31 @Override 32 public int getItemCount() { 33 return mDatas.size(); 34 } 35 36 @SuppressLint("NewApi") 37 @Override 38 public void onBindViewHolder(final ItemViewHolder itemViewHolder, final int i) { 39 itemViewHolder.mTextView.setText(mDatas.get(i)); 40 if(mOnItemClickListener != null) { 41 /** 42 * 这里加了判断,itemViewHolder.itemView.hasOnClickListeners() 43 * 目的是减少对象的创建,如果已经为view设置了click监听事件,就不用重复设置了 44 * 不然每次调用onBindViewHolder方法,都会创建两个监听事件对象,增加了内存的开销 45 */ 46 if(!itemViewHolder.itemView.hasOnClickListeners()) { 47 Log.e("ListAdapter", "setOnClickListener"); 48 itemViewHolder.itemView.setOnClickListener(new View.OnClickListener() { 49 @Override 50 public void onClick(View v) { 51 int pos = itemViewHolder.getPosition(); 52 mOnItemClickListener.onItemClick(v, pos); 53 } 54 }); 55 itemViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 56 @Override 57 public boolean onLongClick(View v) { 58 int pos = itemViewHolder.getPosition(); 59 mOnItemClickListener.onItemLongClick(v, pos); 60 return true; 61 } 62 }); 63 } 64 } 65 } 66 67 @Override 68 public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 69 /** 70 * 使用RecyclerView,ViewHolder是可以复用的。这根使用ListView的VIewHolder复用是一样的 71 * ViewHolder创建的个数好像是可见item的个数+3 72 */ 73 Log.e("ListAdapter", "onCreateViewHolder"); 74 ItemViewHolder holder = new ItemViewHolder(mInflater.inflate( 75 R.layout.item_layout, viewGroup, false)); 76 return holder; 77 } 78 79 /** 80 * 向指定位置添加元素 81 * @param position 82 * @param value 83 */ 84 public void add(int position, String value) { 85 if(position > mDatas.size()) { 86 position = mDatas.size(); 87 } 88 if(position < 0) { 89 position = 0; 90 } 91 mDatas.add(position, value); 92 /** 93 * 使用notifyItemInserted/notifyItemRemoved会有动画效果 94 * 而使用notifyDataSetChanged()则没有 95 */ 96 notifyItemInserted(position); 97 } 98 99 /** 100 * 移除指定位置元素 101 * @param position 102 * @return 103 */ 104 public String remove(int position) { 105 if(position > mDatas.size()-1) { 106 return null; 107 } 108 String value = mDatas.remove(position); 109 notifyItemRemoved(position); 110 return value; 111 } 112 113 114 public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { 115 this.mOnItemClickListener = mOnItemClickListener; 116 } 117 118 /** 119 * 处理item的点击事件和长按事件 120 */ 121 interface OnItemClickListener { 122 public void onItemClick(View view, int position); 123 public void onItemLongClick(View view, int position); 124 } 125 126 class ItemViewHolder extends RecyclerView.ViewHolder { 127 128 private TextView mTextView; 129 130 public ItemViewHolder(View itemView) { 131 super(itemView); 132 mTextView = (TextView) itemView.findViewById(R.id.textview); 133 } 134 } 135 136 }
这里值得注意的是,RecyclerView并没有提供setOnItemClickListener方法来设置item的点击事件,所以这里我们自己来实现item的点击事件,这点很坑爹有木有?没有就自己设置呗!上面代码121-124定义了一个点击接口。然后给Adapter设置定义的接口对象,然后在onBindViewHolder中为每个holder设置点击事件就行了。但是有一点得注意,因为只要滑动RecyclerView,onBindViewHolder就会不停的调用,如果不加判断的话,则会不停的创建新的点击事件对象,浪费内存,所以在设置点击事件之前需要判断一下是否已经设置过了(如上面代码46行),如果设置过了就不需要创建了。
item的布局:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 <TextView 6 android:id="@+id/textview" 7 android:layout_width="wrap_content" 8 android:layout_height="48dp" 9 android:minWidth="48dp" 10 android:gravity="center" 11 android:layout_centerInParent="true" 12 android:text="XXX"/> 13 </RelativeLayout>
item分割线:
1 package com.bbk.lling.recyclerview; 2 3 import android.content.Context; 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 11 /** 12 * @Class: ListItemDecoration 13 * @Description: listview的item分割线 14 * @author: lling(www.liuling123.com) 15 * @Date: 2015/10/29 16 */ 17 public class ListItemDecoration extends RecyclerView.ItemDecoration { 18 19 private Drawable mDrawable; 20 21 private final static int DEFAULT_ORENTATION = LinearLayoutManager.VERTICAL; 22 23 private int mOrientation; 24 25 public ListItemDecoration(Context context, int orientation) { 26 if(orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) { 27 this.mOrientation = DEFAULT_ORENTATION; 28 } else { 29 this.mOrientation = orientation; 30 } 31 mDrawable = context.getResources().getDrawable(R.drawable.divider); 32 } 33 34 @Override 35 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 36 if(mOrientation == LinearLayoutManager.HORIZONTAL) { 37 drawHorizontal(c, parent); 38 } else { 39 drawVertical(c, parent); 40 } 41 } 42 43 private void drawHorizontal(Canvas c, RecyclerView parent) { 44 int top = parent.getPaddingTop(); 45 int bottom = parent.getHeight() - parent.getPaddingBottom(); 46 47 int childCount = parent.getChildCount(); 48 for (int i = 0; i < childCount; i++) { 49 View child = parent.getChildAt(i); 50 RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 51 .getLayoutParams(); 52 int left = child.getRight() + params.rightMargin; 53 int right = left + mDrawable.getIntrinsicHeight(); 54 mDrawable.setBounds(left, top, right, bottom); 55 mDrawable.draw(c); 56 } 57 } 58 59 private void drawVertical(Canvas c, RecyclerView parent) { 60 int left = parent.getPaddingLeft(); 61 int right = parent.getWidth() - parent.getPaddingRight(); 62 63 int childCount = parent.getChildCount(); 64 for (int i = 0; i < childCount; i++) { 65 View child = parent.getChildAt(i); 66 android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); 67 RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 68 .getLayoutParams(); 69 int top = child.getBottom() + params.bottomMargin; 70 int bottom = top + mDrawable.getIntrinsicHeight(); 71 mDrawable.setBounds(left, top, right, bottom); 72 mDrawable.draw(c); 73 } 74 } 75 76 @Override 77 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 78 super.getItemOffsets(outRect, view, parent, state); 79 } 80 }
divider.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > 3 <size android:height="1dp" android:width="1dp"/> 4 <!--<solid android:color="#e0e0e0"/>--> 5 <solid android:color="#ff0000"/> 6 </shape>
好了,ListView的效果已经实现了,看下效果图
RecyclerView实现GridView以及瀑布流效果的代码这里就不贴出来了,demo源码里面有,需要的可以下载看看。