Android 为什么 RecyclerView Adapter 会崩溃?
Android why is RecyclerView Adapter crashing?
我有一个 RecyclerView
列表,可以在两种布局之间进行选择,一种是默认布局,另一种是使用适配器的 CardView
布局。默认 UI 布局在用户尚未创建任何 CardView
时首先显示。在用户创建并保存 CardView
后,布局切换到 CardView
布局。布局切换使用 "viewType",在 0 和 1 之间切换。
当我使用这两种方法时,布局之间的切换工作正常:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
public int getItemViewType(int position) {
return contactList.size() == 0 ? 0:1;
}
然后我添加了以下方法用于其他用途,但应用程序崩溃了:
public Contact getItem(int position) {
return contactList.get(position);
}
我在这里错过了什么?
Logcat:
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.jdw.v52/com.wimso.v052.MainActivity}:
java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
at android.app.ActivityThread.access0(ActivityThread.java:130)
at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IndexOutOfBoundsException: Invalid index 0, size
is 0
at
java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
at java.util.ArrayList.get(ArrayList.java:304)
at
com.wimso.v052.adapter.ContactListAdapter.getItem(ContactListAdapter.java:95)
at
com.wimso.v052.adapter.ContactListAdapter.clear(ContactListAdapter.java:72)
at com.wimso.v052.MainActivity.loadData(MainActivity.java:171)
at com.wimso.v052.MainActivity.onStart(MainActivity.java:126)
Adapter.java
...
public ContactListAdapter(Context context) {
this.context = context;
this.contactList = new ArrayList<>();
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void add(Contact item) {
if (contactList.size()==0) {
// if list is empty
// remove empty cards first
contactList.clear();
notifyDataSetChanged();
}
contactList.add(item);
notifyItemInserted(contactList.size() -1);
}
public void clear() {
while (getItemCount() > 0) {
remove(getItem(0));
}
}
// Remove an item from the RecyclerView/
public void remove(Contact item) {
if (contactList.size()==0) {
// if no more contacts in list,
// we rebuild from scratch
contactList.clear();
notifyDataSetChanged();
}
int position = contactList.indexOf(item);
if (position > -1) {
contactList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, contactList.size()); // I added this.
}
}
// Get the Item's position.
public Contact getItem(int position) {
return contactList.get(position);
}
// Update the existing List of RecyclerView items.
public void addAll(List<Contact> contactList) {
for (Contact contact : contactList) {
add(contact);
}
}
@Override
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
// if there are zero CardViews, use viewType 0 to get default_layout,
// otherwise provide a viewType of 1 to for each CardView in order to
// show the singlecard_layout.
@Override
public int getItemViewType(int position) {
return contactList.size() == 0 ? 0:1;
}
// Get the Item's Id.
public long getItemId(int position) {
return contactList.get(position).getId();
}
private static class DefaultViewHolder extends RecyclerView.ViewHolder {
DefaultViewHolder(View itemView) {
super(itemView);
}
}
private class ContactViewHolder extends RecyclerView.ViewHolder {
CountDownTimer timer;
...
private ContactViewHolder(View itemView) {
super(itemView);
...
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View itemView = mLayoutInflater.inflate(R.layout.defaultcard_layout, parent, false);
return new DefaultViewHolder(itemView);
} else {
View itemView = mLayoutInflater.inflate(R.layout.list_contact_item, parent, false);
...
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
int type = getItemViewType(position);
if (type == 1) {
Contact contact = contactList.get(position);
final ContactViewHolder holder = (ContactViewHolder) viewHolder;
...
这是问题所在:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
当 contactList
的大小为 0 时,您仍然返回 RecyclerView 至少有 1 个视图。那个视图试图访问你的 contactList
,它已经是 0,因此:
java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
你可以这样做:
public int getItemCount() {
return contactList.size() != null ? contactList.size() : 0;
}
万一你得到一个空的联系人列表。
或
public int getItemCount() {
return contactList.size();
}
如果您从未向适配器输入 null contactList
,只需返回列表大小即可。
问题是您的适配器的 getItem()
和 getItemId()
没有正确检查联系人列表是否为空。由于您的适配器报告大小为 1,即使该列表为空,这些方法也会被调用,因为 RecyclerView 需要显示一个项目。
您似乎试图在适配器没有项目时显示不同的视图。虽然可以单独在适配器中执行此操作,但我认为在布局中使用单独的 View
并在加载联系人列表时相应地更改可见性会更简单、更清晰。这样你的适配器就不需要那么复杂了。
adapter.setItems(list); // or add, whatever
if (list.size() > 0) {
recyclerView.setVisiblity(View.VISIBLE);
emptyView.setVisiblity(View.GONE);
} else {
emptyView.setVisiblity(View.VISIBLE);
recyclerView.setVisiblity(View.GONE);
}
我有一个类似的崩溃是由于托管主题 activity。
<item name="android:windowIsTranslucent">true</item>
我的 activity 是透明的,里面有一个片段,里面有一个 recyclerview。一切都会加载但是在旋转时我得到了同样的错误。我的片段或 recyclerview 实现没有任何问题,它是 activity 上的样式。删除解决了这个问题。
我有一个 RecyclerView
列表,可以在两种布局之间进行选择,一种是默认布局,另一种是使用适配器的 CardView
布局。默认 UI 布局在用户尚未创建任何 CardView
时首先显示。在用户创建并保存 CardView
后,布局切换到 CardView
布局。布局切换使用 "viewType",在 0 和 1 之间切换。
当我使用这两种方法时,布局之间的切换工作正常:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
public int getItemViewType(int position) {
return contactList.size() == 0 ? 0:1;
}
然后我添加了以下方法用于其他用途,但应用程序崩溃了:
public Contact getItem(int position) {
return contactList.get(position);
}
我在这里错过了什么?
Logcat:
FATAL EXCEPTION: main java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.jdw.v52/com.wimso.v052.MainActivity}: java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084) at android.app.ActivityThread.access0(ActivityThread.java:130) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0 at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251) at java.util.ArrayList.get(ArrayList.java:304) at com.wimso.v052.adapter.ContactListAdapter.getItem(ContactListAdapter.java:95) at com.wimso.v052.adapter.ContactListAdapter.clear(ContactListAdapter.java:72) at com.wimso.v052.MainActivity.loadData(MainActivity.java:171) at com.wimso.v052.MainActivity.onStart(MainActivity.java:126)
Adapter.java
...
public ContactListAdapter(Context context) {
this.context = context;
this.contactList = new ArrayList<>();
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void add(Contact item) {
if (contactList.size()==0) {
// if list is empty
// remove empty cards first
contactList.clear();
notifyDataSetChanged();
}
contactList.add(item);
notifyItemInserted(contactList.size() -1);
}
public void clear() {
while (getItemCount() > 0) {
remove(getItem(0));
}
}
// Remove an item from the RecyclerView/
public void remove(Contact item) {
if (contactList.size()==0) {
// if no more contacts in list,
// we rebuild from scratch
contactList.clear();
notifyDataSetChanged();
}
int position = contactList.indexOf(item);
if (position > -1) {
contactList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, contactList.size()); // I added this.
}
}
// Get the Item's position.
public Contact getItem(int position) {
return contactList.get(position);
}
// Update the existing List of RecyclerView items.
public void addAll(List<Contact> contactList) {
for (Contact contact : contactList) {
add(contact);
}
}
@Override
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
// if there are zero CardViews, use viewType 0 to get default_layout,
// otherwise provide a viewType of 1 to for each CardView in order to
// show the singlecard_layout.
@Override
public int getItemViewType(int position) {
return contactList.size() == 0 ? 0:1;
}
// Get the Item's Id.
public long getItemId(int position) {
return contactList.get(position).getId();
}
private static class DefaultViewHolder extends RecyclerView.ViewHolder {
DefaultViewHolder(View itemView) {
super(itemView);
}
}
private class ContactViewHolder extends RecyclerView.ViewHolder {
CountDownTimer timer;
...
private ContactViewHolder(View itemView) {
super(itemView);
...
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 0) {
View itemView = mLayoutInflater.inflate(R.layout.defaultcard_layout, parent, false);
return new DefaultViewHolder(itemView);
} else {
View itemView = mLayoutInflater.inflate(R.layout.list_contact_item, parent, false);
...
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
int type = getItemViewType(position);
if (type == 1) {
Contact contact = contactList.get(position);
final ContactViewHolder holder = (ContactViewHolder) viewHolder;
...
这是问题所在:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
当 contactList
的大小为 0 时,您仍然返回 RecyclerView 至少有 1 个视图。那个视图试图访问你的 contactList
,它已经是 0,因此:
java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
你可以这样做:
public int getItemCount() {
return contactList.size() != null ? contactList.size() : 0;
}
万一你得到一个空的联系人列表。
或
public int getItemCount() {
return contactList.size();
}
如果您从未向适配器输入 null contactList
,只需返回列表大小即可。
问题是您的适配器的 getItem()
和 getItemId()
没有正确检查联系人列表是否为空。由于您的适配器报告大小为 1,即使该列表为空,这些方法也会被调用,因为 RecyclerView 需要显示一个项目。
您似乎试图在适配器没有项目时显示不同的视图。虽然可以单独在适配器中执行此操作,但我认为在布局中使用单独的 View
并在加载联系人列表时相应地更改可见性会更简单、更清晰。这样你的适配器就不需要那么复杂了。
adapter.setItems(list); // or add, whatever
if (list.size() > 0) {
recyclerView.setVisiblity(View.VISIBLE);
emptyView.setVisiblity(View.GONE);
} else {
emptyView.setVisiblity(View.VISIBLE);
recyclerView.setVisiblity(View.GONE);
}
我有一个类似的崩溃是由于托管主题 activity。
<item name="android:windowIsTranslucent">true</item>
我的 activity 是透明的,里面有一个片段,里面有一个 recyclerview。一切都会加载但是在旋转时我得到了同样的错误。我的片段或 recyclerview 实现没有任何问题,它是 activity 上的样式。删除解决了这个问题。