如何在使用自定义方式保存数据 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);
}