Android: если в «последнем» элементе списка просмотра есть анимация, его можно удалить, но анимация «призрак» сохраняется - PullRequest
0 голосов
/ 12 октября 2018

Я изучаю программирование Android, следуя примерам книги, но я все еще новичок в этой области.

Цель книги - создать прогрессивно более совершенное приложение для заметок,начиная с очень простого, затем каждый раз добавляя функции.

Я получил рабочее приложение, которое позволяет добавлять заметки, удалять заметки: каждая добавленная заметка становится элементом в списке и имеет заголовок, текст инекоторые булевы флаги типа «идея», «задача» и «важно».Добавление заметок, для которых установлен «важный» логический флаг, делает «важный» значок видимым.

Внутри заметки хранятся в списке (см. Код ниже).

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

Это работает.

Теперь я следую за следующим шагом: добавление«мигающая» анимация для предметов (заметок) с установленным флагом «Важно».Элементы обычных заметок не анимированы, но «важные» постоянно «мигают», когда они появляются в просмотре списка.

Это работает.

Удаление заметок, одна за другой, все еще работаети каждый элемент удаляется.

Но, если я удаляю все заметки, и мигает последняя - эта последняя заметка фактически «удаляется» из списка - но просмотр списка по-прежнемупоказывает (только) свою анимацию!?!

Но мигающую анимацию, показывающую последний (удаленный) заголовок и текст в элементе списка, нельзя щелкнуть ... проверяя код, который фактически исчез из списка, но не мигающую анимацию.

Кстати, книга на данный момент не охватывает удаление заметок, и это функция, которую я добавил самостоятельно.

модал, показывающий заметку,как сказал btnDelete, он работает следующим образом:

btnDelete.setOnClickListener(
        new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                new AlertDialog.Builder(getContext())
                        .setMessage("Really delete this note?")
                        .setNegativeButton("Cancel",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {
                                        //nothing
                                    }
                                }
                        )
                        .setPositiveButton("Ok",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialogInterface, int i) {

                                        MainActivity mainActivity = (MainActivity) getActivity();
                                        mainActivity.deleteNote(getNoteId());

                                        String msg = "Note " + getNoteId() + " deleted (" + mNote.getTitle() + ")";

                                        Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
                                        dismiss();
                                    }
                                })
                        .show();


            }
        }
);

Я пытался добавлять и удалять многие заметки, мигающие и нет, но единственный способ получить этот «последний» мигающий призрак - это удалить все заметки, но остатьсяс однимпоследняя мигающая заметка.Затем, после удаления этой последней мигающей заметки, мигающий призрак остается.

У меня может быть любое количество мигающих заметок, и, возможно, одна нормальная заметка, но если я удаляю все заметки, оставляя нормальную, то удаляется просто отлично: оставь последнюю мигающую заметку и, даже если она будет удалена,мигающий призрак остается.

Когда эта мигающая призрачная анимация длится вечно, если я добавляю новую заметку, я вижу новый текст заметки и значки поверх мигающей призрачной анимации , поскольку она добавляется в первую очередь.... есть две переоцененные анимации, одна - это старый "призрак", а другая - "обычная" новая заметка ...

Теперь еще немного кода:

Список просмотра

listNote = (ListView) findViewById(R.id.listView);

и "управляется" объектом mNoteAdapter

listNote.setAdapter(mNoteAdapter);

, который расширяет BaseAdapter, переопределяя необходимые методы, а также имеет два метода для добавления и удаления заметок

public void addNote(Note n) {
    noteList.add(n);
    notifyDataSetChanged();
    saveNotes();

}

public void deleteNote(int i) {
    //(I tried getting the view and canceling animation, setting it to null.. nothing works)
    //View listItem = listNote.getAdapter().getView(i, null, listNote);

    noteList.remove(i);

    notifyDataSetChanged();
    saveNotes();
}

добавленные заметки хранятся в списке:

 List<Note> noteList;

основным переопределенным методом является getView, который «волшебным образом» вызывается адаптером при добавлении узла и создает представление списка элементов, включаямигающая анимация, если необходимо.

@Override
public View getView(int i, View view, ViewGroup viewGroup) {

    if (view == null) {

        LayoutInflater inflater = (LayoutInflater)
                getSystemService(getApplicationContext().LAYOUT_INFLATER_SERVICE);

        view = inflater.inflate(R.layout.listitem, viewGroup, false);

    }

    TextView txtTitle = (TextView) view.findViewById(R.id.txtSettingsTitle);
    TextView txtDescription = (TextView) view.findViewById(R.id.txtDescription);
    ImageView ivImportant = (ImageView) view.findViewById(R.id.imageViewImportant);
    ImageView ivTodo = (ImageView) view.findViewById(R.id.imageViewTodo);
    ImageView ivIdea = (ImageView) view.findViewById(R.id.imageViewIdea);

    Note tmpNote = noteList.get(i);

    if (mAnimationSpeed != SettingsActivity.NONE && tmpNote.isImportant()) {
        view.setAnimation(mAnimFlash);
    } else {
        view.setAnimation(mAnimFadeIn);
    }

    if (!tmpNote.isIdea()) ivIdea.setVisibility(View.GONE);
    if (!tmpNote.isImportant()) ivImportant.setVisibility(View.GONE);
    if (!tmpNote.isTodo()) ivTodo.setVisibility(View.GONE);

    txtTitle.setText(tmpNote.getTitle());
    txtDescription.setText(tmpNote.getDescription());

    return view;
}

