Reputation: 1649
I have a RecyclerView
list that chooses between two layouts, a default layout and a CardView
layout with the Adapter. The default UI layout is shown first when the user has not yet created any CardView
's. The layout switches to the CardView
layout after the user creates and saves a CardView
. The layouts switch using "viewType", switching between 0 and 1.
The switching between the layouts was working fine when I used these two methods:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
public int getItemViewType(int position) {
return contactList.size() == 0 ? 0:1;
}
Then I added the below method for another use and the app is crashing:
public Contact getItem(int position) {
return contactList.get(position);
}
What am I missing here?
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.access$600(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;
...
Upvotes: 1
Views: 4658
Reputation: 3431
I have a similar crash that is due to the theme of the hosting activity.
<item name="android:windowIsTranslucent">true</item>
My activity was transparent with a fragment which has a recyclerview inside. Everything would load but upon rotation I get the same error. Nothing is wrong with my fragment or recyclerview implementation it was the style on the activity. Removing fixes the issue.
Upvotes: 0
Reputation: 38585
The problem is your adapter's getItem()
and getItemId()
do not properly check that the contact list is empty. Since your adapter reports a size of 1 even if that list is empty, those methods will be called because the RecyclerView expects to have an item to show.
You seem to be trying to show a different view when the adapter has no items. While it is possible to do this in the adapter alone, I think it would be simpler and cleaner to have a separate View
in your layout and simply change the visibility accordingly whenever you load the list of contacts. This way your adapter doesn't need to be nearly as complicated.
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);
}
Upvotes: 1
Reputation: 1750
Here's the problem:
public int getItemCount() {
return contactList.size()>0 ? contactList.size():1;
}
When the contactList
is 0 in size, you are still returning the RecyclerView to have at least 1 view. That one view tries to access your contactList
which is already 0 and thus this:
java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
You can do this:
public int getItemCount() {
return contactList.size() != null ? contactList.size() : 0;
}
in case you get a null contactList.
or
public int getItemCount() {
return contactList.size();
}
if you never input a null contactList
to the adapter, simply returning the list size will do.
Upvotes: 4