RecyclerView并提供了向下的兼容库,也就是说5.0一下也可以使用它。
为什么叫做RecyclerView呢?Recycler:资源回收。下面就是重点的解释:
- RecyclerView不关心Item是否显示在正确的位置,那如何显示呢?我们就需要引入LayoutManager用来显示RecyclerView的风格。他可以显示ListView、GridView、横向GridView、横向ListView还有瀑布流效果。
- RecyclerView不关心Item间如何分隔的。我们需要用到ItemDecoration来绘制分割。
- RecyclerView不关心Item增加与删除的动画效果。我们需要使用ItemAnimator来完成。
这种插件式的玩法,也让RecyclerView仅仅关注如何回收和复用View。
下面还有一些与RecyclerView相关的重要类:
LayoutManager,ItemDecoration,ItemAnimator,Adapter,ViewHolder(强制使用)
下面我们来实战一下:
创建项目recyclerView,选择最小版本为5.1
我们使用的android studio,当创建项目完成后,我们要导入包,通过File->Project Structure->app->Dependencies->左边上角+号,Library dependency->找到com.android.support:recyclerview-v7:*****
我们回到Project模式下的app->build.gradle文件中
1 2 3 4 5 6 |
dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:recyclerview-v7:24.0.0' } |
你可以看到recyclerview已经正式加入。
使用
android studio的Android模式
下来到res/Layout/activity_main.xml中:
1 2 3 4 5 |
<android.support.v7.widget.RecyclerView android:id="@+id/id_recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" /> |
下面来到MainActivity中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class MainActivity extends AppCompatActivity { //相关声明 private RecyclerView mRecyclerView; private List<String> mDatas; private SimpleAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //加载数据 initDatas(); //初始化Views initViews(); } private void initViews() { mRecyclerView=(RecyclerView) findViewById(R.id.id_recyclerview); } private void initDatas() { mDatas = new ArrayList<String>(); //A-z的所有字母 for(int i='A'; i<='z';i++){ mDatas.add(""+(char)i); } } } |
上面的含义,我已添加注释,我们发现我们还缺少mAdapter没有被使用,那么就新建一个吧?
创建Adapter
首先MainActivity同包下创建一个
1 |
public class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> { |
并实现MyViewHolder,
1 2 3 4 5 6 |
class MyViewHolder extends ViewHolder { public MyViewHolder(View itemView) { super(itemView); } } |
后面我还还会修改这个ViewHolder。
我们需要实现SimpleAdapter的几个必要方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> { private LayoutInflater mInflater; //上下文 private Context mContext; private List<String> mDatas; public SimpleAdapter(Context context, List<String> datas){ this.mContext=context; this.mDatas=datas; //载入布局页面 mInflater=LayoutInflater.from(context); } //创建ViewHolder @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return null; } //绑定ViewHolder @Override public void onBindViewHolder(MyViewHolder holder, int position) { } //获取item数 @Override public int getItemCount() { return this.mDatas.size(); } } |
我们要完成onCreateViewHolder的方法,就需要创建一个视图xml:
在res/Layout中创建一个item_single_textview.xml文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:background="#44ff0000" android:layout_height="72dp" > <TextView android:id="@+id/id_tv" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" /> </FrameLayout> |
回到我们之前创建的SimpleAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//创建ViewHolder @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { /* inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。 关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义 resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。 root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true, 就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上, 也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。 attachToRoot:是否将root附加到布局文件的根视图上 */ View view=mInflater.inflate(R.layout.item_single_textview,parent,false); MyViewHolder viewHolder = new MyViewHolder(view); return viewHolder; } |
返回需要的viewHolder。
我们现在就要完善之前的ViewHolder了:
1 2 3 4 5 6 7 8 9 |
class MyViewHolder extends ViewHolder { TextView tv; public MyViewHolder(View itemView) { super(itemView); tv= (TextView) itemView.findViewById(R.id.id_tv); } } |
好我们来到simpleAdapter中,实现绑定
1 2 3 4 5 6 |
//绑定ViewHolder @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.tv.setText(mDatas.get(position)); } |
也就是说,我们给textView绑定了相应的值。
来到MainActivity,使用我们新创建的Adapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //加载数据 initDatas(); //初始化Views initViews(); mAdapter=new SimpleAdapter(this,mDatas); mRecyclerView.setAdapter(mAdapter); LinearLayoutManager linearLayoutMangaer= new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false); mRecyclerView.setLayoutManager(linearLayoutMangaer); } |
LinearLayoutManager中的LinearLayoutManager.VERTICAL 表示垂直布局,整体就类似于Listview的垂直布局。
添加分割线
你会发现我们上面运行的效果没有分割线,嘿嘿,这就要用到我们之前所说的ItemDecoration了:
因为google对这个类并没有默认的实现,所以我们找到了一个网络上使用率很高的公共类:
他的地址是:https://gist.github.com/alexfu/0f464fc3742f134ccd1e 估计你要翻墙才能得到,我直接复制到项目中了,大家可以随时复制使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package com.example.napoleon.recyclerview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; public class DividerItemDecoration 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 DividerItemDecoration(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); 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); } } } |
浏览一下这个类吧,mDivider:item间隔图片资源 ;mOrientation:方向,水平和垂直;onDraw方法会根据方向绘制;getItemOffsets提供绘制分割线的间隙。
来到ManActivity中的onCreate方法使用这个类:
1 2 3 4 5 6 |
//设置RecyclerView的布局管理 LinearLayoutManager linearLayoutMangaer= new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false); mRecyclerView.setLayoutManager(linearLayoutMangaer); //设置RecyclerView的Item分隔符 mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST)); |
运行我们的程序:
分割符已经有了。
MenuItem切换GridView效果和瀑布流
我们下面想切换listView效果为GridView效果,需要MenuItem作为切换的中介。
首先,我们要创建Menu,在res目录下的menu文件夹下创建一个main.xml文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_listview" android:orderInCategory="100" android:title="ListView" /> <item android:id="@+id/action_gridview" android:orderInCategory="100" android:title="GridView" /> <item android:id="@+id/action_hor_gridview" android:orderInCategory="100" android:title="HorizontalGridView" /> <item android:id="@+id/action_staggered" android:orderInCategory="100" android:title="StaggeredGridView" /> </menu> |
注释:这里创建了两个菜单项,其中<item>标签就是用来创建具体的一个菜单项,然后通过android:id给这个菜单项指定一个唯一的标识符,通过android:title给这菜单指定一个名称。
然后打开Activity,重写onCreateOptionsMenu()方法,内容如下:
1 2 3 4 5 6 7 |
//通过getMenuInflater()方法得到MenuInflater对象 public boolean onCreateOptionsMenu(Menu menu){ //调用inflate()方法创建菜单 getMenuInflater().inflate(R.menu.main,menu); //如果返回false,创建的菜单无法显示 return true; } |
当然,仅仅让菜单显示出来是不够的,菜单不是用来看的,关键是要菜单真正可用才行,因此还要再定义菜单响应事件。
在Activity中重写onOptionsItemSelected()方法,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public boolean onOptionsItemSelected(MenuItem item){ //通过调用item.getItemId()来判断菜单项 switch (item.getItemId()){ case R.id.action_gridview: Toast.makeText(this,"You Clicked Add",Toast.LENGTH_SHORT).show(); break; case R.id.action_listview: Toast.makeText(this,"You Clicked Add",Toast.LENGTH_SHORT).show(); break; case R.id.action_staggered: Toast.makeText(this,"You Clicked Add",Toast.LENGTH_SHORT).show(); break; case R.id.action_hor_gridview: Toast.makeText(this,"You Clicked Add",Toast.LENGTH_SHORT).show(); break; default: } return true; } |
当然,仅仅让菜单显示出来是不够的,菜单不是用来看的,关键是要菜单真正可用才行,因此还要再定义菜单响应事件。
在Activity中重写onOptionsItemSelected()方法,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public boolean onOptionsItemSelected(MenuItem item){ //通过调用item.getItemId()来判断菜单项 switch (item.getItemId()){ case R.id.action_gridview: Toast.makeText(this,"You Clicked GridView",Toast.LENGTH_SHORT).show(); break; case R.id.action_listview: Toast.makeText(this,"You Clicked Listview",Toast.LENGTH_SHORT).show(); break; case R.id.action_staggered: Toast.makeText(this,"You Clicked Staggered",Toast.LENGTH_SHORT).show(); break; case R.id.action_hor_gridview: Toast.makeText(this,"You Clicked HorizontalGridView",Toast.LENGTH_SHORT).show(); break; default: } return true; } |
运行程序,并按下Menu键就能够显示菜单项了。
菜单默认是不会显示出来的,只有按下Menu键,菜单才会在显示出来。
下面我们就来看看如何从ListView效果到GridView效果,其实代码是很简单的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public boolean onOptionsItemSelected(MenuItem item){ //通过调用item.getItemId()来判断菜单项 switch (item.getItemId()){ case R.id.action_gridview: Toast.makeText(this,"You Clicked GridView",Toast.LENGTH_SHORT).show(); //new GridLayoutManager第二个参数为3列 mRecyclerView.setLayoutManager(new GridLayoutManager(this,3)); break; case R.id.action_listview: Toast.makeText(this,"You Clicked Listview",Toast.LENGTH_SHORT).show(); //维持原来的listView效果 mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); break; case R.id.action_staggered: Toast.makeText(this,"You Clicked Staggered",Toast.LENGTH_SHORT).show(); break; case R.id.action_hor_gridview: Toast.makeText(this,"You Clicked HorizontalGridView",Toast.LENGTH_SHORT).show(); //new StaggeredGridLayoutManager()第一个参数为列数,第二个参数为方向横向GridView mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5,StaggeredGridLayoutManager.HORIZONTAL)); break; default: } return true; } |
我做了备注,可查看代码什么意思。
我们看到了GirdView。
但是当你切换到HorizontalGridView时,你发现没有GridView的效果,我们自需要控制res\layout\item_single_textview.xml的长度即可:
1 |
android:layout_width="72dp" |
可能会好很多,具体参数你可以根据你的需求来改变。
当然我们可以使用android:layout_margin=”3dp”来制作分隔样式。
实现瀑布流效果
实现瀑布流效果,代码其实差不多,但是还要小小调整一下,所有我们先复制MainActivity另存为StaggeredGridLayoutActivity,复制SimpleAdapter为StaggeredAdapter。
我们主要要做的事情就是动态控制我们的item的高度。
来到刚复制的StaggeredAdapter中添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
..... //声明存放随机高度 private List<Integer> mHeights; public StaggeredAdapter(Context context, List<String> datas){ ....... //载入布局页面 mInflater=LayoutInflater.from(context); //创建随机高度 mHeights=new ArrayList<Integer>(); for(int i=0; i < mDatas.size();i++){ mHeights.add((int)(100+Math.random()*300)); } } |
绑定这些随机高度到ViewHolder
1 2 3 4 5 6 7 8 9 10 |
//绑定ViewHolder @Override public void onBindViewHolder(StaggeredAdapter.MyViewHolder holder, int position) { ViewGroup.LayoutParams lp=holder.itemView.getLayoutParams(); lp.height=mHeights.get(position); holder.itemView.setLayoutParams(lp); holder.tv.setText(mDatas.get(position)); } |
修改StaggeredGridLayoutActivity
将StaggeredGridLayoutActivity中原来的SimpleAdapter更改为StaggeredAdapter。
修改:
1 2 3 4 5 6 |
@Override protected void onCreate(Bundle savedInstanceState) { ....... //设置RecyclerView的布局管理 mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)); } |
来到app\src\main\AndroidManifest.xml,添加StaggeredGridLayoutActivity
1 |
<activity android:name=".StaggeredGridLayoutActivity"></activity> |
最后,回到MainActivity.java,我们要在menu中打开StaggeredGridLayoutActivity:
1 2 3 4 5 6 7 8 9 10 11 |
public boolean onOptionsItemSelected(MenuItem item){ ......... case R.id.action_staggered: Toast.makeText(this,"You Clicked Staggered",Toast.LENGTH_SHORT).show(); Intent intent=new Intent(this,StaggeredGridLayoutActivity.class); startActivity(intent); break; .......... } return true; } |
并且res\layout\item_single_textview.xml代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:background="#44ff0000" android:layout_margin="3dp" android:layout_height="72dp" > <TextView android:id="@+id/id_tv" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" /> </FrameLayout> |
好了,瀑布流效果已经展现。
我现在要展示出StaggeredGridLayoutActivity,和StaggeredAdapter的全部代码,以便大家参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
public class StaggeredAdapter extends RecyclerView.Adapter<StaggeredAdapter.MyViewHolder> { private LayoutInflater mInflater; //上下文 private Context mContext; private List<String> mDatas; //声明存放随机高度 private List<Integer> mHeights; public StaggeredAdapter(Context context, List<String> datas){ this.mContext=context; this.mDatas=datas; //载入布局页面 mInflater=LayoutInflater.from(context); //创建随机高度 mHeights=new ArrayList<Integer>(); for(int i=0; i < mDatas.size();i++){ mHeights.add((int)(100+Math.random()*300)); } } //创建ViewHolder @Override public StaggeredAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { /* inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。 关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义 resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。 root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true, 就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上, 也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。 attachToRoot:是否将root附加到布局文件的根视图上 */ View view=mInflater.inflate(R.layout.item_single_textview,parent,false); MyViewHolder viewHolder = new MyViewHolder(view); return viewHolder; } //绑定ViewHolder @Override public void onBindViewHolder(StaggeredAdapter.MyViewHolder holder, int position) { ViewGroup.LayoutParams lp=holder.itemView.getLayoutParams(); lp.height=mHeights.get(position); holder.itemView.setLayoutParams(lp); holder.tv.setText(mDatas.get(position)); } //获取item数 @Override public int getItemCount() { return this.mDatas.size(); } class MyViewHolder extends ViewHolder { TextView tv; public MyViewHolder(View itemView) { super(itemView); tv= (TextView) itemView.findViewById(R.id.id_tv); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class StaggeredGridLayoutActivity extends AppCompatActivity { //相关声明 private RecyclerView mRecyclerView; private List<String> mDatas; private StaggeredAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //加载数据 initDatas(); //初始化Views initViews(); mAdapter=new StaggeredAdapter(this,mDatas); mRecyclerView.setAdapter(mAdapter); //设置RecyclerView的布局管理 mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)); } private void initViews() { mRecyclerView=(RecyclerView) findViewById(R.id.id_recyclerview); } private void initDatas() { mDatas = new ArrayList<String>(); //A-z的所有字母 for(int i='A'; i<='z';i++){ mDatas.add(""+(char)i); } } } |
RecyclerView的添加和删除动画效果
来到menu/main.xml下添加如下两个item:
1 2 3 4 5 6 7 8 9 10 11 12 |
<item android:id="@+id/action_add" android:orderInCategory="100" android:title="add" android:showAsAction="ifRoom" /> <item android:id="@+id/action_delete" android:orderInCategory="100" android:title="delete" android:showAsAction="ifRoom" /> |
app:showAsAction
1.always:这个值会使菜单项一直显示在ActionBar上。
2.ifRoom:如果有足够的空间,这个值会使菜单显示在ActionBar上。
3.never:这个值菜单永远不会出现在ActionBar是。
4.withText:这个值使菜单和它的图标,菜单文本一起显示。
来到MainActivity,添加两个按钮的触发效果:
1 2 3 4 5 6 7 8 9 10 11 |
public boolean onOptionsItemSelected(MenuItem item){ //通过调用item.getItemId()来判断菜单项 switch (item.getItemId()){ case R.id.action_add: Toast.makeText(this,"You Clicked add",Toast.LENGTH_SHORT).show(); break; case R.id.action_delete: Toast.makeText(this,"You Clicked add",Toast.LENGTH_SHORT).show(); break; |
还记得最最前面介绍的setItemAnimator吗?现在就要用到了,这可是动画效果的关键:
1 2 3 4 5 6 7 8 |
@Override protected void onCreate(Bundle savedInstanceState) { ......... //设置RecyclerView的布局管理 LinearLayoutManager linearLayoutMangaer= new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false); mRecyclerView.setLayoutManager(linearLayoutMangaer); mRecyclerView.setItemAnimator(new DefaultItemAnimator()); |
动画还不行,当我们点击add和delete时,要有数据变化,所以还需要对SimpleAdapter进行处理,让其完成对数据的添加:
1 2 3 4 5 6 7 8 9 10 11 12 |
public class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> { public void addData(int pos){ mDatas.add(pos,"Insert one"); //这里不要调用 notifyDataSetChanged();因为没有动画效果 notifyItemInserted(pos); } public void deleteData(int pos){ mDatas.remove(pos); notifyItemRemoved(pos); } |
继续切换到MainActivity,来完成剩下的事情:
1 2 3 4 5 6 7 8 9 10 11 |
public boolean onOptionsItemSelected(MenuItem item){ //通过调用item.getItemId()来判断菜单项 switch (item.getItemId()){ case R.id.action_add: Toast.makeText(this,"You Clicked add",Toast.LENGTH_SHORT).show(); mAdapter.addData(1); break; case R.id.action_delete: Toast.makeText(this,"You Clicked add",Toast.LENGTH_SHORT).show(); mAdapter.deleteData(1); break; |
你会注意到添加了:mAdapter.addData(1);mAdapter.deleteData(1);
Item的onClick事件
RecyclerView中并没有实现任何的onClick事件,所以这些都需要我们自己来实现:
切换到SimpleAdapter
1 2 3 4 5 6 7 8 9 10 11 |
public class SimpleAdapter extends RecyclerView.Adapter<MyViewHolder> { ......... public interface OnItemClickListener { void onItemClick(View view,int position); void onItemLongClick(View view,int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener listener){ this.mOnItemClickListener = listener; } |
接下来
1 2 3 4 5 6 7 8 9 10 11 12 |
//绑定ViewHolder @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.tv.setText(mDatas.get(position)); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { } }); |
接下来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//绑定ViewHolder @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.tv.setText(mDatas.get(position)); if(mOnItemClickListener!=null){ holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mOnItemClickListener.onItemClick(holder.itemView,position); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { mOnItemClickListener.onItemLongClick(holder.itemView,position); return false; } }); } } |
MainAcivity使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Override protected void onCreate(Bundle savedInstanceState) { ........ mAdapter.setOnItemClickListener(new SimpleAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this,"click :"+position,Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this,"long click:"+position,Toast.LENGTH_SHORT).show(); } }); |
有一个很重要的Bug
当我们点击ActionBar的add后,添加多个item,你会发现这多个item点击的onclick事件输出的position都是一样的,这可麻烦了,因为我们在实战中会经常用到这个position,怎么解决呢?从布局来看,这个item已经被插入进去了,并且item的位置也发生了变化。
我们来到SimpleAdapter,我们的position应该用holder.getLayoutPosition();来代替
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.tv.setText(mDatas.get(position)); //item点击 if(mOnItemClickListener!=null){ holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int layoutPosition = holder.getLayoutPosition(); mOnItemClickListener.onItemClick(holder.itemView,layoutPosition); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { int layoutPosition = holder.getLayoutPosition(); mOnItemClickListener.onItemLongClick(holder.itemView,layoutPosition); return false; } }); } //end item点击 } |
万事大吉,希望你在实战中,展现更好的效果!
本文主要是:http://www.imooc.com/learn/424 明日之星-RecyclerView的记录