Recycler View: обнаружена несогласованность. Недействительный адаптер держателя держателя ViewViewer
Ошибка несогласованности просмотра Recycler обнаружена, при прокрутке быстрой или прокрутки при загрузке большего количества элементов.
FATAL EXCEPTION: main
Process: com.pratap.endlessrecyclerview, PID: 21997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Адаптер
public class DataAdapter extends RecyclerView.Adapter {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<Feed> mFeed;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
mFeed = feeds;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
@Override
public int getItemViewType(int position) {
return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
}
else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof StudentViewHolder) {
Feed singleStudent= (Feed) mFeed.get(position);
((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
((StudentViewHolder) holder).student= singleStudent;
} else {
ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
public void addFeed(Feed feed) {
mFeed.add(feed);
//mFeed.addAll(0, (Collection<? extends Feed>) feed);
notifyItemInserted(mFeed.size());
//notifyItemRangeInserted(0,mFeed.size());
notifyDataSetChanged();
//notifyItemInserted(mFeed.size());
//setLoaded();
//notifyItemInserted(mFeed.size());
}
public void removeAll(){
mFeed.clear();
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return mFeed.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
public Feed student;
public StudentViewHolder(View v) {
super(v);
tvName = (TextView) v.findViewById(R.id.tvName);
//tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
//public ProgressBar progressBar;
public static ProgressBar PROGRESS_BAR;
public ProgressViewHolder(View v) {
super(v);
PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
// progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
}
активность
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private Toolbar toolbar;
private TextView tvEmptyView;
private RecyclerView mRecyclerView;
private DataAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RestManager mManager;
private List<Feed> mFeed;
SwipeRefreshLayout mSwipeRefreshLayout;
protected Handler handler;
private int currentPage=1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
tvEmptyView = (TextView) findViewById(R.id.empty_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mSwipeRefreshLayout.setOnRefreshListener(this);
//studentList = new ArrayList<Student>();
mFeed = new ArrayList<Feed>();
handler = new Handler();
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Android Students");
}
mManager = new RestManager();
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
// use a linear layout manager
mRecyclerView.setLayoutManager(mLayoutManager);
// create an Object for Adapter
mAdapter = new DataAdapter(mFeed,mRecyclerView);
// set the adapter object to the Recyclerview
mRecyclerView.setAdapter(mAdapter);
// mAdapter.notifyDataSetChanged();
loadData(false);
// if (mFeed.isEmpty()) {
// mRecyclerView.setVisibility(View.GONE);
// tvEmptyView.setVisibility(View.VISIBLE);
//
// } else {
// mRecyclerView.setVisibility(View.VISIBLE);
// tvEmptyView.setVisibility(View.GONE);
// }
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
//add null , so the adapter will check view_type and show progress bar at bottom
mFeed.add(null);
mAdapter.notifyItemInserted(mFeed.size() - 1);
handler.postDelayed(new Runnable() {
@Override
public void run() {
// remove progress item
mFeed.remove(mFeed.size() - 1);
// mAdapter.notifyItemRemoved(mFeed.size());
//add items one by one
int start = mFeed.size();
currentPage++;
Log.d("CurrentPage", String.valueOf(currentPage));
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
@Override
public void onResponse(Call<Results> call, Response<Results> response) {
mSwipeRefreshLayout.setRefreshing(false);
if (response.isSuccess()) {
if (response.body() != null) {
Results feedList = response.body();
// List<Results> newUsers = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
mAdapter.addFeed(feed);
// mAdapter.notifyDataSetChanged();
//mAdapter.notifyItemInserted(mFeed.size());
}
// mAdapter.notifyDataSetChanged();
}
}
}
@Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", "Error");
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
});
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
//
// }
mAdapter.setLoaded();
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
}
});
}
// load initial data
private void loadData(final boolean removePreData) {
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
@Override
public void onResponse(Call<Results> call, Response<Results> response) {
if (response.isSuccess()) {
if (response.body() != null) {
// if(removePreData) mAdapter.removeAll();
Results feedList = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
//mAdapter.notifyDataSetChanged();
mAdapter.addFeed(feed);
}
mSwipeRefreshLayout.setRefreshing(false);
}
}
}
@Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", String.valueOf(t));
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
}
);
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
//
// }
mSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void onRefresh() {
mFeed.clear();
mAdapter.notifyDataSetChanged();
loadData(true);
currentPage=1;
}
}
Ответы
Ответ 1
Он похож на известный андроид ошибка
Существует довольно уродливый, но рабочий подход
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
//... constructor
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
}
}
}
mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));
Для меня это работает без какого-либо побочного эффекта.
Ответ 2
Эта проблема является известной ошибкой RecyclerView. Лучшее решение - очищать список каждый раз перед обновлением RecyclerView.
Для решения этой проблемы просто вызовите notifyDataSetChanged() с пустым списком перед обновлением представления корзины.
Например
//Method for refresh recycle view
if (!yourList.isEmpty())
yourList.clear(); //The list for update recycle view
adapter.notifyDataSetChanged();
Ответ 3
Используйте это, чтобы обновить RecyclerView
items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data
`
Ответ 4
У меня была аналогичная проблема, и это решение помогло мне после добавления нового элемента в мое RV:
recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();
Ответ 5
У меня возникла эта проблема, прокручивая ее через бесконечный /paging RecyclerView
. Корень моей проблемы возник из-за того, что у меня был элемент "header" в начале списка, этот элемент заголовка не был частью источника данных, он был просто вставлен в начало списка adapter
. Поэтому при быстрой прокрутке и добавлении новых страниц элементов в RecyclerView
adapter
и уведомлении adapter
о том, что новые данные были вставлены, я не принимал во внимание дополнительный элемент заголовка, тем самым делая размер списка адаптеров неправильно... и вызывает это исключение...
Итак, если вы используете верхний/нижний колонтитул в нашем адаптере RecyclerView
, обязательно учтите его при обновлении данных адаптеров.
Пример:
public void addNewPageToList(List<MyData> list)
{ //
// Make sure you account for any header/footer in your list!
//
// Add one to the currentSize to account for the header item.
//
int currentSize = this.adapterList.size() + 1;
this.adapterList.addAll(list);
notifyItemRangeInserted(currentSize, this.adapterList.size());
}
Edit:
Я думаю, вы всегда можете использовать метод адаптера getItemCount()
, чтобы получить размер, вместо того, чтобы получать размер из "списка данных" и добавлять к нему. Ваш метод getItemCount()
должен уже принимать во внимание любые дополнительные верхние и нижние колонтитулы /etc, которые у вас есть в вашем списке.
Ответ 6
В моем случае я делал это как notifyItemInserted(position);
Это вызвало у меня эту проблему, тогда я использовал как и он работал отлично. notifyItemRangeInserted(startIndex,endIndex);
Ответ 7
Может быть, вы можете попробовать это до обновления адаптера:
dataList.clear();
patrolListAdapter.notifyDataSetChanged();
Ответ 8
У меня была похожая проблема. Удаление всех видов из RecyclerView помогло мне:
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
Ответ 9
У меня такая же проблема. Воссоздание нового списка таким образом решило проблему совершенно:
в классе адаптера
public void updateItems(List<ComponentEntity> newItems) {
List<ComponentEntity> updatedItems = new ArrayList<>();
updatedItems = newItems;
items = updatedItems;
notifyDataSetChanged();
}
Ответ 10
Попробуйте изменить private List<Feed> mFeed;
в private ArrayList<Feed> mFeed;
И в вашем конструкторе измените mFeed = feeds
на
public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
mFeed.addAll(feeds);
...
Затем вы можете добавить, как обычно, так:
public void addFeed(Feed feed) {
mFeed.add(feed);
notifyItemInserted(mFeed.size());
}
Поэтому изменение List
на ArrayList
решило мою проблему. Я думаю, это потому, что List
является абстрактным классом, и методы add
, remove
, clear
и т.д. На самом деле там не реализованы. Поэтому, когда вы определяете List (private ArrayList<Feed> mFeed;
) и инициируете его с помощью другого List (mFeed = feeds
;), вызов mFeed.add(feed)
ничего не делает и заставляет recyclerView
генерировать исключение.
Ответ 11
это работает для меня, плз попробуйте это sol: https://medium.com/@nhancv/android-fix-java-lang-indexoutofboundsexception-inconsistency-detected-invalid-item-70e9b3b489a2
Ответ 12
Для меня проблема была в том, что я не публиковал notifyDatasetChanged, когда набор данных изменился, когда я реализовал добавочный поиск.
У меня был список, который был отфильтрован на основе того, что пользователь искал в виджете поиска. Для каждого элемента в списке я делал удаленный запрос, а когда получил результат, я обновлял эту конкретную ячейку.
Я должен был сделать оба уведомления для представления переработчика, чтобы работать
Отфильтруйте исходный набор данных, а затем опубликуйте изменение набора данных.
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyDataSetChanged()
}
После получения ответа снова отправьте уведомления
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}
Вы должны публиковать обновления, а не отправлять уведомляющие сообщения напрямую, чтобы предотвратить сбой представления рециркулятора при появлении обновления до его представления.
Еще один важный момент: когда вы публикуете отдельные обновления после удаленного ответа, вы должны убедиться, что список, который видит пользователь, является списком, существовавшим на момент отправки запросов.
Ответ 13
Проблема в этой строке кода:
mFeed = feeds;
Вы назначаете mFeed
к экземпляру вызывающего feeds
таким образом всякий раз, когда абонент изменяет его переменное (может быть добавлением, очистки или удаления элементов), ваш локальный mFeed
изменится
попытаться изменить на
mFeed.addAll(feeds);
не забудьте инициализировать mFeed
для любого списка, который соответствует вашим потребностям, например mFeed = new ArrayList<>();