Мне, конечно, не нужна мигающая функция, но я хочу понять, что происходит и, возможно, решить ее.

мигающая анимация(mAnimFlash) xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:repeatMode="reverse"
        android:repeatCount="infinite"/>
</set>

, в то время как "исчезновение" (mAnimFadeIn, которое не вызывает какие-либо проблемы): xml анимации:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="500"
        android:interpolator="@android:anim/accelerate_interpolator">
    </alpha>
</set>

Может кто-нибудь помочьЯ понимаю, почему эта призрачная анимация сохраняется, именно в этом случае, и как я могу лучше реализовать подобные вещи?

1 Ответ

0 голосов
/ 17 октября 2018

Это немного спекуляция с моей стороны, я не совсем уверен, но я предполагаю, что это поведение является результатом взаимодействия между:

  1. Способ ListView перезапускает и кэшируетпредставления для элементов, которые больше не видны
  2. анимация длится «навсегда»
  3. анимируемое свойство является одним из свойств, используемых для «скрытия» кэшированных представлений, сохраняемых ListViewдля дальнейшего использования.

A ListView - это контейнер, который упорядочивает дочерние представления так, что «кажется», что все элементы видны.Элементы, которые выходят за пределы ListView, на самом деле отсутствуют.Это сделано из соображений производительности (скорость и экономия памяти).Когда пользователь прокручивает и вид исчезает из поля зрения, он «перерабатывается» и переводится в режим ожидания до тех пор, пока не появится другой элемент для повторного использования для этого нового элемента.Он передается как 2-й параметр функции getView().

Это означает, что обычно существует больше представлений, чем видимых элементов.Там может быть место для 10 предметов, и будет 10 видимых предметов и 1-3 скрытых дополнительных, которые будут использоваться для предметов, которые появляются в поле зрения.(см. Как работает механизм утилизации ListView ).

Один недостаток ListView заключается в том, что нет простого способа определить, когда представление перерабатывается (оно становится скрытым).Обычно нам приходится ждать, пока он не будет использован для другого предмета.У RecyclerView есть события, которые нужно обрабатывать, когда представление перерабатывается.

В вашем случае мы можем видеть это, когда вы настраиваете анимацию.Если вы установите mAnimFlash для элемента, то единственный раз, когда он останавливается, это когда вид повторно используется для другого не важного элемента, и он получает анимацию mAnimFadeIn (заменяющую мигающую анимацию).

Если вы прокомментировали вызов view.setAnimation(mAnimFadeIn), вы заметите, что при прокрутке вниз некоторые элементы мигают, когда не должны.Это потому, что нет места, где вид должен перестать мигать, даже после назначения другому элементу в списке.

Теперь перейдем к пункту 3 списка выше, свойству 'alpha'.Вы анимируете свойство 'alpha' представления.Поэтому, если ListView делает вид прозрачным, чтобы скрыть его от вида, то после того, как ListView скрывает его, делая его прозрачным, анимация снова делает его видимым, изменяя значения альфа-прозрачности.

Больше мыслей по этому поводу:

Если бы анимация работала со свойством дочернего элемента представления вместо «основного» представления, вероятно, представление будет оставаться скрытым.Или анимация изменила что-то еще, например цвет текста.

Дополнительная информация / темы:

Дальнейшее исследование можно выполнить с помощью Layout Inspector , который можно использовать для проверкигде находятся виды и их свойства.Например, вы можете проверить, какие дочерние представления находятся внутри ListView и каковы их свойства.

Вы можете использовать разные макеты для разных элементов.См. getItemViewType () и связанные функции класса Adapter.Например, вы можете указать, что существует 2 типа представлений: «важные» и «не важные», и представления, переданные как convertView, будут соответствовать типу для этого элемента, и вам не придется делать выбор в * 1048.*, но выделите решение типа в getItemViewType().

Также взгляните на RecyclerView, так как он лучше обрабатывается в некоторых ситуациях, когда ListView терпит неудачу.Например, адаптер для RecyclerView имеет метод onViewRecycled(), который позволяет вам знать, что представление больше не используется, и вы можете останавливать анимацию там.

...