RecyclerView.Adapter 的 onCreateViewHolder 被调用了两次或更多次,多次
onCreateViewHolder of RecyclerView.Adapter is called twice or more, multiple times
我目前正在开发一个使用 RecyclerView 的应用程序。
在查看 LogCat 时,我注意到 onCreateViewHolder 在实例化后被调用了两次。
09-22 05:22:55.209 V/Adapter﹕ Construct
09-22 05:22:55.213 V/Adapter﹕ onCreateViewHolder
09-22 05:22:55.224 V/Adapter﹕ onBindViewHolder
09-22 05:22:55.240 V/Adapter﹕ onCreateViewHolder
09-22 05:22:55.247 V/Adapter﹕ onBindViewHolder
onBindViewHolder 也被调用了两次,但我知道每当回收项目时都会调用它。
不过我觉得onCreateViewHolder调用一次就够了
这是异常行为吗?如果是,如何解决?
这不是异常,而是很正常的行为。你不用担心。
确实是一个ViewHolder会被回收,不会再重新创建。
但是,需要多个 ViewHolders 才能在应用屏幕上显示多个项目。因此,将创建一定数量的 ViewHolder,并为此调用特定次数的 onCreateViewHolder。
每当数据重置为 ViewHolder 之一时,将无限调用 onBindViewHolder。
我用下面的测试应用程序检查了这个事实:
主要活动:
public class MainActivity extends AppCompatActivity {
private static final String[] DATASET = new String[]{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
MyAdapter myAdapter = new MyAdapter(DATASET);
recyclerView.setAdapter(myAdapter);
}
}
我的适配器:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private static final String LOG_TAG = "RecyclerViewAdapter";
private String[] dataset;
private int counterOnCreateViewHolder = 0;
private int counterOnBindViewHolder = 0;
public MyAdapter(String[] dataset) {
Log.d(LOG_TAG, "Construct");
this.dataset = dataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public ViewHolder(TextView textView) {
super(textView);
this.textView = textView;
}
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(LOG_TAG, "onCreateViewHolder (" + ++counterOnCreateViewHolder + ")");
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.adapter_textview, parent, false);
ViewHolder viewHolder = new ViewHolder((TextView) view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d(LOG_TAG, "onBindViewHolder (" + ++counterOnBindViewHolder + ")");
holder.textView.setText(dataset[position]);
}
@Override
public int getItemCount() {
// Log.d(LOG_TAG, "getItemCount");
return dataset.length;
}
}
layout/activity_main.xml:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:id="@+id/recycler_view" />
layout/adapter_textview.xml:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/adapter_textview"
android:textSize="30sp" />
结果:
09-22 06:03:04.166 D/RecyclerViewAdapter﹕ Construct
09-22 06:03:05.179 D/RecyclerViewAdapter﹕ onCreateViewHolder (1)
09-22 06:03:05.183 D/RecyclerViewAdapter﹕ onBindViewHolder (1)
09-22 06:03:05.190 D/RecyclerViewAdapter﹕ onCreateViewHolder (2)
09-22 06:03:05.192 D/RecyclerViewAdapter﹕ onBindViewHolder (2)
09-22 06:03:05.192 D/RecyclerViewAdapter﹕ onCreateViewHolder (3)
09-22 06:03:05.194 D/RecyclerViewAdapter﹕ onBindViewHolder (3)
09-22 06:03:05.195 D/RecyclerViewAdapter﹕ onCreateViewHolder (4)
09-22 06:03:05.197 D/RecyclerViewAdapter﹕ onBindViewHolder (4)
09-22 06:03:05.198 D/RecyclerViewAdapter﹕ onCreateViewHolder (5)
09-22 06:03:05.199 D/RecyclerViewAdapter﹕ onBindViewHolder (5)
09-22 06:03:05.200 D/RecyclerViewAdapter﹕ onCreateViewHolder (6)
09-22 06:03:05.202 D/RecyclerViewAdapter﹕ onBindViewHolder (6)
09-22 06:03:05.203 D/RecyclerViewAdapter﹕ onCreateViewHolder (7)
09-22 06:03:05.204 D/RecyclerViewAdapter﹕ onBindViewHolder (7)
09-22 06:03:05.206 D/RecyclerViewAdapter﹕ onCreateViewHolder (8)
09-22 06:03:05.207 D/RecyclerViewAdapter﹕ onBindViewHolder (8)
09-22 06:03:05.209 D/RecyclerViewAdapter﹕ onCreateViewHolder (9)
09-22 06:03:05.211 D/RecyclerViewAdapter﹕ onBindViewHolder (9)
09-22 06:03:05.212 D/RecyclerViewAdapter﹕ onCreateViewHolder (10)
09-22 06:03:05.213 D/RecyclerViewAdapter﹕ onBindViewHolder (10)
09-22 06:03:05.215 D/RecyclerViewAdapter﹕ onCreateViewHolder (11)
09-22 06:03:05.217 D/RecyclerViewAdapter﹕ onBindViewHolder (11)
09-22 06:03:05.218 D/RecyclerViewAdapter﹕ onCreateViewHolder (12)
09-22 06:03:05.220 D/RecyclerViewAdapter﹕ onBindViewHolder (12)
09-22 06:03:55.048 D/RecyclerViewAdapter﹕ onCreateViewHolder (13)
09-22 06:03:55.050 D/RecyclerViewAdapter﹕ onBindViewHolder (13)
09-22 06:03:55.228 D/RecyclerViewAdapter﹕ onCreateViewHolder (14)
09-22 06:03:55.229 D/RecyclerViewAdapter﹕ onBindViewHolder (14)
09-22 06:03:55.230 D/RecyclerViewAdapter﹕ onCreateViewHolder (15)
09-22 06:03:55.231 D/RecyclerViewAdapter﹕ onBindViewHolder (15)
09-22 06:03:55.232 D/RecyclerViewAdapter﹕ onBindViewHolder (16)
09-22 06:03:55.232 D/RecyclerViewAdapter﹕ onBindViewHolder (17)
09-22 06:03:55.260 D/RecyclerViewAdapter﹕ onBindViewHolder (18)
09-22 06:03:55.276 D/RecyclerViewAdapter﹕ onBindViewHolder (19)
09-22 06:03:55.296 D/RecyclerViewAdapter﹕ onBindViewHolder (20)
09-22 06:03:55.310 D/RecyclerViewAdapter﹕ onBindViewHolder (21)
如您所见,在onCreateViewHolder (15)
之后只调用了onBindViewHolder。
您可以通过这段代码增加RecycledViewPool 的数量。默认情况下它有 5 个。
可以根据需要增加。
recyclerView.getRecycledViewPool().setMaxRecycledViews(0,50);//0 - your view type
这里是 RecyclerView 稳定视图的稳定答案
下面这些行有助于在垂直方向上保持 recyclerview 中列表数据的一致性
在我的垂直列表中只有 10 个项目,我不想再次重新绑定。
每行都有无限的水平 RecyclerView 列表。
mRecyclerview.getRecycledViewPool().setMaxRecycledViews(int viewtype,int itemsCount)
mRecyclerview.setItemViewCacheSize(int itemsCount)
这两种方法都有助于不回收垂直视图。
但是如果list太大的话会导致OOM错误,所以要小心实现。
Here 0 is viewType in Recyclerview adapter and 10 are the items count.
mRecyclerview.getRecycledViewPool().setMaxRecycledViews(0, 10);
mRecyclerview.setItemViewCacheSize(10);
取决于您的屏幕可以容纳多少行,但大多数情况下肯定多于 1 行。除非数组中只有一行作为参数传递给适配器。
我的应用适合 13 个纵向观看者。但有趣的是,我调用了 17 次 onCreateViewHolders。下面是一段日志语句:
...
D/NerdLauncherActivity: onCreateViewHolder 14
D/NerdLauncherActivity: onBindViewHolder 14
D/NerdLauncherActivity: onCreateViewHolder 15
D/NerdLauncherActivity: onBindViewHolder 15
D/NerdLauncherActivity: onCreateViewHolder 16
D/NerdLauncherActivity: onBindViewHolder 16
D/NerdLauncherActivity: onCreateViewHolder 17
D/NerdLauncherActivity: onBindViewHolder 17
D/NerdLauncherActivity: onBindViewHolder 18
D/NerdLauncherActivity: onBindViewHolder 19
D/NerdLauncherActivity: onBindViewHolder 20
...
我认为 onCreateViewHolder 会被调用 13 次,或最多 14 次,因为当我向下滚动到第 14 行时,第 1 行仍然部分可见。但是回收视图似乎需要一些时间才能开始重用现有的 viewHolder。在第 17 个 viewHolder 之后,不再创建新的 viewHolder。从那时起,现有的 viewHolders 将被回收。
我目前正在开发一个使用 RecyclerView 的应用程序。
在查看 LogCat 时,我注意到 onCreateViewHolder 在实例化后被调用了两次。
09-22 05:22:55.209 V/Adapter﹕ Construct
09-22 05:22:55.213 V/Adapter﹕ onCreateViewHolder
09-22 05:22:55.224 V/Adapter﹕ onBindViewHolder
09-22 05:22:55.240 V/Adapter﹕ onCreateViewHolder
09-22 05:22:55.247 V/Adapter﹕ onBindViewHolder
onBindViewHolder 也被调用了两次,但我知道每当回收项目时都会调用它。
不过我觉得onCreateViewHolder调用一次就够了
这是异常行为吗?如果是,如何解决?
这不是异常,而是很正常的行为。你不用担心。
确实是一个ViewHolder会被回收,不会再重新创建。
但是,需要多个 ViewHolders 才能在应用屏幕上显示多个项目。因此,将创建一定数量的 ViewHolder,并为此调用特定次数的 onCreateViewHolder。
每当数据重置为 ViewHolder 之一时,将无限调用 onBindViewHolder。
我用下面的测试应用程序检查了这个事实:
主要活动:
public class MainActivity extends AppCompatActivity {
private static final String[] DATASET = new String[]{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
MyAdapter myAdapter = new MyAdapter(DATASET);
recyclerView.setAdapter(myAdapter);
}
}
我的适配器:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private static final String LOG_TAG = "RecyclerViewAdapter";
private String[] dataset;
private int counterOnCreateViewHolder = 0;
private int counterOnBindViewHolder = 0;
public MyAdapter(String[] dataset) {
Log.d(LOG_TAG, "Construct");
this.dataset = dataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public ViewHolder(TextView textView) {
super(textView);
this.textView = textView;
}
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(LOG_TAG, "onCreateViewHolder (" + ++counterOnCreateViewHolder + ")");
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.adapter_textview, parent, false);
ViewHolder viewHolder = new ViewHolder((TextView) view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d(LOG_TAG, "onBindViewHolder (" + ++counterOnBindViewHolder + ")");
holder.textView.setText(dataset[position]);
}
@Override
public int getItemCount() {
// Log.d(LOG_TAG, "getItemCount");
return dataset.length;
}
}
layout/activity_main.xml:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:id="@+id/recycler_view" />
layout/adapter_textview.xml:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/adapter_textview"
android:textSize="30sp" />
结果:
09-22 06:03:04.166 D/RecyclerViewAdapter﹕ Construct
09-22 06:03:05.179 D/RecyclerViewAdapter﹕ onCreateViewHolder (1)
09-22 06:03:05.183 D/RecyclerViewAdapter﹕ onBindViewHolder (1)
09-22 06:03:05.190 D/RecyclerViewAdapter﹕ onCreateViewHolder (2)
09-22 06:03:05.192 D/RecyclerViewAdapter﹕ onBindViewHolder (2)
09-22 06:03:05.192 D/RecyclerViewAdapter﹕ onCreateViewHolder (3)
09-22 06:03:05.194 D/RecyclerViewAdapter﹕ onBindViewHolder (3)
09-22 06:03:05.195 D/RecyclerViewAdapter﹕ onCreateViewHolder (4)
09-22 06:03:05.197 D/RecyclerViewAdapter﹕ onBindViewHolder (4)
09-22 06:03:05.198 D/RecyclerViewAdapter﹕ onCreateViewHolder (5)
09-22 06:03:05.199 D/RecyclerViewAdapter﹕ onBindViewHolder (5)
09-22 06:03:05.200 D/RecyclerViewAdapter﹕ onCreateViewHolder (6)
09-22 06:03:05.202 D/RecyclerViewAdapter﹕ onBindViewHolder (6)
09-22 06:03:05.203 D/RecyclerViewAdapter﹕ onCreateViewHolder (7)
09-22 06:03:05.204 D/RecyclerViewAdapter﹕ onBindViewHolder (7)
09-22 06:03:05.206 D/RecyclerViewAdapter﹕ onCreateViewHolder (8)
09-22 06:03:05.207 D/RecyclerViewAdapter﹕ onBindViewHolder (8)
09-22 06:03:05.209 D/RecyclerViewAdapter﹕ onCreateViewHolder (9)
09-22 06:03:05.211 D/RecyclerViewAdapter﹕ onBindViewHolder (9)
09-22 06:03:05.212 D/RecyclerViewAdapter﹕ onCreateViewHolder (10)
09-22 06:03:05.213 D/RecyclerViewAdapter﹕ onBindViewHolder (10)
09-22 06:03:05.215 D/RecyclerViewAdapter﹕ onCreateViewHolder (11)
09-22 06:03:05.217 D/RecyclerViewAdapter﹕ onBindViewHolder (11)
09-22 06:03:05.218 D/RecyclerViewAdapter﹕ onCreateViewHolder (12)
09-22 06:03:05.220 D/RecyclerViewAdapter﹕ onBindViewHolder (12)
09-22 06:03:55.048 D/RecyclerViewAdapter﹕ onCreateViewHolder (13)
09-22 06:03:55.050 D/RecyclerViewAdapter﹕ onBindViewHolder (13)
09-22 06:03:55.228 D/RecyclerViewAdapter﹕ onCreateViewHolder (14)
09-22 06:03:55.229 D/RecyclerViewAdapter﹕ onBindViewHolder (14)
09-22 06:03:55.230 D/RecyclerViewAdapter﹕ onCreateViewHolder (15)
09-22 06:03:55.231 D/RecyclerViewAdapter﹕ onBindViewHolder (15)
09-22 06:03:55.232 D/RecyclerViewAdapter﹕ onBindViewHolder (16)
09-22 06:03:55.232 D/RecyclerViewAdapter﹕ onBindViewHolder (17)
09-22 06:03:55.260 D/RecyclerViewAdapter﹕ onBindViewHolder (18)
09-22 06:03:55.276 D/RecyclerViewAdapter﹕ onBindViewHolder (19)
09-22 06:03:55.296 D/RecyclerViewAdapter﹕ onBindViewHolder (20)
09-22 06:03:55.310 D/RecyclerViewAdapter﹕ onBindViewHolder (21)
如您所见,在onCreateViewHolder (15)
之后只调用了onBindViewHolder。
您可以通过这段代码增加RecycledViewPool 的数量。默认情况下它有 5 个。
可以根据需要增加。
recyclerView.getRecycledViewPool().setMaxRecycledViews(0,50);//0 - your view type
这里是 RecyclerView 稳定视图的稳定答案
下面这些行有助于在垂直方向上保持 recyclerview 中列表数据的一致性 在我的垂直列表中只有 10 个项目,我不想再次重新绑定。 每行都有无限的水平 RecyclerView 列表。
mRecyclerview.getRecycledViewPool().setMaxRecycledViews(int viewtype,int itemsCount)
mRecyclerview.setItemViewCacheSize(int itemsCount)
这两种方法都有助于不回收垂直视图。 但是如果list太大的话会导致OOM错误,所以要小心实现。
Here 0 is viewType in Recyclerview adapter and 10 are the items count. mRecyclerview.getRecycledViewPool().setMaxRecycledViews(0, 10); mRecyclerview.setItemViewCacheSize(10);
取决于您的屏幕可以容纳多少行,但大多数情况下肯定多于 1 行。除非数组中只有一行作为参数传递给适配器。
我的应用适合 13 个纵向观看者。但有趣的是,我调用了 17 次 onCreateViewHolders。下面是一段日志语句:
...
D/NerdLauncherActivity: onCreateViewHolder 14
D/NerdLauncherActivity: onBindViewHolder 14
D/NerdLauncherActivity: onCreateViewHolder 15
D/NerdLauncherActivity: onBindViewHolder 15
D/NerdLauncherActivity: onCreateViewHolder 16
D/NerdLauncherActivity: onBindViewHolder 16
D/NerdLauncherActivity: onCreateViewHolder 17
D/NerdLauncherActivity: onBindViewHolder 17
D/NerdLauncherActivity: onBindViewHolder 18
D/NerdLauncherActivity: onBindViewHolder 19
D/NerdLauncherActivity: onBindViewHolder 20
...
我认为 onCreateViewHolder 会被调用 13 次,或最多 14 次,因为当我向下滚动到第 14 行时,第 1 行仍然部分可见。但是回收视图似乎需要一些时间才能开始重用现有的 viewHolder。在第 17 个 viewHolder 之后,不再创建新的 viewHolder。从那时起,现有的 viewHolders 将被回收。