Как сделать анимацию изменения списка при использовании пользовательского способа сохранения данных RecyclerView - PullRequest
1 голос
/ 01 ноября 2019

Я создаю приложение для заметок, которое использует RecyclerView для отображения ArrayList<Note> данных, где Note - это класс данных для каждого элемента, и я сохраняю и извлекаю их, используя другой класс, называемый NotesController (используя 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 предоставляет нам notifyItemInserted(int) и notifyItemRemoved(int), но в моем случае они не работали (даже с удалением notifyDataSetChanged (), который прерывает эти анимации).

Пожалуйста, неПредложите мне исключить NotesController, поскольку он помогает легко получать доступ к заметкам из разных частей приложения, и мне просто нужен способ, чтобы эти два метода уведомления о вставке и удалении работали без проблем (кстати, любое другое решение приветствуется).

Примечание: вот мой тип адаптера: public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder>.

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Не следует создавать новый экземпляр адаптера всякий раз, когда вы хотите обновить элементы в программе recyclerview. Вы должны создать один экземпляр адаптера и назначить его в программе recyclerview. Если вы хотите добавить или удалить элементы в программе recyclerview, вам просто нужно заменить данные в адаптере. Я бы порекомендовал ListAdapter, потому что он имеет функцию submitList(list), которую можно легко использовать для обновления данных.

Также, если вы хотите добиться анимации, вы можете использовать this

0 голосов
/ 02 ноября 2019

Я исправил проблему, используя - как предложено Миланом Кундачиной - функцию submitList(list) вместо переназначения адаптера. Но так как эта функция поставляется только с ListAdapter, пока я использую только Adapter, я создал свой собственный, как показано ниже (suuuuuper simple):

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);
}    
...