Adapter RecyclerView notifyDataSetChanged zatrzymuje fantazyjną animację

Buduję komponent oparty na RecyclerView, pozwalając użytkownikowi zmieniać kolejność elementów poprzez przeciąganie i upuszczanie. Gdy jestem po stronie Draglistenera, potrzebuję pozycji, którą ma w adapterze, aby wykonać poprawny ruch, ale mam tylko dostęp do widoku. Oto, co robię w wiązaniu widoku adaptera:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    Track track = mArray.get(position);
    viewHolder.itemView.setTag(R.string.TAG_ITEM_POSITION, position);
}
Czy wydaje ci się to właściwe ? Bo jak przeniosę taki przedmiot:
public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
}

Wtedy znacznik pozycji nie jest już poprawny, a jeśli I notifyDataSetChanged (), tracę fantazyjną animację. Jakieś sugestie ?

Author: elgui, 2014-12-04

7 answers

Nie, to jest złe. Po pierwsze, nie można odnieść się do pozycji przekazanej onBindViewHolder po zwróceniu tej metody. RecyclerView nie przywróci widoku, gdy zmieni się jego pozycja (z powodu poruszania się elementów itp.).

Zamiast tego możesz użyć ViewHolder#getPosition(), który zwróci Ci zaktualizowaną pozycję.

Jeśli to naprawisz, Twój kod ruchu powinien działać i zapewniać ładne animacje.

Wywołanie notifyDataSetChanged zapobiegnie predykcyjnym animacjom, więc unikaj ich tak długo, jak możesz. Zobacz też dokumentacja dla szczegółów.

Edit (z komentarza): aby uzyskać pozycję z zewnątrz, Pobierz uchwyt widoku dziecka z recyclerview, a następnie pobierz pozycję z vh. Zobacz RecyclerView api dla szczegółów

 6
Author: yigit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-05 14:10:13

Istnieje sposób na zachowanie fantazyjnych animacji za pomocą notifyDataSetChanged()

  1. Musisz utworzyć własną GridLayoutManager z nadpisaną metodą supportsPredictiveItemAnimations() zwracającą true;

  2. Musisz mAdapter.setHasStableIds(true)

  3. Część, którą uważam za trudną, polega na tym, że musisz obejść metodę getItemId() adaptera. Powinna zwracać wartość, która jest naprawdę unikalna, a nie bezpośrednia funkcja position. Coś jak mItems.get(position).hashCode()

W moim przypadku działało idealnie-piękne animacje dla dodawanie, usuwanie i przenoszenie elementów tylko za pomocą notifyDataSetChanged()

 124
Author: tochkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-22 09:10:48

1) użyjesz notifyItemInserted(position); lub notifyItemRemoved(position); zamiast notifyDataSetChanged() do animacji. 2) możesz po prostu ręcznie rozwiązać swój problem - używając

public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
    ViewHolder fromHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(from);
    ViewHolder toHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(to);
    Tag fromTag = fromHolder.itemView.getTag();
    fromHolder.itemView.setTag(toHolder.itemView.getTag()); 
    toHolder.itemView.setTag(fromTag);

}
 3
Author: Rahim Rahimov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-05 09:39:36

Należy przenieść swoją metodę na OnCreateViewHolder, a następnie notifyItemRemoved(index) działa poprawnie.

 0
Author: Dominik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-21 15:01:43

Jestem w stanie utrzymać animacje dotykowe, dodając to do zewnętrznego elementu mojej listy

<View
    android:foreground="?android:attr/selectableItemBackground"
...>
 0
Author: fix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-27 02:59:33

Naprawiłem to używając 'notifyItemChanged (int position); 'zamiast' notifyDataSetChanged (); '

Mój adapter pokazuje fantazyjne animacje doskonale i bez żadnych opóźnień

Edit: dostałem pozycję z pozycji onBindViewHolder.

 0
Author: E. Kolver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-11 08:48:32

Jak stwierdzili inni powyżej, możesz mieć animację podczas korzystania z notifyDataSetChanged na adapterze, chociaż musisz konkretnie użyć stabilnych identyfikatorów. jeśli identyfikatory elementów są ciągami znaków, możesz wygenerować długi identyfikator dla każdego posiadanego ciągu znaków i zachować je na mapie. na przykład:

class StringToLongIdMap {
    private var stringToLongMap = HashMap<String, Long>()
    private var longId: Long = 0

    fun getLongId(stringId: String): Long {
        if (!stringToLongMap.containsKey(stringId)) {
            stringToLongMap[stringId] = longId++
        }
        return stringToLongMap[stringId] ?: -1
    }
}

A następnie w adapterze:

private var stringToLongIdMap = StringToLongIdMap()

override fun getItemId(position: Int): Long {
    val item = items[position]
    return stringToLongIdMap.getLongId(item.id)
}

Kolejna przydatna rzecz do rozważenia, jeśli używasz klasy danych kotlin jako elementów w adapterze, a nie masz id, możesz użyć hashCode samej klasy danych jako stable id (jeśli masz pewność, że kombinacja właściwości elementu jest unikalna w twoim zestawie danych):

override fun getItemId(position: Int): Long = items[position].hashCode().toLong()
 0
Author: Raphael C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-08 07:31:50