Perché RecyclerView.notifyItemChanged () creerà un nuovo ViewHolder e utilizza sia il vecchio ViewHolder che quello nuovo?

Recentemente utilizzo RecyclerView e aggiungo una vista intestazione personalizzata (un altro tipo di visualizzazione degli elementi) e provo ad aggiornarlo quando i dati sono cambiati. Accade qualcosa di strano. L'adattatore crea un nuovo HeaderViewHolder e utilizza sia il nuovo HeaderViewHolder che quello vecchio.

Ecco il campione.

  • Onclick LinearLayout non si traduce dopo TranslateAnimation
  • Come distriggersre Google chiedendo l'authorization per controllare regolarmente le applicazioni installate sul mio telefono?
  • Come creare un widget Widget.Holo.Spinner in V7
  • Il presentatore ha conoscenza dell'attività / context una ctriggers idea nel model MVP?
  • Barra di stato trasparente non funziona con windowTranslucentNavigation = "false"
  • Come memorizzare i dati dalla mia app
  • MainActivity.java

    public class MainActivity extends ActionBarActivity { private RecyclerView mRecyclerView; private MyAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView) findViewById(R.id.list); LinearLayoutManager llm = new LinearLayoutManager(this); llm.setSmoothScrollbarEnabled(true); mRecyclerView.setLayoutManager(llm); mRecyclerView.setAdapter(mAdapter = new MyAdapter(this, genItemList())); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } public void addItems(View view) { mAdapter.addItemList(genItemList()); } private List<Item> genItemList() { List<Item> list = new ArrayList<>(50); for (int i = 0; i < 50; i++) { Item item = new Item(); item.text1 = "AAAAAAAAAAAAAAAAAAAAAAAA"; item.text2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; list.add(item); } return list; } public void updateHeader(View view) { mAdapter.updateHeader("Updated header"); } } 

    MyAdapter.java

     public class MyAdapter extends RecyclerView.Adapter { private static final String TAG = "MyAdapter"; private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; private LayoutInflater mInflater; private List<Item> mItemList; private Header mHeader; public MyAdapter(Context context, List<Item> items) { mInflater = LayoutInflater.from(context); mItemList = items != null ? items : new ArrayList<Item>(); mHeader = new Header(); mHeader.text = "header"; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) { switch (type) { case TYPE_HEADER: Log.d(TAG, "create header view holder"); View headerView = mInflater.inflate(android.R.layout.simple_list_item_1, viewGroup, false); return new HeaderViewHolder(headerView); case TYPE_ITEM: View itemView = mInflater.inflate(R.layout.layout_item, viewGroup, false); return new MyViewHolder(itemView); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { if (viewHolder instanceof HeaderViewHolder) { Log.d(TAG, "bind header view holder"); TextView textView = (TextView) viewHolder.itemView.findViewById(android.R.id.text1); textView.setText(mHeader.text); Log.d(TAG, "position: " + position + " holder: " + viewHolder + " text: " + mHeader.text); } else if (viewHolder instanceof MyViewHolder) { Item item = mItemList.get(position - 1) ((MyViewHolder) viewHolder).setText1(item.text1); ((MyViewHolder) viewHolder).setText2(item.text2); } } @Override public int getItemCount() { return mItemList == null ? 0 : mItemList.size() + 1; // plus header } @Override public int getItemViewType(int position) { return position == 0 ? TYPE_HEADER : TYPE_ITEM; } public void addItemList(List<Item> list) { if (list != null) { mItemList.addAll(list); notifyDataSetChanged(); } } public void updateHeader(String text) { mHeader.text = text; notifyItemChanged(0); // notifyDataSetChanged(); } static class HeaderViewHolder extends RecyclerView.ViewHolder { public HeaderViewHolder(View itemView) { super(itemView); } } static class MyViewHolder extends RecyclerView.ViewHolder { TextView mTextView1; TextView mTextView2; public MyViewHolder(View itemView) { super(itemView); mTextView1 = (TextView) itemView.findViewById(R.id.text1); mTextView2 = (TextView) itemView.findViewById(R.id.text2); } public void setText1(String text) { mTextView1.setText(text); } public void setText2(String text) { mTextView2.setText(text); } } } 

    Header.java

     public class Header { public String text; } 

    Item.java

     public class Item { public String text1; public String text2; } 

    activity_main.xml

     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout style="?android:buttonBarStyle" android:layout_width="match_parent" android:layout_height="56dp" android:orientation="horizontal"> <Button android:id="@+id/add" style="?android:buttonBarButtonStyle" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="addItems" android:text="Add items" /> <Button android:id="@+id/update" style="?android:buttonBarButtonStyle" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:onClick="updateHeader" android:text="update header" /> </LinearLayout> </LinearLayout> 

    layout_item.xml

     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="marquee" android:maxLines="1" android:textAppearance="?android:textAppearanceLarge" android:textColor="@android:color/black" /> <TextView android:id="@+id/text2" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="marquee" android:maxLines="1" android:textAppearance="?android:textAppearanceSmall" android:textColor="@android:color/black" /> </LinearLayout> 

    E poi, ecco l'output di logcat quando ho cliccato 3 volte "header di aggiornamento":

     06-05 19:57:50.368 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ create header view holder 06-05 19:57:50.369 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ bind header view holder 06-05 19:57:50.370 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ position: 0 holder: ViewHolder{3f742717 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} text: header 06-05 19:57:54.030 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ create header view holder 06-05 19:57:54.031 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ bind header view holder 06-05 19:57:54.031 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ position: 0 holder: ViewHolder{3ac01621 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} text: Updated header 06-05 19:57:56.938 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ bind header view holder 06-05 19:57:56.938 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ position: 0 holder: ViewHolder{3f742717 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} text: Updated header 06-05 19:57:59.613 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ bind header view holder 06-05 19:57:59.613 20400-20400/com.imaygou.recyclerupdateitemdemo D/MyAdapter﹕ position: 0 holder: ViewHolder{3ac01621 position=0 id=-1, oldPos=-1, pLpos:-1 no parent} text: Updated header 

    Se utilizzo notifyDataSetChanged() invece di notifyItemChanged(0) , tutto funziona bene. Nessuno più ViewHolder. Ma perché?

    Perché creare un nuovo ViewHolder e utilizzare entrambi?

    Qual è la migliore pratica sull'utilizzo di notifyItemChanged(int) ?

  • Quanto è intelligente Eclipse / ADT quando si tratta di progetti di libreria Android?
  • Come centrare Allineare l'elemento del model in PdfPCell
  • Come intercettare i carichi url in WebView (android)?
  • Schede di materiale Android 5.0 con barra degli strumenti
  • Come posso get il nome e il path apk in modo programmato?
  • Problema nel bloccare l'utente nella chatlist utilizzando il server smack e open fire
  • 4 Solutions collect form web for “Perché RecyclerView.notifyItemChanged () creerà un nuovo ViewHolder e utilizza sia il vecchio ViewHolder che quello nuovo?”

    RecyclerView utilizza entrambi ViewHolder per un'animation liscia da un vecchio stato a un nuovo. Questo è il comportmento predefinito di RecyclerView.ItemAnimator.

    Puoi distriggersre l'animation passando un animatore vuoto di oggetti a RecyclerView:

     listView.setItemAnimator(null); 

    Ecco alcuni problemi con la tua implementazione:

    • getItemCount si aspetta il count di tutti gli elementi nel recyclerview incluso l'intestazione in modo da restituire mItemList.size() + 1

    • il field di posizione in onBindViewHolder () si riferisce alla posizione di un elemento nell'integer recyclerview compreso l'intestazione. in modo da bind un elemento non intestazione si farà qualcosa di simile a item = mItemList.get(position - 1) – questo non verrà a mancare perché getItemViewType restituisce un numero maggiore di 0 per TYPE_ITEMs

    In tal modo, notifyItemChanged dovrebbe comportrsi come previsto

    Più soluzione più pulita (non è bug in animatore, ma questa è una caratteristica del gestore di layout):

     mRecyclerView.setLayoutManager(new GridLayoutManager(this, 5, LinearLayoutManager.VERTICAL, false){ @Override public boolean supportsPredictiveItemAnimations() { return false;//super.supportsPredictiveItemAnimations(); } }); 
     ((SimpleItemAnimator) myRecyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 
    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.