【问题标题】:Android why is RecyclerView Adapter crashing?Android 为什么 RecyclerView Adapter 会崩溃?
【发布时间】:2017-01-08 03:12:10
【问题描述】:

我有一个RecyclerView 列表,可以在两种布局之间进行选择,默认布局和带有适配器的CardView 布局。当用户尚未创建任何CardView 时,首先显示默认 UI 布局。用户创建并保存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:

致命异常:主要 java.lang.RuntimeException:无法启动活动 组件信息{com.example.jdw.v52/com.wimso.v052.MainActivity}: java.lang.IndexOutOfBoundsException:索引 0 无效,大小为 0 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084) 在 android.app.ActivityThread.access$600(ActivityThread.java:130) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper.java:137) 在 android.app.ActivityThread.main(ActivityThread.java:4745) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:511) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 在 dalvik.system.NativeStart.main(本机方法) 引起:java.lang.IndexOutOfBoundsException: Invalid index 0, size 是 0 在 java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251) 在 java.util.ArrayList.get(ArrayList.java:304) 在 com.wimso.v052.adapter.ContactListAdapter.getItem(ContactListAdapter.java:95) 在 com.wimso.v052.adapter.ContactListAdapter.clear(ContactListAdapter.java:72) 在 com.wimso.v052.MainActivity.loadData(MainActivity.java:171) 在 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;
    ...

【问题讨论】:

    标签: android android-recyclerview android-adapter


    【解决方案1】:

    问题来了:

    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,则只需返回列表大小即可。

    【讨论】:

    • 好的,但是如果我只使用“return contactList.size();”那么当尺寸从 0(应该显示默认布局)到 1 或更多(显示 CardView 布局)时,布局不能正确切换......有什么想法吗?
    • 您不应将 getItemType 用于此目的。这用于在单个 recyclerView 中创建多个视图类型。使用不同的方法。
    • 您对如何更好地在两种 CardView 布局之间切换有什么建议吗?
    • 如果您的布局完全不同,请更改布局管理器并在适配器上调用 notifyDataSetChanged。
    • 或者干脆改变视图类型并调用notifyDataSetChanged
    【解决方案2】:

    问题是您的适配器的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);
    }
    

    【讨论】:

      【解决方案3】:

      由于托管活动的主题,我遇到了类似的崩溃。

      &lt;item name="android:windowIsTranslucent"&gt;true&lt;/item&gt;

      我的活动是透明的,其中有一个内部有 recyclerview 的片段。一切都会加载,但在旋转时我得到同样的错误。我的片段或 recyclerview 实现没有任何问题,它是活动的样式。删除解决了这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-20
        • 1970-01-01
        • 2013-01-31
        • 2014-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多