【发布时间】:2017-03-28 08:34:44
【问题描述】:
我正在开发Android应用程序并在Play商店发布后,我多次遇到这个问题。
Exception java.lang.IndexOutOfBoundsException: Inconsistency detected.Invalid view holder adapter positionViewHolder{2f9d83c position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition (RecyclerView.java:5297)
android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:5479)
android.support.v7.widget.GapWorker.prefetchPositionWithDeadline (GapWorker.java:282)
android.support.v7.widget.GapWorker.flushTaskWithDeadline (GapWorker.java:336)
android.support.v7.widget.GapWorker.flushTasksWithDeadline (GapWorker.java:349)
android.support.v7.widget.GapWorker.prefetch (GapWorker.java:356)
android.support.v7.widget.GapWorker.run (GapWorker.java:387)
android.os.Handler.handleCallback (Handler.java:815)
android.os.Handler.dispatchMessage (Handler.java:104)
android.os.Looper.loop (Looper.java:207)
android.app.ActivityThread.main (ActivityThread.java:5737)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:789)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:679)
我搜索了很长时间关于这个问题并应用了许多解决方案,但仍然引发了异常
我应用的解决方案之一是制作自定义 LinearLayoutManager:
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
public WrapContentLinearLayoutManager(Context context) {
super(context);
}
//... constructor
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
setAutoMeasureEnabled(false);
} catch (IndexOutOfBoundsException e) {
Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
}
}
public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
setAutoMeasureEnabled(false);
}
public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setAutoMeasureEnabled(false);
}
@Override
public boolean supportsPredictiveItemAnimations() {
return false;
}
}
在我的应用程序中依赖于具有 Load more 和 Swipe to Refresh 功能的 RecyclerView
我使用 Volley 从 Web 服务获取数据以进行 Load more 和 Swipe to Refresh
要在 RecyclerView 中加载更多项目,在 Volley Response 的回调中,我将新项目附加到 ArrayList 并通知适配器,如下所示:
final int positionStart = list.size() - 1;
final int itemCount = response.length();
for (int i=0;i<response.length();i++)
{
try
{
obj = response.getJSONObject(i);
list.add(new ItemEntry(obj.getInt("Id"),obj.getString("Title").trim()obj.getBoolean("isFavorite"));
} catch (JSONException e) {
e.printStackTrace();
}
if(index == 0)
{
// for first Initialize for adapter
adapter = new MyAdapter(latestEntryList,getActivity());
adapter.setOnItemClickCallBack(mainFragment);
recyclerView.setAdapter(adapter);
}
else
{
// for load more items
recyclerView.post(new Runnable()
{
@Override
public void run()
{
adapter.updateItems(positionStart,itemCount);
}
});
}
}
Adapter.UpdateItems 的代码
public void updateItems(int positionStart,int itemCount) {
itemsCount = getItemCount();
this.notifyItemRangeInserted(positionStart,itemCount);
}
如果加载更多,我会执行以下操作;在 OnRefresh 中
public void onRefresh() {
listFragment.LatestEntry(0,false);
// 0 the index and False isAppend to list of not
}
LatestEntry 的代码是:
private void LatestEntry(final int index, final boolean isAppend)
{
String customerId = "123";
String uri = String.format(BASE_URL+"/api/Entry/LatestEntry?customerId=%s&index=%s",customerId,index);
JsonArrayRequest LatestEntryJsonArrayRequest = new JsonArrayRequest(Request.Method.GET, uri,
null, new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response)
{
JSONObject obj = new JSONObject();
if(response.length() > 0 && !isAppend)
{
int startPosition = 0;
int preSize;
if(adapter != null)
preSize = adapter.entries.size();
else
preSize = 0;
if(preSize > 0 && EntryList.size() > 0) {
EntryList.clear();
adapter.entries.clear();
adapter.notifyItemRangeRemoved(startPosition, preSize);
}
}
final int positionStart = EntryList.size() - 1;
final int itemCount = response.length();
for (int i=0;i<response.length();i++)
{
try {
obj = response.getJSONObject(i);
list.add(new ItemEntry
(obj.getInt("Id"),obj.getString("Title").trim()
obj.getBoolean("isFavorite"));
} catch (JSONException e) {
e.printStackTrace();
}
}
if(index == 0)
{ // for first Initialize for adapter
adapter = new MyAdapter(latestEntryList,getActivity());
adapter.setOnItemClickCallBack(mainFragment);
recyclerView.setAdapter(adapter);
}
else
{
// for load more items
recyclerView.post(new Runnable()
{
@Override
public void run() {
adapter.updateItems(positionStart,itemCount);
}
});
}
}
setListShownNoAnimation(true);
if(mainFragment.swipeRefreshLayout.isRefreshing())
mainFragment.swipeRefreshLayout.setRefreshing(false);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MyApplication.getAppContext(),error.getMessage(),Toast.LENGTH_LONG);
}
}
);
LatestEntryJsonArrayRequest.setTag("LatestEntry");
KidsSingleton.getInstance().addToRequestQueue(LatestEntryJsonArrayRequest);
}
请注意以下几点:
- Recyclerview 版本为:recyclerview-v7:25.3.0
- 我在 ViewPager 中有三个 RecylerView
- 每个 ViewPager 都包含 Fragment,而 Fragment 包含带有 RecylerView 的 ListFragment。
- 所有 RecylcrViews 都有一个适配器。
谁能帮助解决这个问题?
【问题讨论】:
-
你的 reccyleview 支持库的版本是多少?
-
我用这个版本recyclerview-v7:25.3.0
-
仅供参考:
onLayoutChildren()在这里不是问题 - 您的堆栈跟踪不会在列表中显示它。 -
这里有完全相同的问题。看起来像
prefetch功能错误。这些错误仅在 Google 在 25.1.0 添加此功能后出现。
标签: android