Android Room –待办事项列表应用程序
在本教程中,我们将讨论和实现Google引入的Room库。
我们将使用它来开发Todo List Android应用程序。
安卓机房
Room是Google引入的库,可作为SQLite上的抽象层,使数据库查询更加容易。
为什么要使用房间?
它确实对SQL查询进行编译时验证。
从而防止运行时崩溃。
它减少了样板代码。
代替使用SQLiteHelper和编写冗长的查询,我们可以使用注释为我们完成工作。
以下是Room库的结构:Google文档
通常,Room由以下三个主要部分组成:
- 实体:这是一个模型类,其中定义了充当列字段的属性。
我们也可以使用注解@ColumnInfo设置列名。
我们也需要设置表名。
要忽略表中的属性,请在其上使用注解@@ Ignore。
至少一个属性必须具有@ PrimaryKey
。
要设置与字段名称不同的列名称,请使用" @Embedded" - "数据库":这是一个抽象类,必须扩展" RoomDatabase"。
我们必须在这里设置实体。
另外,每次更改架构时,我们都需要更新版本号。
将exportSchema
设置为true/false - 道:数据访问对象。
这是我们设置SQL查询的接口。
@ Insert,@ Query,@ Update,@ Delete。
@Insert无法返回int。
@Update和@Delete可以返回一个整数,该整数表示已更改/删除的行数。
房间查询不能也不应在主线程上执行。
会导致崩溃。
让我们在Todo List Android应用程序中使用Room库。
Android TODO App项目结构
我们的应用程序包括插入,更新,删除待办事项。
我们首先需要在build.gradle中导入以下Room依赖项:
implementation 'android.arch.persistence.room:runtime:1.0.0' annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
让我们使用Todo.java类为数据库创建表,如下所示:
package com.theitroad.androidroomtodolist; import android.arch.persistence.room.Entity; import android.arch.persistence.room.Ignore; import android.arch.persistence.room.PrimaryKey; import java.io.Serializable; @Entity(tableName = MyDatabase.TABLE_NAME_TODO) public class Todo implements Serializable { @PrimaryKey(autoGenerate = true) public int todo_id; public String name; public String description; public String category; @Ignore public String priority; }
MyDatabase.java
package com.theitroad.androidroomtodolist; import android.arch.persistence.room.Database; import android.arch.persistence.room.RoomDatabase; @Database(entities = {Todo.class}, version = 1, exportSchema = false) public abstract class MyDatabase extends RoomDatabase { public static final String DB_NAME = "app_db"; public static final String TABLE_NAME_TODO = "todo"; public abstract DaoAccess daoAccess(); }
这是一个抽象类,其中包含DaoAccess()接口的定义。
DaoAccess.java
package com.theitroad.androidroomtodolist; import android.arch.persistence.room.Dao; import android.arch.persistence.room.Delete; import android.arch.persistence.room.Insert; import android.arch.persistence.room.Query; import android.arch.persistence.room.Update; import java.util.List; @Dao public interface DaoAccess { @Insert long insertTodo(Todo todo); @Insert void insertTodoList(List<Todo> todoList); @Query("SELECT * FROM " + MyDatabase.TABLE_NAME_TODO) List<Todo> fetchAllTodos(); @Query("SELECT * FROM " + MyDatabase.TABLE_NAME_TODO + " WHERE category = :category") List<Todo> fetchTodoListByCategory(String category); @Query("SELECT * FROM " + MyDatabase.TABLE_NAME_TODO + " WHERE todo_id = :todoId") Todo fetchTodoListById(int todoId); @Update int updateTodo(Todo todo); @Delete int deleteTodo(Todo todo); }
添加注释和一些sql查询。
并且所有SQLite功能都可以在我们的Activity中实现。
下面给出了activity_main.xml布局的代码:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Spinner android:id="@+id/spinner" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/spinner" android:layout_marginTop="16dp" <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="16dp" android:src="@android:drawable/ic_input_add" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" </RelativeLayout>
下面给出了recyclerview_item_layout.xml的代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.CardView android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="16dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp"> <TextView android:id="@+id/txtNo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/txtNo" android:orientation="vertical"> <TextView android:id="@+id/txtName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:textAllCaps="true" android:textColor="@android:color/black" <TextView android:id="@+id/txtDesc" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:ellipsize="end" android:maxLines="1" <TextView android:id="@+id/txtCategory" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:textColor="@android:color/holo_red_dark" </LinearLayout> </RelativeLayout> </android.support.v7.widget.CardView> </LinearLayout>
MainActivity.java类的代码如下:
package com.theitroad.androidroomtodolist; import android.annotation.SuppressLint; import android.arch.persistence.room.Room; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.Toast; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.ClickListener, AdapterView.OnItemSelectedListener { MyDatabase myDatabase; RecyclerView recyclerView; Spinner spinner; RecyclerViewAdapter recyclerViewAdapter; FloatingActionButton floatingActionButton; private String[] categories = { "All", "Android", "iOS", "Kotlin", "Swift" }; ArrayList<Todo> todoArrayList = new ArrayList<>(); ArrayList<String> spinnerList = new ArrayList<>(Arrays.asList(categories)); public static final int NEW_TODO_REQUEST_CODE = 200; public static final int UPDATE_TODO_REQUEST_CODE = 300; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); myDatabase = Room.databaseBuilder(getApplicationContext(), MyDatabase.class, MyDatabase.DB_NAME).fallbackToDestructiveMigration().build(); checkIfAppLaunchedFirstTime(); spinner.setOnItemSelectedListener(this); spinner.setSelection(0); floatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivityForResult(new Intent(MainActivity.this, TodoNoteActivity.class), NEW_TODO_REQUEST_CODE); } }); } private void initViews() { floatingActionButton = findViewById(R.id.fab); spinner = findViewById(R.id.spinner); ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, spinnerList); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerViewAdapter = new RecyclerViewAdapter(this); recyclerView.setAdapter(recyclerViewAdapter); } @Override public void launchIntent(int id) { startActivityForResult(new Intent(MainActivity.this, TodoNoteActivity.class).putExtra("id", id), UPDATE_TODO_REQUEST_CODE); } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (position == 0) { loadAllTodos(); } else { String string = parent.getItemAtPosition(position).toString(); loadFilteredTodos(string); } } @Override public void onNothingSelected(AdapterView<?> parent) { } @SuppressLint("StaticFieldLeak") private void loadFilteredTodos(String category) { new AsyncTask<String, Void, List<Todo>>() { @Override protected List<Todo> doInBackground(String... params) { return myDatabase.daoAccess().fetchTodoListByCategory(params[0]); } @Override protected void onPostExecute(List<Todo> todoList) { recyclerViewAdapter.updateTodoList(todoList); } }.execute(category); } @SuppressLint("StaticFieldLeak") private void fetchTodoByIdAndInsert(int id) { new AsyncTask<Integer, Void, Todo>() { @Override protected Todo doInBackground(Integer... params) { return myDatabase.daoAccess().fetchTodoListById(params[0]); } @Override protected void onPostExecute(Todo todoList) { recyclerViewAdapter.addRow(todoList); } }.execute(id); } @SuppressLint("StaticFieldLeak") private void loadAllTodos() { new AsyncTask<String, Void, List<Todo>>() { @Override protected List<Todo> doInBackground(String... params) { return myDatabase.daoAccess().fetchAllTodos(); } @Override protected void onPostExecute(List<Todo> todoList) { recyclerViewAdapter.updateTodoList(todoList); } }.execute(); } private void buildDummyTodos() { Todo todo = new Todo(); todo.name = "Android Retrofit Tutorial"; todo.description = "Cover a tutorial on the Retrofit networking library using a RecyclerView to show the data."; todo.category = "Android"; todoArrayList.add(todo); todo = new Todo(); todo.name = "iOS TableView Tutorial"; todo.description = "Covers the basics of TableViews in iOS using delegates."; todo.category = "iOS"; todoArrayList.add(todo); todo = new Todo(); todo.name = "Kotlin Arrays"; todo.description = "Cover the concepts of Arrays in Kotlin and how they differ from the Java ones."; todo.category = "Kotlin"; todoArrayList.add(todo); todo = new Todo(); todo.name = "Swift Arrays"; todo.description = "Cover the concepts of Arrays in Swift and how they differ from the Java and Kotlin ones."; todo.category = "Swift"; todoArrayList.add(todo); insertList(todoArrayList); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { //reset spinners spinner.setSelection(0); if (requestCode == NEW_TODO_REQUEST_CODE) { long id = data.getLongExtra("id", -1); Toast.makeText(getApplicationContext(), "Row inserted", Toast.LENGTH_SHORT).show(); fetchTodoByIdAndInsert((int) id); } else if (requestCode == UPDATE_TODO_REQUEST_CODE) { boolean isDeleted = data.getBooleanExtra("isDeleted", false); int number = data.getIntExtra("number", -1); if (isDeleted) { Toast.makeText(getApplicationContext(), number + " rows deleted", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), number + " rows updated", Toast.LENGTH_SHORT).show(); } loadAllTodos(); } } else { Toast.makeText(getApplicationContext(), "No action done by user", Toast.LENGTH_SHORT).show(); } } @SuppressLint("StaticFieldLeak") private void insertList(List<Todo> todoList) { new AsyncTask<List<Todo>, Void, Void>() { @Override protected Void doInBackground(List<Todo>... params) { myDatabase.daoAccess().insertTodoList(params[0]); return null; } @Override protected void onPostExecute(Void voids) { super.onPostExecute(voids); } }.execute(todoList); } private void checkIfAppLaunchedFirstTime() { final String PREFS_NAME = "SharedPrefs"; SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); if (settings.getBoolean("firstTime", true)) { settings.edit().putBoolean("firstTime", false).apply(); buildDummyTodos(); } } }
myDatabase = Room.databaseBuilder(getApplicationContext(),MyDatabase.class,MyDatabase.DB_NAME).fallbackToDestructiveMigration()。
build();)用于初始化数据库。fallbackToDestructiveMigration()提供跨数据库版本的无缝迁移而不会崩溃。
我们会检查应用程序是首次启动还是不使用共享首选项。
然后,我们创建一个虚拟ArrayList来填充我们的RecyclerView。
我们需要使用异步任务来运行查询。
因此,请使用AsyncTask或者使用RxJava。
在本教程中,我们使用了AsyncTasks。我们的应用程序中将执行以下操作:从列表中更新待办事项
将新的待办事项添加到列表中。
对于这两种情况,我们都使用" startActivityForResult"从下一个活动中获取数据,并相应地更新RecyclerView。
我们的微调器用于根据查询数据库的类别过滤RecyclerView。
RecyclerViewAdapter.java
package com.theitroad.androidroomtodolist; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> { private List<Todo> todoList; private RecyclerViewAdapter.ClickListener clickListener; public RecyclerViewAdapter(ClickListener clickListener) { this.clickListener = clickListener; todoList = new ArrayList<>(); } @Override public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_layout, parent, false); RecyclerViewAdapter.ViewHolder viewHolder = new RecyclerViewAdapter.ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) { Todo todo = todoList.get(position); holder.txtName.setText(todo.name); holder.txtNo.setText("#" + String.valueOf(todo.todo_id)); holder.txtDesc.setText(todo.description); holder.txtCategory.setText(todo.category); } @Override public int getItemCount() { return todoList.size(); } public void updateTodoList(List<Todo> data) { todoList.clear(); todoList.addAll(data); notifyDataSetChanged(); } public void addRow(Todo data) { todoList.add(data); notifyDataSetChanged(); } public class ViewHolder extends RecyclerView.ViewHolder { public TextView txtName; public TextView txtNo; public TextView txtDesc; public TextView txtCategory; public CardView cardView; public ViewHolder(View view) { super(view); txtNo = view.findViewById(R.id.txtNo); txtName = view.findViewById(R.id.txtName); txtDesc = view.findViewById(R.id.txtDesc); txtCategory = view.findViewById(R.id.txtCategory); cardView = view.findViewById(R.id.cardView); cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { clickListener.launchIntent(todoList.get(getAdapterPosition()).todo_id); } }); } } public interface ClickListener { void launchIntent(int id); } }
下面给出了activity_todo_note.xml布局的代码:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".TodoNoteActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" </android.support.design.widget.AppBarLayout> <include layout="@layout/content_todo_note" </android.support.design.widget.CoordinatorLayout>
content_todo_note.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".TodoNoteActivity" tools:showIn="@layout/activity_todo_note"> <EditText android:id="@+id/inTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:ems="10" android:hint="Title" android:text="Sample Title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" <EditText android:id="@+id/inDescription" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:ems="10" android:hint="Description" android:minLines="3" android:text="Sample Description" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/inTitle" <Spinner android:id="@+id/spinner" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:padding="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/inDescription" <Button android:id="@+id/btnDone" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:text="DONE" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/spinner" <Button android:id="@+id/btnDelete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="DELETE" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btnDone" </android.support.constraint.ConstraintLayout>
下面给出了TodoNoteActivity.java类的代码
package com.theitroad.androidroomtodolist; import android.annotation.SuppressLint; import android.arch.persistence.room.Room; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import java.util.ArrayList; import java.util.Arrays; public class TodoNoteActivity extends AppCompatActivity { Spinner spinner; EditText inTitle, inDesc; Button btnDone, btnDelete; boolean isNewTodo = false; private String[] categories = { "Android", "iOS", "Kotlin", "Swift" }; public ArrayList<String> spinnerList = new ArrayList<>(Arrays.asList(categories)); MyDatabase myDatabase; Todo updateTodo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_todo_note); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); spinner = findViewById(R.id.spinner); inTitle = findViewById(R.id.inTitle); inDesc = findViewById(R.id.inDescription); btnDone = findViewById(R.id.btnDone); btnDelete = findViewById(R.id.btnDelete); ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, spinnerList); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); myDatabase = Room.databaseBuilder(getApplicationContext(), MyDatabase.class, MyDatabase.DB_NAME).build(); int todo_id = getIntent().getIntExtra("id", -100); if (todo_id == -100) isNewTodo = true; if (!isNewTodo) { fetchTodoById(todo_id); btnDelete.setVisibility(View.VISIBLE); } btnDone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isNewTodo) { Todo todo = new Todo(); todo.name = inTitle.getText().toString(); todo.description = inDesc.getText().toString(); todo.category = spinner.getSelectedItem().toString(); insertRow(todo); } else { updateTodo.name = inTitle.getText().toString(); updateTodo.description = inDesc.getText().toString(); updateTodo.category = spinner.getSelectedItem().toString(); updateRow(updateTodo); } } }); btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { deleteRow(updateTodo); } }); } @SuppressLint("StaticFieldLeak") private void fetchTodoById(final int todo_id) { new AsyncTask<Integer, Void, Todo>() { @Override protected Todo doInBackground(Integer... params) { return myDatabase.daoAccess().fetchTodoListById(params[0]); } @Override protected void onPostExecute(Todo todo) { super.onPostExecute(todo); inTitle.setText(todo.name); inDesc.setText(todo.description); spinner.setSelection(spinnerList.indexOf(todo.category)); updateTodo = todo; } }.execute(todo_id); } @SuppressLint("StaticFieldLeak") private void insertRow(Todo todo) { new AsyncTask<Todo, Void, Long>() { @Override protected Long doInBackground(Todo... params) { return myDatabase.daoAccess().insertTodo(params[0]); } @Override protected void onPostExecute(Long id) { super.onPostExecute(id); Intent intent = getIntent(); intent.putExtra("isNew", true).putExtra("id", id); setResult(RESULT_OK, intent); finish(); } }.execute(todo); } @SuppressLint("StaticFieldLeak") private void deleteRow(Todo todo) { new AsyncTask<Todo, Void, Integer>() { @Override protected Integer doInBackground(Todo... params) { return myDatabase.daoAccess().deleteTodo(params[0]); } @Override protected void onPostExecute(Integer number) { super.onPostExecute(number); Intent intent = getIntent(); intent.putExtra("isDeleted", true).putExtra("number", number); setResult(RESULT_OK, intent); finish(); } }.execute(todo); } @SuppressLint("StaticFieldLeak") private void updateRow(Todo todo) { new AsyncTask<Todo, Void, Integer>() { @Override protected Integer doInBackground(Todo... params) { return myDatabase.daoAccess().updateTodo(params[0]); } @Override protected void onPostExecute(Integer number) { super.onPostExecute(number); Intent intent = getIntent(); intent.putExtra("isNew", false).putExtra("number", number); setResult(RESULT_OK, intent); finish(); } }.execute(todo); } }
setResult(RESULT_OK,intent);用于将结果传递回MainActivity。
我们或者传递新的待办任务的ID,或者传递删除/更新的行数,并相应地更新RecyclerView。