Eu tenho uma visão do reciclador que funciona perfeitamente em todos os dispositivos, exceto na Samsung. Na Samsung, eu recebo
java.lang.IndexOutOfBoundsException: Inconsistência detectada. Posição do adaptador do suporte da vista inválida
quando eu voltar ao fragmento com a visão do reciclador de outra atividade.
Código do adaptador:
public class FeedRecyclerAdapter extends RecyclerView.Adapter<FeedRecyclerAdapter.MovieViewHolder> {
public static final String getUserPhoto = APIConstants.BASE_URL + APIConstants.PICTURE_PATH_SMALL;
Movie[] mMovies = null;
Context mContext = null;
Activity mActivity = null;
LinearLayoutManager mManager = null;
private Bus uiBus = null;
int mCountOfLikes = 0;
//Constructor
public FeedRecyclerAdapter(Movie[] movies, Context context, Activity activity,
LinearLayoutManager manager) {
mContext = context;
mActivity = activity;
mMovies = movies;
mManager = manager;
uiBus = BusProvider.getUIBusInstance();
}
public void setMoviesAndNotify(Movie[] movies, boolean movieIgnored) {
mMovies = movies;
int firstItem = mManager.findFirstVisibleItemPosition();
View firstItemView = mManager.findViewByPosition(firstItem);
int topOffset = firstItemView.getTop();
notifyDataSetChanged();
if(movieIgnored) {
mManager.scrollToPositionWithOffset(firstItem - 1, topOffset);
} else {
mManager.scrollToPositionWithOffset(firstItem, topOffset);
}
}
// Create new views (called by layout manager)
@Override
public MovieViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.feed_one_recommended_movie_layout, parent, false);
return new MovieViewHolder(view);
}
// Replaced contend of each view (called by layout manager)
@Override
public void onBindViewHolder(MovieViewHolder holder, int position) {
setLikes(holder, position);
setAddToCollection(holder, position);
setTitle(holder, position);
setIgnoreMovieInfo(holder, position);
setMovieInfo(holder, position);
setPosterAndTrailer(holder, position);
setDescription(holder, position);
setTags(holder, position);
}
// returns item count (called by layout manager)
@Override
public int getItemCount() {
return mMovies != null ? mMovies.length : 0;
}
private void setLikes(final MovieViewHolder holder, final int position) {
List<Reason> likes = new ArrayList<>();
for(Reason reason : mMovies[position].reasons) {
if(reason.title.equals("Liked this movie")) {
likes.add(reason);
}
}
mCountOfLikes = likes.size();
holder.likeButton.setText(mContext.getString(R.string.like)
+ Html.fromHtml(getCountOfLikesString(mCountOfLikes)));
final MovieRepo repo = MovieRepo.getInstance();
final int pos = position;
final MovieViewHolder viewHolder = holder;
holder.likeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mMovies[pos].isLiked) {
repo.unlikeMovie(AuthStore.getInstance()
.getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
@Override
public void success(Movie movie, Response response) {
Drawable img = mContext.getResources().getDrawable(R.drawable.ic_like);
viewHolder.likeButton
.setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
if (--mCountOfLikes <= 0) {
viewHolder.likeButton.setText(mContext.getString(R.string.like));
} else {
viewHolder.likeButton
.setText(Html.fromHtml(mContext.getString(R.string.like)
+ getCountOfLikesString(mCountOfLikes)));
}
mMovies[pos].isLiked = false;
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext.getApplicationContext(),
mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG)
.show();
}
});
} else {
repo.likeMovie(AuthStore.getInstance()
.getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
@Override
public void success(Movie movie, Response response) {
Drawable img = mContext.getResources().getDrawable(R.drawable.ic_liked_green);
viewHolder.likeButton
.setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
viewHolder.likeButton
.setText(Html.fromHtml(mContext.getString(R.string.like)
+ getCountOfLikesString(++mCountOfLikes)));
mMovies[pos].isLiked = true;
setComments(holder, position);
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext,
mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG).show();
}
});
}
}
});
}
private void setComments(final MovieViewHolder holder, final int position) {
holder.likeAndSaveButtonLayout.setVisibility(View.GONE);
holder.commentsLayout.setVisibility(View.VISIBLE);
holder.sendCommentButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (holder.commentsInputEdit.getText().length() > 0) {
CommentRepo repo = CommentRepo.getInstance();
repo.sendUserComment(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
holder.commentsInputEdit.getText().toString(), new Callback<Void>() {
@Override
public void success(Void aVoid, Response response) {
Toast.makeText(mContext, mContext.getString(R.string.thanks_for_your_comment),
Toast.LENGTH_SHORT).show();
hideCommentsLayout(holder);
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext, mContext.getString(R.string.cannot_add_comment),
Toast.LENGTH_LONG).show();
}
});
} else {
hideCommentsLayout(holder);
}
}
});
}
private void hideCommentsLayout(MovieViewHolder holder) {
holder.commentsLayout.setVisibility(View.GONE);
holder.likeAndSaveButtonLayout.setVisibility(View.VISIBLE);
}
private void setAddToCollection(final MovieViewHolder holder, int position) {
final int pos = position;
if(mMovies[position].isInWatchlist) {
holder.saveButton
.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);
}
final CollectionRepo repo = CollectionRepo.getInstance();
holder.saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!mMovies[pos].isInWatchlist) {
repo.addMovieToCollection(AuthStore.getInstance().getAuthToken(), 0, mMovies[pos].id, new Callback<MovieCollection[]>() {
@Override
public void success(MovieCollection[] movieCollections, Response response) {
holder.saveButton
.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);
mMovies[pos].isInWatchlist = true;
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext, mContext.getString(R.string.movie_not_added_to_collection),
Toast.LENGTH_LONG).show();
}
});
} else {
repo.removeMovieFromCollection(AuthStore.getInstance().getAuthToken(), 0,
mMovies[pos].id, new Callback<MovieCollection[]>() {
@Override
public void success(MovieCollection[] movieCollections, Response response) {
holder.saveButton
.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_plus, 0, 0, 0);
mMovies[pos].isInWatchlist = false;
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext,
mContext.getString(R.string.cannot_delete_movie_from_watchlist),
Toast.LENGTH_LONG).show();
}
});
}
}
});
}
private String getCountOfLikesString(int countOfLikes) {
String countOfLikesStr;
if(countOfLikes == 0) {
countOfLikesStr = "";
} else if(countOfLikes > 999) {
countOfLikesStr = " " + (countOfLikes/1000) + "K";
} else if (countOfLikes > 999999){
countOfLikesStr = " " + (countOfLikes/1000000) + "M";
} else {
countOfLikesStr = " " + String.valueOf(countOfLikes);
}
return "<small>" + countOfLikesStr + "</small>";
}
private void setTitle(MovieViewHolder holder, final int position) {
holder.movieTitleTextView.setText(mMovies[position].title);
holder.movieTitleTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MovieDetailActivity.openView(mContext, mMovies[position].id, true, false);
}
});
}
private void setIgnoreMovieInfo(MovieViewHolder holder, final int position) {
holder.ignoreMovie.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MovieRepo repo = MovieRepo.getInstance();
repo.hideMovie(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
new Callback<Void>() {
@Override
public void success(Void aVoid, Response response) {
Movie[] newMovies = new Movie[mMovies.length - 1];
for (int i = 0, j = 0; j < mMovies.length; i++, j++) {
if (i != position) {
newMovies[i] = mMovies[j];
} else {
if (++j < mMovies.length) {
newMovies[i] = mMovies[j];
}
}
}
uiBus.post(new MoviesChangedEvent(newMovies));
setMoviesAndNotify(newMovies, true);
Toast.makeText(mContext, mContext.getString(R.string.movie_ignored),
Toast.LENGTH_SHORT).show();
}
@Override
public void failure(RetrofitError error) {
Toast.makeText(mContext, mContext.getString(R.string.movie_ignored_failed),
Toast.LENGTH_LONG).show();
}
});
}
});
}
private void setMovieInfo(MovieViewHolder holder, int position) {
String imdp = "IMDB: ";
String sources = "", date;
if(mMovies[position].showtimes != null && mMovies[position].showtimes.length > 0) {
int countOfSources = mMovies[position].showtimes.length;
for(int i = 0; i < countOfSources; i++) {
sources += mMovies[position].showtimes[i].name + ", ";
}
sources = sources.trim();
if(sources.charAt(sources.length() - 1) == ',') {
if(sources.length() > 1) {
sources = sources.substring(0, sources.length() - 2);
} else {
sources = "";
}
}
} else {
sources = "";
}
imdp += mMovies[position].imdbRating + " | ";
if(sources.isEmpty()) {
date = mMovies[position].releaseYear;
} else {
date = mMovies[position].releaseYear + " | ";
}
holder.movieInfoTextView.setText(imdp + date + sources);
}
private void setPosterAndTrailer(final MovieViewHolder holder, final int position) {
if (mMovies[position] != null && mMovies[position].posterPath != null
&& !mMovies[position].posterPath.isEmpty()) {
Picasso.with(mContext)
.load(mMovies[position].posterPath)
.error(mContext.getResources().getDrawable(R.drawable.noposter))
.into(holder.posterImageView);
} else {
holder.posterImageView.setImageResource(R.drawable.noposter);
}
holder.posterImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MovieDetailActivity.openView(mActivity, mMovies[position].id, false, false);
}
});
if(mMovies[position] != null && mMovies[position].trailerLink != null
&& !mMovies[position].trailerLink.isEmpty()) {
holder.playTrailer.setVisibility(View.VISIBLE);
holder.playTrailer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MovieDetailActivity.openView(mActivity, mMovies[position].id, false, true);
}
});
}
}
private void setDescription(MovieViewHolder holder, int position) {
String text = mMovies[position].overview;
if(text == null || text.isEmpty()) {
holder.descriptionText.setText(mContext.getString(R.string.no_description));
} else if(text.length() > 200) {
text = text.substring(0, 196) + "...";
holder.descriptionText.setText(text);
} else {
holder.descriptionText.setText(text);
}
final int pos = position;
holder.descriptionText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MovieDetailActivity.openView(mActivity, mMovies[pos].id, false, false);
}
});
}
private void setTags(MovieViewHolder holder, int position) {
List<String> tags = Arrays.asList(mMovies[position].tags);
if(tags.size() > 0) {
CastAndTagsFeedAdapter adapter = new CastAndTagsFeedAdapter(tags,
mContext, ((FragmentActivity) mActivity).getSupportFragmentManager());
holder.tags.setItemMargin(10);
holder.tags.setAdapter(adapter);
} else {
holder.tags.setVisibility(View.GONE);
}
}
// class view holder that provide us a link for each element of list
public static class MovieViewHolder extends RecyclerView.ViewHolder {
TextView movieTitleTextView, movieInfoTextView, descriptionText, reasonsCountText;
TextView reasonText1, reasonAuthor1, reasonText2, reasonAuthor2;
EditText commentsInputEdit;
Button likeButton, saveButton, playTrailer, sendCommentButton;
ImageButton ignoreMovie;
ImageView posterImageView, userPicture1, userPicture2;
TwoWayView tags;
RelativeLayout mainReasonsLayout, firstReasonLayout, secondReasonLayout, reasonsListLayout;
RelativeLayout commentsLayout;
LinearLayout likeAndSaveButtonLayout;
ProgressBar progressBar;
public MovieViewHolder(View view) {
super(view);
movieTitleTextView = (TextView)view.findViewById(R.id.movie_title_text);
movieInfoTextView = (TextView)view.findViewById(R.id.movie_info_text);
descriptionText = (TextView)view.findViewById(R.id.text_description);
reasonsCountText = (TextView)view.findViewById(R.id.reason_count);
reasonText1 = (TextView)view.findViewById(R.id.reason_text_1);
reasonAuthor1 = (TextView)view.findViewById(R.id.author_1);
reasonText2 = (TextView)view.findViewById(R.id.reason_text_2);
reasonAuthor2 = (TextView)view.findViewById(R.id.author_2);
commentsInputEdit = (EditText)view.findViewById(R.id.comment_input);
likeButton = (Button)view.findViewById(R.id.like_button);
saveButton = (Button)view.findViewById(R.id.save_button);
playTrailer = (Button)view.findViewById(R.id.play_trailer_button);
sendCommentButton = (Button)view.findViewById(R.id.send_button);
ignoreMovie = (ImageButton)view.findViewById(R.id.ignore_movie_imagebutton);
posterImageView = (ImageView)view.findViewById(R.id.poster_image);
userPicture1 = (ImageView)view.findViewById(R.id.user_picture_1);
userPicture2 = (ImageView)view.findViewById(R.id.user_picture_2);
tags = (TwoWayView)view.findViewById(R.id.list_view_feed_tags);
mainReasonsLayout = (RelativeLayout)view.findViewById(R.id.reasons_main_layout);
firstReasonLayout = (RelativeLayout)view.findViewById(R.id.first_reason);
secondReasonLayout = (RelativeLayout)view.findViewById(R.id.second_reason);
reasonsListLayout = (RelativeLayout)view.findViewById(R.id.reasons_list);
commentsLayout = (RelativeLayout)view.findViewById(R.id.comments_layout);
likeAndSaveButtonLayout = (LinearLayout)view
.findViewById(R.id.like_and_save_buttons_layout);
progressBar = (ProgressBar)view.findViewById(R.id.centered_progress_bar);
}
}
}
Exceção:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{42319ed8 position=1 id=-1, oldPos=0, pLpos:0 scrap tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4166)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4297)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4278)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1947)
at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:434)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1322)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:556)
at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2627)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2971)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:562)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-30 12:48:22.688 9590-9590/com.Filmgrail.android.debug W/System.err? at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
at android.view.View.layout(View.java:15746)
at android.view.ViewGroup.layout(ViewGroup.java:4867)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2356)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2069)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6630)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:573)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5479)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
Como posso consertar isso?
android
android-recyclerview
Владимир Фишер
fonte
fonte
Respostas:
Esse problema é causado por
RecyclerView
dados modificados em segmentos diferentes. A melhor maneira é verificar todo o acesso a dados. E uma solução alternativa está sendo resolvidaLinearLayoutManager
.Resposta anterior
Na verdade, havia um bug no RecyclerView e o suporte 23.1.1 ainda não foi corrigido.
Para uma solução alternativa, observe que o backtrace empilha, se conseguirmos capturar isso
Exception
em uma classe, ele poderá pular essa falha. Para mim, eu crioLinearLayoutManagerWrapper
e substituo oonLayoutChildren
:Em seguida, defina-o como
RecyclerView
:Na verdade, pegue essa exceção e parece não ter nenhum efeito colateral ainda.
Além disso, se você usar
GridLayoutManager
ouStaggeredGridLayoutManager
precisar criar um wrapper para ele.Aviso: O
RecyclerView
pode estar em um estado interno errado.fonte
LinearLayoutManager
e substituir isso. Eu vou uma adição na minha resposta.Este é um exemplo para atualizar dados com conteúdo completamente novo. Você pode modificá-lo facilmente para atender às suas necessidades. Eu resolvi isso no meu caso chamando:
antes:
Esta é a solução correta e também é mencionada neste post por um membro do projeto AOSP.
fonte
notifyItemRangeInserted
e tem esse problema com alguns dispositivos SamsungnotifyItemRangeInserted
Eu enfrentei esse problema uma vez e resolvi isso envolvendo as
LayoutManager
animações preditivas desativadas e desativadas.Aqui está um exemplo:
E configure-o para
RecyclerView
:fonte
public boolean supportsPredictiveItemAnimations() { return false; }
LinearLayoutManager
diz que o padrão é falso, mas essa declaração é falsa :-( O código descompilado paraLinearLayoutManager
tem o seguinte: public boolean supportPredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}Nova resposta: use o DiffUtil para todas as atualizações do RecyclerView. Isso ajudará no desempenho e no bug acima. Veja aqui
Resposta anterior: Isso funcionou para mim. A chave é não usar
notifyDataSetChanged()
e fazer as coisas certas na ordem correta:fonte
Razões que causaram esse problema:
SOLUÇÃO:
----------------- SOLUÇÃO 1 ---------------
Crie um LinearLayoutManager personalizado como a seguir e defina-o como ReyclerView
Em seguida, defina o RecyclerVIew Layout Manager da seguinte maneira:
----------------- SOLUÇÃO 2 ---------------
Novamente, crie um Linear Layout Manager personalizado da seguinte maneira:
Em seguida, defina o RecyclerVIew Layout Manager da seguinte maneira:
----------------- SOLUÇÃO 3 ---------------
----------------- SOLUÇÃO 4 ---------------
fonte
Eu tive um problema parecido.
Problema no código de erro abaixo:
Solução:
fonte
newList.size() - 1
.De acordo com esse problema , o problema foi resolvido e provavelmente foi lançado em algum momento próximo ao início de 2015. Uma citação desse mesmo segmento :
Se você ainda estiver tendo problemas com uma versão recente da biblioteca de suporte, sugiro revisar suas chamadas para
notifyXXX
(especificamente, seu uso denotifyDataSetChanged
) dentro do adaptador, para garantir que você esteja cumprindo oRecyclerView.Adapter
contrato (um tanto delicado / obscuro) . Certifique-se também de emitir essas notificações no thread principal.fonte
Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
Eu tive o mesmo problema. Foi causado porque atrasei a notificação do adaptador sobre a inserção do item.
Mas
ViewHolder
tentou redesenhar alguns dados em sua exibição e iniciou aRecyclerView
contagem e contagem de crianças contadas - naquele momento falhou (a lista de itens e seu tamanho já estavam atualizados, mas o adaptador ainda não foi notificado).fonte
Isso acontece quando você especifica a posição incorreta para o notifyItemChanged, notifyItemRangeInserted etc.
Antes: (Errôneo)
Depois: (Correto)
fonte
notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);
e nãonotifyItemRangeInserted(initialSize, list.size());
?initialSize
elist
tamanho. Portanto, as duas variantes estão erradas.notifyItemRangeInserted(initialSize, list.size()-1);
mas eu não entendo. Por que tenho que reduzir o tamanho inserido em um para o itemCount?outro motivo pelo qual esse problema ocorre é quando você chama esses métodos com índices incorretos (índices que NÃO ocorreram inseridos ou removidos)
-notifyItemRangeRemoved
-notifyItemRemoved
-notifyItemRangeInserted
-notifyItemInserted
verifique os parâmetros indexados nesses métodos e verifique se eles são precisos e corretos.
fonte
Esse bug ainda não foi corrigido na 23.1.1, mas uma solução comum seria capturar a exceção.
fonte
Pode confirmar o encadeamento como um problema e, desde que deparei com o problema, o RxJava está se tornando cada vez mais popular: verifique se você está usando
.observeOn(AndroidSchedulers.mainThread())
sempre que está ligandonotify[whatever changed]
exemplo de código do adaptador:
fonte
No meu caso, toda vez que eu chamo notifyItemRemoved (0), ele travava. Aconteceu que eu ajustei
setHasStableIds(true)
egetItemId
retornei a posição do item. Acabei atualizando-o para retornar ohashCode()
ID exclusivo do item ou auto-definido, que resolveu o problema.fonte
No meu caso, eu estava tendo esse problema devido a atualizações de dados do servidor (estou usando o Firebase Firestore) e, enquanto o primeiro conjunto de dados está sendo processado pelo DiffUtil em segundo plano, outro conjunto de atualizações de dados ocorre e causa um problema de simultaneidade. iniciando outro DiffUtil.
Em resumo, se você estiver usando o DiffUtil em um encadeamento em segundo plano, que retornará ao encadeamento principal para despachar os resultados para o RecylerView, será possível obter esse erro quando várias atualizações de dados ocorrerem em pouco tempo.
Resolvi isso seguindo os conselhos desta maravilhosa explicação: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2
Só para explicar a solução é enviar as atualizações enquanto a atual está sendo executada no Deque. O deque pode executar as atualizações pendentes assim que a atual terminar, manipulando todas as atualizações subsequentes, mas evitando erros de inconsistência também!
Espero que isso ajude, porque este me fez coçar a cabeça!
fonte
O problema ocorreu para mim somente quando:
Eu criei o adaptador com uma lista vazia . Então eu inseri itens e liguei
notifyItemRangeInserted
.Solução:
Resolvi isso criando o adaptador somente depois de ter o primeiro pedaço de dados e inicializando-o imediatamente. O próximo pedaço poderia ser inserido e
notifyItemRangeInserted
chamado sem nenhum problema.fonte
notifyItemRangeInserted
, mas nunca tive essa exceção por lá.Meu problema era que, apesar de limpar a lista de matrizes que contém o modelo de dados para a exibição do reciclador, não notifiquei o adaptador sobre essa alteração; portanto, ele tinha dados obsoletos do modelo anterior. O que causou confusão sobre a posição do suporte da vista. Para corrigir isso, sempre notifique o adaptador que o conjunto de dados foi alterado antes de atualizar novamente.
fonte
No meu caso, eu estava alterando os dados anteriormente dentro de um encadeamento com mRecyclerView.post (novo Runnable ...) e depois novamente alterei os dados no encadeamento da interface do usuário, o que causou inconsistência.
fonte
O erro pode ser causado por suas alterações serem inconsistentes com o que você está notificando. No meu caso:
O que eu obviamente tinha que fazer:
fonte
No meu caso, o problema era que eu usei notifyDataSetChanged quando a quantidade de dados recém-carregados era menor que os dados iniciais. Essa abordagem me ajudou a:
fonte
notifyDataSetChanged
depende de novos dados? Eu pensei que iria atualizar lista inteira.Eu tive o mesmo problema.
Meu aplicativo usa componentes de navegação com um fragmento contendo meu recyclerView. Minha lista foi exibida bem na primeira vez em que o fragmento foi carregado ... mas, ao navegar e voltar, esse erro ocorreu.
Ao navegar, o ciclo de vida do fragmento passou apenas pelo onDestroyView e, ao retorná-lo, começou pelo onCreateView. No entanto, meu adaptador foi inicializado no onCreate do fragmento e não foi reinicializado ao retornar.
A correção foi inicializar o adaptador no onCreateView.
Espero que isso ajude alguém.
fonte
Eu recebi esse erro porque estava chamando um método por engano para remover uma linha específica da minha reciclerview várias vezes. Eu tinha um método como:
Eu estava acidentalmente chamando esse método três vezes em vez de uma vez, então a segunda vez
loc
foi -1 e o erro foi dado ao tentar removê-lo. As duas correções eram para garantir que o método fosse chamado apenas uma vez e também para adicionar uma verificação de integridade como esta:fonte
Eu tenho o mesmo problema e li que isso aconteceu apenas nos telefones Samsung ... Mas a realidade mostrou que isso acontece em muitas marcas.
Após o teste, percebi que isso acontece apenas quando você rolar rapidamente o RecyclerView e voltar com o botão Voltar ou o botão Acima. Então eu coloquei dentro do botão Para cima e onBackpressed o trecho abaixo:
Com esta solução, você apenas carrega uma nova lista de matriz no adaptador e um novo adaptador no recyclerView e termina a atividade.
Espero que ajude alguém
fonte
Eu recebi esse erro porque estava chamando "notifyItemInserted" duas vezes por engano.
fonte
No meu caso, tive mais de 5000 itens na lista. Meu problema era que, ao rolar a exibição do reciclador, às vezes o "onBindViewHolder" é chamado enquanto o método "myCustomAddItems" está alterando a lista.
Minha solução foi adicionar "sincronizado (syncObject) {}" a todos os métodos que alteram a lista de dados. Dessa forma, a qualquer momento, apenas um método pode ler esta lista.
fonte
No meu caso, os dados do adaptador foram alterados. E eu estava errado usar notifyItemInserted () para essas alterações. Quando uso o notifyItemChanged, o erro desapareceu.
fonte
Encontrei o mesmo problema quando removi e atualizei os itens da lista ... Após dias de investigação, acho que finalmente encontrei uma solução.
O que você precisa fazer é primeiro fazer todo o conteúdo
notifyItemChanged
da sua lista e só depois fazer tudonotifyItemRemoved
em ordem decrescenteEspero que isso ajude as pessoas que estão enfrentando o mesmo problema ...
fonte
Estou usando um Cursor, portanto não posso usar o DiffUtils como proposto nas respostas populares. Para fazê-lo funcionar, desabilito as animações quando a lista não está ociosa. Esta é a extensão que corrige esse problema:
Então você pode atualizar seu adaptador assim
fonte
Se o problema ocorrer após o multitoque, você poderá desativar o multitoque com
no arquivo de layout.
fonte
Se seus dados mudarem muito, você pode usar
ou alguns itens únicos no seu conjunto de dados são alterados, você pode usar
Para o uso de métodos detalhados, você pode consultar o documento , de certa forma, tente não usar diretamente
mAdapter.notifyDataSetChanged()
.fonte
notifyItemRangeChanged
também produz a mesma falha.