如何在使用自定义方式保存数据 RecyclerView 时制作列表更改动画
How can I make list changes animations while using custom way of saving data RecyclerView
我正在制作一个备忘录应用程序,它使用 RecyclerView 显示 ArrayList<Note>
数据,其中 Note
是每个项目的数据 class,我保存和检索它们使用另一个名为 NotesController 的 class(使用 Gson)。为了更新列表,我现在要做的是为我已设置到列表的 NotesAdapter(RecyclerView 适配器)重新分配一个新值。这是我的代码:
NotesController:
public class NotesController {
private ArrayList<Note> notesList;
private String notesPath;
private Gson gson = new Gson();
private Type type = new TypeToken<ArrayList<Note>>() {
}.getType();
public NotesController(String notesPath) {
this.notesPath = notesPath;
if (FileUtil.isExistFile(notesPath)) {
getNotesList();
} else {
createNewList();
}
}
/**
* Creates a new list if it doesn't exist. Internal class use only.
*/
private void createNewList() {
notesList = new ArrayList<>();
saveLatestData();
}
/**
* Reads the saved notes.json file and retrieves the ArrayList of items of class {@link Note}.
* @return An ArrayList<<h>Note</h>> containing all notes saved in file <b>notes.json</b>
*/
public ArrayList<Note> getNotesList() {
String json = FileUtil.readFile(notesPath);
notesList = gson.fromJson(json, type);
return notesList;
}
/**
* Saves latest changes to the list {@linkplain NotesController#notesList} to notes.json file. Internal class use only.
*/
private void saveLatestData() {
String json = gson.toJson(notesList, type);
FileUtil.writeFile(notesPath, json);
}
/**
* Adds an item of type {@link Note} to the list and saves data by calling {@link NotesController#saveLatestData()}.
* @param note The {@link Note} instance to get added.
*/
public void add(Note note) {
notesList.add(0, note);
saveLatestData();
}
/**
* Replaces an existing item with a new one of type {@link Note} in the list {@link NotesController#notesList} and saves data by calling {@link NotesController#saveLatestData()}.
* @param position The position of the item to get replaced.
* @param note The {@link Note} instance to replace the old item.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public void set(int position, Note note) {
notesList.set(position, note);
saveLatestData();
}
/**
* Gets the {@link Note} item from the specified position.
* @param position The position of the item to return.
* @return The item at the position specified.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public Note get(int position) {
return notesList.get(position);
}
/**
* Removes the {@link Note} item in the specified position from the list.
* @param position The position of the item to remove.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public void remove(int position) {
notesList.remove(position);
saveLatestData();
}
/**
* Indexes the notes list for the given text and returns items that contain the query either in the title or the content.
* @param query The text query to search for (low cased).
* @return The notes whose title or content contains the query (all trimmed and low cased).
*/
public ArrayList<Note> search(String query) {
ArrayList<Note> results = new ArrayList<>();
for (Note note: getNotesList()) {
if (note.getTitle().trim().toLowerCase().contains(query.trim().toLowerCase()) || note.getContent().trim().toLowerCase().contains(query.trim().toLowerCase())) {
results.add(note);
}
}
return results;
}
/**
* Simple method to convert many int parameters to an int[] array.
* @param categories The varargs int[] array.
* @return int[] array from parameters.
*/
public int[] categories(int... categories) {
return categories;
}
}
MainActivity: (仅相关代码)
public class MainActivity extends AppCompatActivity {
private NotesAdapter notesAdapter;
public static NotesController notesController;
private RecyclerView notesRecyclerView;
private String notesDir;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Answers(), new Crashlytics());
setContentView(R.layout.activity_main);
...
notesDir = ContextCompat.getDataDir(this).getPath() + "/files/notes.json";
notesController = new NotesController(notesDir);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
notesRecyclerView.setLayoutManager(layoutManager);
updateRecyclerView();
notesAdapter.setOnItemActionListener(new NotesAdapter.ActionListener() {
@Override
public void onItemClick(final int position, View v) {
...
}
@Override
public void onItemLongClick(final int position, View v) {
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this).setTitle("Delete?").setMessage("Just for testing.");
dialog.setPositiveButton("DELETE", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
notesController.remove(position);
updateRecyclerView();
}
});
dialog.show();
}
});
...
}
...
private void updateRecyclerView() {
notesAdapter = new NotesAdapter(notesController.getNotesList(), getApplicationContext());
notesRecyclerView.setAdapter(notesAdapter);
notesAdapter.notifyDataSetChanged();
}
}
现在查看 updateRecyclerView()
方法,您会看到我使用来自 NotesController 的新数据再次重新分配适配器,然后通知列表数据已更改。
但我需要以某种方式,在不消除控制器的情况下,在我删除(例如通过长按)或添加某些内容(只是默认的)时制作列表制作删除动画。为此,Android RecyclerView Adapter class 为我们提供了 notifyItemInserted(int)
和 notifyItemRemoved(int)
但它们在我的情况下不起作用(即使删除了中断这些动画的 notifyDataSetChanged() ).
请不要建议我删除 NotesController,因为它有助于从应用程序的不同部分轻松访问笔记,我只需要一种方法让这两种插入和删除通知方法正常工作(任何其他解决方案)欢迎顺便说一句)。
注意:这是我的适配器类型:public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder>
。
每当您想更新 recyclerview 中的项目时,您都不应该创建新的适配器实例。你应该创建一个适配器实例并将它分配给recyclerview,如果你想在recyclerview中添加或删除项目,你只需要替换适配器中的数据。我会推荐 ListAdapter
因为它有功能 submitList(list)
可以很容易地用来更新数据。
此外,如果你想实现动画,你可以使用 this
我按照 Milan Kundacina 的建议使用函数 submitList(list)
解决了这个问题,而不是重新分配适配器。但是由于此函数仅随 ListAdapter 一起提供,而我仅使用 Adapter,因此我创建了自己的函数如下(suuuuuper 简单):
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder> {
private ArrayList<Note> notesList;
...
NotesAdapter(ArrayList<Note> notesList) {
super();
this.notesList = notesList;
}
public void submitList(ArrayList<Note> list) {
this.notesList = list;
}
...
}
并使用这种方式更新列表:
// inside MainActivity:
private void notifyListAdd(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemInserted(position);
}
private void notifyListRemove(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemRemoved(position);
}
我正在制作一个备忘录应用程序,它使用 RecyclerView 显示 ArrayList<Note>
数据,其中 Note
是每个项目的数据 class,我保存和检索它们使用另一个名为 NotesController 的 class(使用 Gson)。为了更新列表,我现在要做的是为我已设置到列表的 NotesAdapter(RecyclerView 适配器)重新分配一个新值。这是我的代码:
NotesController:
public class NotesController {
private ArrayList<Note> notesList;
private String notesPath;
private Gson gson = new Gson();
private Type type = new TypeToken<ArrayList<Note>>() {
}.getType();
public NotesController(String notesPath) {
this.notesPath = notesPath;
if (FileUtil.isExistFile(notesPath)) {
getNotesList();
} else {
createNewList();
}
}
/**
* Creates a new list if it doesn't exist. Internal class use only.
*/
private void createNewList() {
notesList = new ArrayList<>();
saveLatestData();
}
/**
* Reads the saved notes.json file and retrieves the ArrayList of items of class {@link Note}.
* @return An ArrayList<<h>Note</h>> containing all notes saved in file <b>notes.json</b>
*/
public ArrayList<Note> getNotesList() {
String json = FileUtil.readFile(notesPath);
notesList = gson.fromJson(json, type);
return notesList;
}
/**
* Saves latest changes to the list {@linkplain NotesController#notesList} to notes.json file. Internal class use only.
*/
private void saveLatestData() {
String json = gson.toJson(notesList, type);
FileUtil.writeFile(notesPath, json);
}
/**
* Adds an item of type {@link Note} to the list and saves data by calling {@link NotesController#saveLatestData()}.
* @param note The {@link Note} instance to get added.
*/
public void add(Note note) {
notesList.add(0, note);
saveLatestData();
}
/**
* Replaces an existing item with a new one of type {@link Note} in the list {@link NotesController#notesList} and saves data by calling {@link NotesController#saveLatestData()}.
* @param position The position of the item to get replaced.
* @param note The {@link Note} instance to replace the old item.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public void set(int position, Note note) {
notesList.set(position, note);
saveLatestData();
}
/**
* Gets the {@link Note} item from the specified position.
* @param position The position of the item to return.
* @return The item at the position specified.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public Note get(int position) {
return notesList.get(position);
}
/**
* Removes the {@link Note} item in the specified position from the list.
* @param position The position of the item to remove.
* @throws ArrayIndexOutOfBoundsException When position is out of {@link NotesController#notesList} range.
*/
public void remove(int position) {
notesList.remove(position);
saveLatestData();
}
/**
* Indexes the notes list for the given text and returns items that contain the query either in the title or the content.
* @param query The text query to search for (low cased).
* @return The notes whose title or content contains the query (all trimmed and low cased).
*/
public ArrayList<Note> search(String query) {
ArrayList<Note> results = new ArrayList<>();
for (Note note: getNotesList()) {
if (note.getTitle().trim().toLowerCase().contains(query.trim().toLowerCase()) || note.getContent().trim().toLowerCase().contains(query.trim().toLowerCase())) {
results.add(note);
}
}
return results;
}
/**
* Simple method to convert many int parameters to an int[] array.
* @param categories The varargs int[] array.
* @return int[] array from parameters.
*/
public int[] categories(int... categories) {
return categories;
}
}
MainActivity: (仅相关代码)
public class MainActivity extends AppCompatActivity {
private NotesAdapter notesAdapter;
public static NotesController notesController;
private RecyclerView notesRecyclerView;
private String notesDir;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Answers(), new Crashlytics());
setContentView(R.layout.activity_main);
...
notesDir = ContextCompat.getDataDir(this).getPath() + "/files/notes.json";
notesController = new NotesController(notesDir);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
notesRecyclerView.setLayoutManager(layoutManager);
updateRecyclerView();
notesAdapter.setOnItemActionListener(new NotesAdapter.ActionListener() {
@Override
public void onItemClick(final int position, View v) {
...
}
@Override
public void onItemLongClick(final int position, View v) {
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this).setTitle("Delete?").setMessage("Just for testing.");
dialog.setPositiveButton("DELETE", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
notesController.remove(position);
updateRecyclerView();
}
});
dialog.show();
}
});
...
}
...
private void updateRecyclerView() {
notesAdapter = new NotesAdapter(notesController.getNotesList(), getApplicationContext());
notesRecyclerView.setAdapter(notesAdapter);
notesAdapter.notifyDataSetChanged();
}
}
现在查看 updateRecyclerView()
方法,您会看到我使用来自 NotesController 的新数据再次重新分配适配器,然后通知列表数据已更改。
但我需要以某种方式,在不消除控制器的情况下,在我删除(例如通过长按)或添加某些内容(只是默认的)时制作列表制作删除动画。为此,Android RecyclerView Adapter class 为我们提供了 notifyItemInserted(int)
和 notifyItemRemoved(int)
但它们在我的情况下不起作用(即使删除了中断这些动画的 notifyDataSetChanged() ).
请不要建议我删除 NotesController,因为它有助于从应用程序的不同部分轻松访问笔记,我只需要一种方法让这两种插入和删除通知方法正常工作(任何其他解决方案)欢迎顺便说一句)。
注意:这是我的适配器类型:public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder>
。
每当您想更新 recyclerview 中的项目时,您都不应该创建新的适配器实例。你应该创建一个适配器实例并将它分配给recyclerview,如果你想在recyclerview中添加或删除项目,你只需要替换适配器中的数据。我会推荐 ListAdapter
因为它有功能 submitList(list)
可以很容易地用来更新数据。
此外,如果你想实现动画,你可以使用 this
我按照 Milan Kundacina 的建议使用函数 submitList(list)
解决了这个问题,而不是重新分配适配器。但是由于此函数仅随 ListAdapter 一起提供,而我仅使用 Adapter,因此我创建了自己的函数如下(suuuuuper 简单):
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder> {
private ArrayList<Note> notesList;
...
NotesAdapter(ArrayList<Note> notesList) {
super();
this.notesList = notesList;
}
public void submitList(ArrayList<Note> list) {
this.notesList = list;
}
...
}
并使用这种方式更新列表:
// inside MainActivity:
private void notifyListAdd(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemInserted(position);
}
private void notifyListRemove(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemRemoved(position);
}