Material Design材料设计原则----RecyclerView控件使用(二)列表项的单击事件、添加(删除)列表项
上篇文章介绍了RecyclerView控件的基本用法,我们已经知道它是一个ListView的升级版,可轻松的展示各种列表风格,例如水平列表、垂直列表、网格列表、瀑布流列表等。但是,我们项目中仅仅展示数据是不够的,我们经常还有列表项的单击事件、添加列表项、删除列表项等。今天我们就着重讲解一下RecyclerView列表项的单击事件、添加列表项、删除列表项。
效果:
1. RecyclerView列表项的单击事件
不幸的是RecyclerView控件没有与ListView一样的 onItemClickListener单击事件监听器,那么就需要我们自定义onItemClickListener。我们先贴出MyRecyclerAdapter源码(在上一篇源码基础上修改,用来给RecyclerView装配数据的adapter)
package com.anyikang.volunteer.sos.recyclerview; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> { private List<String> list; //列表数据 private OnItemClickListener mOnItemClickListener; public MyRecyclerAdapter(List<String> list) { // TODO Auto-generated constructor stub this.list = list; } /** * * 实例化 列表项布局中的控件,在此为一个简单的textview */ class MyViewHolder extends RecyclerView.ViewHolder{ TextView tv; public MyViewHolder(View view) { super(view); tv = (TextView)view.findViewById(R.id.text1); } } /** * 要显示的列表项数 * @return 列表项总数 */ @Override public int getItemCount() { // TODO Auto-generated method stub return list.size(); } /** * 用数据填充列表项上的 textview文本 * @param holder: 当前列表项 布局的 控件实例 * @param position: 列表项的索引 */ @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.tv.setText(list.get(position)); //将列表数据list数组中的position位置的字符串 填充给 这个列表项上的textview //实现列表item单击事件,主要使用java interface来实现回调MainActivity中的onItemClick函数 if(mOnItemClickListener!=null){ holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onItemClick(v, position); } }); } } /** * 为列表项实例化布局,将在onBindViewHolder里为布局控件赋值 * @param viewGroup * @param arg1 * @return */ @Override public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) { MyViewHolder holder = new MyViewHolder(View.inflate(viewGroup.getContext(), R.layout.list_item, null)); return holder; } public interface OnItemClickListener{ void onItemClick(View view, int position); } public void setOnItemClickListener(OnItemClickListener listener){ this.mOnItemClickListener = listener; } }
红色标注的代码,就实现了一个列表单击监听器。我们先回顾一下listview的itemOnclickListener在代码中是如何运用的:
//listview列表项单击事件,放在一个Activity里,例如MainActivity
lvFence.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id)
//在这里添加单击处理代码
return;
}
});
上述代码通过调用listview的setOnItemClickListener函数为listview设置了一个 单击监听器OnItemClickListener类对象,
我们从中能分析到什么?
(1)Listview类里有一个成员变量,该成员变量是一个OnItemClickListener类的实例对象。同时,有一个setOnItemClickListener函数来设置该成员变量的具体实例值。
(2)从new AdapterView.OnItemClickListener()这行代码可以猜测OnItemClickListener可能是定义在ListView类中的一个抽象接口,需要在MainActivity里通过 实现(实例化)这个接口以及它包含的抽象函数onItemClick 来处理业务逻辑。当ListView列表项被单击时,ListView会调用OnItemClickListener类型的成员对象(已通过setOnItemClickListener赋值)的 onItemClick函数,由于onItemClick函数实质是在MainActivity里实现的,因此onItemClick的处理代码将在MainActivity里执行,这也就是我们通常所说的“回调”,即在MainActivity里先实现抽象函数,这时并没有调用,而是在恰当的时机回过头来调用onItemClick函数。
综合(1)(2)分析类比,我们为RecyclerView控件自定义了列表项监听功能,具体步骤如下:
第一步,在适配器MyRecyclerAdapter类里定义抽象接口“OnItemClickListener”,其中有一个抽象函数OnItemClick。
第二步,在适配器MyRecyclerAdapter类声明一个OnItemClickListener类型的成员变量与setOnItemClickListener函数。
第三步,在Activity(例如MainActivity)里实现化接口OnItemClickListener及OnItemClick函数,并调用setOnItemClickListener函数为MyRecyclerAdapter中的OnItemClickListener类型成员变量赋值。
第四步,回调。在MyRecyclerAsetOnClickListenerdapter的onBindViewHolder函数里为列表项对应的VIEW布局的单击函数
(这个系统里已经有了,就是普通的按钮单击事件处理)里回调onItemClick函数,具体核心代码如下:
//实现列表item单击事件,主要使用java interface来实现回调MainActivity中的onItemClick函数
if(mOnItemClickListener!=null){
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, position);
}
});
}
到此,为RecyclerView控件补充我们自定义的列表项单击事件分析完毕,具体源码在文章最后。
2. RecyclerView添加、删除列表项与高效更新
当我们更新RecyclerView控件的列表数据时,以往的作法有如下:
(1)setAdapter ,为控件配置数据适配器,同时刷新全部列表项
(2)notifyDataSetChanged 刷新全部列表项数据
以上2种作法比较影响效率,RecyclerView控件为我们提供了 更有效率的两个刷新列表项函数:
函数1----public final void notifyItemInserted(int position)
用于在某一个列表项位置position上新添加一个列表项的情况下刷新列表。示例代码如下:
list.add(position,"additem"+position); //在列表数据数组List<String>的position位置上添加一个新数据
//notifyDataSetChanged();--会影响效率=
notifyItemInserted(position);
其中notifyItemInserted(position)不会重新刷新整个列表数据,只是在position位置上新绘制一个列表项,同时原来
position位置上的列表项的索引 变为position + 1,即从原position位置开始的列表项统一后移。函数2--notifyItemRemoved(int position)
list.remove(position,"additem"+position); //在列表数据数组List<String>的position位置上删除一个新数据
//notifyDataSetChanged();--会影响效率=
notifyItemRemoved(position);
其中notifyItemInserted(position)不会重新刷新整个列表数据,只是在position位置上删除一个列表项,
同时原来position+1位置上的列表项的索引 变为position,即从原position+1位置开始的列表项统一前移。
完整的代码先贴出来:
布局文件:
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/recylerview" android:layout_width="match_parent" android:layout_height="400dp" /> <LinearLayout android:layout_marginTop="30dp" android:layout_below="@+id/recylerview" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btAdd" android:text="添加" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_marginLeft="20dp" android:id="@+id/btDelete" android:text="删除" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </RelativeLayout>
MainActivity代码:
package com.anyikang.volunteer.sos.recyclerview; import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.anyikang.volunteer.sos.recyclerview.MyRecyclerAdapter.OnItemClickListener; import java.util.ArrayList; public class MainActivity extends Activity { private RecyclerView recylerview; //RecyclerView控件实例对象 private ArrayList<String> list; //RecyclerView要显示的 列表数据,在此为一组字符串。 private MyRecyclerAdapter adapter; //同ListView一样,需要一个适配器来 将list数据 装载到 RecyclerView列表控件。 //private MyStaggedRecyclerAdapter adapter; //列表显示风格 为瀑布流 界面样式 的 适配器。 boolean isGrid = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } public void initView() { //实例化布局中的recylerview控件 recylerview = (RecyclerView)findViewById(R.id.recylerview); Button btAdd = findViewById(R.id.btAdd); Button btDelete = findViewById(R.id.btDelete); Button btConvert = findViewById(R.id.btConvert); btAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { adapter.addData(3); } }); btDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { adapter.removeData(3); } }); } public void initData() { //模拟60条数据 list = new ArrayList<String>(); for (int i = 0; i < 60; i++) { list.add("item"+i); } //普通列表的适配器 adapter = new MyRecyclerAdapter(list); //列表显示风格为 垂直方向的列表 //recylerview.setLayoutManager(new LinearLayoutManager(this)); //列表显示风格为 水平方向的列表 //recylerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)); //列表显示风格为 瀑布流样式 recylerview.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL)); //列表显示风格为 网格样式,如9宫格布局 //recylerview.setLayoutManager(new GridLayoutManager(this, 3));
//瀑布流适配器,与普通适配器adapter的区别是,每一个列表项的布局大小都可能参差不齐
//adapter = new MyStaggedRecyclerAdapter(list);//装载显示列表数据 recylerview.setAdapter(adapter); recylerview.setItemAnimator(new DefaultItemAnimator());//添加删除列表项时会有动画效果,具体运行源码查看。 adapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "点了"+position, Toast.LENGTH_SHORT).show(); } }); return; }}
adapter代码:
package com.anyikang.volunteer.sos.recyclerview; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> { private List<String> list; //列表数据 private OnItemClickListener mOnItemClickListener; public MyRecyclerAdapter(List<String> list) { // TODO Auto-generated constructor stub this.list = list; } /** * * 实例化 列表项布局中的控件,在此为一个简单的textview */ class MyViewHolder extends RecyclerView.ViewHolder{ TextView tv; public MyViewHolder(View view) { super(view); tv = (TextView)view.findViewById(R.id.text1); } } /** * 要显示的列表项数 * @return 列表项总数 */ @Override public int getItemCount() { // TODO Auto-generated method stub return list.size(); } /** * 用数据填充列表项上的 textview文本 * @param holder: 当前列表项 布局的 控件实例 * @param position: 列表项的索引 */ @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.tv.setText(list.get(position)); //将列表数据list数组中的position位置的字符串 填充给 这个列表项上的textview //实现列表item单击事件,主要使用java interface来实现回调MainActivity中的onItemClick函数 if(mOnItemClickListener!=null){ holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onItemClick(v, position); } }); } } /** * 为列表项实例化布局,将在onBindViewHolder里为布局控件赋值 * @param viewGroup * @param arg1 * @return */ @Override public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) { MyViewHolder holder = new MyViewHolder(View.inflate(viewGroup.getContext(), R.layout.list_item, null)); return holder; } public void addData(int position){ list.add(position,"additem"+position); //提示刷新--会影响效率 // notifyDataSetChanged(); notifyItemInserted(position); } public void removeData(int position){ list.remove(position); notifyItemRemoved(position); } public interface OnItemClickListener{ void onItemClick(View view, int position); } public void setOnItemClickListener(OnItemClickListener listener){ this.mOnItemClickListener = listener; }
}
完整的工程源码下载地址:https://download.****.net/download/gaoxiaoweiandy/10364579