Pourquoi RecyclerView.notifyItemChanged() permet de créer un nouveau ViewHolder et d'utiliser à la fois l'ancien ViewHolder et un nouveau?

Récemment, j'ai utiliser RecyclerView et ajouter un en-tête personnalisé de vue (un autre type de point de vue) et d'essayer de le mettre à jour lorsque les données ont changé. Quelque chose d'étrange se passe. L'adaptateur crée un nouveau HeaderViewHolder et utilise à la fois le nouveau HeaderViewHolder et l'ancien.

Voici l'échantillon.

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>

Et puis, voici le logcat sortie quand j'ai cliqué 3 fois "mise à jour de l'en-tête":

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

Si j'utilise notifyDataSetChanged() au lieu de notifyItemChanged(0), tout fonctionne bien. Pas un de plus ViewHolder. Mais pourquoi?

Pourquoi il va créer un nouveau ViewHolder et utiliser les deux?

Quelle est la meilleure pratique sur l'utilisation de notifyItemChanged(int)?

  • Salut, je rencontre le même problème. Avez-vous trouver une solution/solution de contournement pour appeler notifyItemChanged() sans création d'une nouvelle ViewHolder?
InformationsquelleAutor Trey Cai | 2015-06-05