1、技术概述
使用ReclclerView列出账单分类,使用OnItemClickListener接口回调的方法实现在RecyclerView.Adapter外实现对RecyclerView的子项的点击交互事件。技术难点在于在适配器中定义接口,在具体引用的Activity中调用接口具体实现。
2、技术详述
(1)要使用RecyclerView这个控件,首先得在项目的build.gradle中添加相应的依赖库。
- 打开app/build.gradle文件,在dependencies闭包中添加以下内容:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
testCompile 'junit:junit:4.12'
}
- 添加完成后记得点击Sync Now进行库的同步。
(2)安装完依赖后,我们到布局文件中引入ReclclerView。外面包裹的容器是要RelativeLayout或者LinearLayout看你是要竖着显示每个Item还是横着显示。
- RelativeLayout是列表式的竖着显示:
······
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/reclcler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
······
- 具体效果图:
- LinearLayout为横向显示,加上GridLayout可以使Item呈网格式布局。
······
<GridLayout
android:id="@+id/viewpager_item"
android:layout_width="match_parent"
android:layout_height="130dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/category_recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</GridLayout>
······
- 具体效果图:
(3)在layout文件中引入ReclclerView后,还需要增加一个xml布局文件,用以定义每个Item要显示的内容。具体如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="50dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/cardview_light_background">
<TextView
android:id="@+id/category_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:textColor="@color/cardview_dark_background" />
</RelativeLayout>
</LinearLayout>
- 这边仅定义了一个TextView用于显示账单分类的名称,大家可以根据需求自行修改。
(4)准备完布局文件,我们需要为RecyclerView准备一个适配器,新建一个类CategoryAdapter,让这个适配器继承自ReclclewView.Adapter,并将泛型指定为CategoeyAdapter.ViewHolder。
- 在《第一行代码》中RecyclerView中的点击事件是直接在适配器中实现的,这很显然不能满足我们的要求,所以我们在适配器中定义OnItemClickListener接口,然后在要引用适配器的Activity中具体实现该接口,以达到我们所要的RecyclerView的点击交互效果。具体适配器代码如下:
public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.ViewHolder> {
private List<Category> mCategoryList;
//定义OnItemClickListener接口
public interface OnItemClickListener {
void onClick(int position);
}
public OnItemClickListener listener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView categoryName;
public ViewHolder(View view) {
super(view);
categoryName = (TextView) view.findViewById(R.id.category_name);
}
}
public CategoryAdapter(List<Category> mCList) {
mCategoryList = mCList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.category_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Category category = mCategoryList.get(position);
holder.categoryName.setText(category.getCategory_name());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onClick(position);
}
}
});
}
@Override
public int getItemCount() {
return mCategoryList.size();
}
}
(5)最后,我们在具体的Activity中引入适配器。具体代码及释义如下:
public class CategoryEditActivity extends AppCompatActivity implements View.OnClickListener {
······
public CategoryChooseAdapter categoryChooseAdapter;
public RecyclerView recycleView;
······
@Override
protected void onCreate(Bundle savedInstanceState) {
······
recycleView = (RecyclerView) findViewById(R.id.reclcler_view);
//初始化数据,装入ReclclerView中
initCategory();
}
//具体实现RecyclerView的Item点击事件
public void setCategoryChooseAdapter () {
//在这边实现具体的Item交互事件,点击事件交互的内容可以自行定义
categoryChooseAdapter.setOnItemClickListener(new CategoryChooseAdapter.OnItemClickListener() {
@Override
public void onClick(int position) {
······
//这边我使用MaterialDialog来实现点击一个Item后,出现对话框用以修改分类信息
new MaterialDialog.Builder(CategoryEditActivity.this)
.title("修改分类")
.canceledOnTouchOutside(false)
.input(category_name1, "", new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
}
})
.positiveText("确认修改")
.negativeText("取消")
.neutralText("删除该分类")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
//修改分类
······
Toast.makeText(CategoryEditActivity.this, "修改成功!", Toast.LENGTH_SHORT).show();
}
})
.onNeutral(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
······
//删除分类
Toast.makeText(CategoryEditActivity.this, "该分类已成功删除!", Toast.LENGTH_SHORT).show();
})
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
//取消
dialog.dismiss();
dialog = null;
}
})
.show();
}
});
}
private void initCategory() {
CategoryDAO categoryDAO = new CategoryDAOImpl();
categoryList = categoryDAO.listCategory();
//从数据库按需取出分类名称,装入categoryList中
······
categoryChooseAdapter = new CategoryChooseAdapter(categoryList); //将数据装入RecycerView适配器中
categoryChooseAdapter.notifyItemRangeChanged(0, categoryList.size()); //当新添或者删除某个Item时,能够即时刷新数据
recycleView.setAdapter(categoryChooseAdapter); //完成适配器设置
setCategoryChooseAdapter(); //实现每一个Item点击事件
}
}
- 具体效果图:
3、技术使用中遇到的问题和解决过程
RecyclerView并没有提供类似于setOnItemClickListener这样的注册监听器方法,而是需要我们给子项具体的View去注册点击事件。而在《第一行代码》中实现RecyclerView点击事件是在Adapter中的onCreateViewHolder()方法中注册点击事件,使用这种方法的话,当我们想要的点击事件复杂一点或者需要使用具体引入的Activity中的一些数据时,就没办法满足。所以开始找寻能够在适配器之外实现点击事件的方法。
经过一番搜寻,发现可以通过接口回调的方式实现外部定义点击事件。通过在RecyclerView的适配器中定义OnItemClickListener接口,再在Activity中调用接口,具体实现以解决自定义点击子项交互的问题。
使用RecyclerView时如果修改了某项子项页面数据却没有即时刷新的话,可调用RecyclerView中的notifyItemRangeChanged()方法,再重新设置适配器setAdapter(),来实现数据的实时更新。
······
categoryChooseAdapter = new CategoryChooseAdapter(categoryList); //将数据装入RecycerView适配器中
categoryChooseAdapter.notifyItemRangeChanged(0, categoryList.size()); //当修改或者删除某个Item时,能够即时刷新数据
recycleView.setAdapter(categoryChooseAdapter); //完成适配器设置
4、总结
《第一行代码》只是为你的Android开发打下一定的基础,对Android开发的一些基础知识进行较为详细的介绍。而要想做好Android开发,单单靠学完《第一行代码》还是不够的,还需要我们在实际开发时不断地去查找资料,学习更多的拓展知识。
5、参考文献、参考博客
- 《第一行代码》 郭霖著 人民邮电出版社
- RecyclerView(一)点击事件的两种方式 作者:peihp_ 链接:https://blog.csdn.net/P876643136/article/details/84337240