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
