Intereting Posts
Как изменить ширину видеообзора программно Извлечение данных из базы данных в список Пользовательский ListView не выделяет ListViewItem при нажатии Понимание Android «Аппарат радиостанции» для лучшего времени автономной работы Как я могу создавать HTML-страницы, которые автоматически корректируют свои изображения на разные размеры экрана для мобильных устройств? Каков наилучший способ конвертировать из RectF в Rect в Android? Трудности с использованием Model-View-Presenter в Android Ошибка раздувания класса android.support.v7.widget.Toolbar? Могу ли я настроить IVR на свой Android-телефон? Пользовательская строка или заголовок пользовательского агента без изменения кордовы libs Настройка цвета заголовка приложения в обзоре (последние приложения) SelectableItemBackground crashing App Интеграция приложения с результатами поиска Google и Chrome IntelliJ IDEA публикует приложение Android одним щелчком мыши? Создание прозрачности TileOverlays

Обновление элемента RecyclerView + асинхронный сетевой вызов

У меня есть recyclerview, который работает, как ожидалось. У меня есть кнопка в макете, которая заполняет список. Предполагается, что кнопка должна выполнить асинхронный вызов, и по результату я изменил внешний вид кнопки. Все это происходит хорошо.

Но когда я нажимаю на кнопку и быстро прокручиваю список, результат асинхронного вызова обновляет кнопку нового представления (вид, который вместо старого). Как мне это сделать? Могу ли я иметь ручку, когда конкретное представление используется повторно?

Обновить :

Кодовая часть класса адаптера, которая выполняет асинхронный вызов и обновление ui.

@Override public void onBindViewHolder(CommentsViewHolder holder, int position) { try { Comments comment = comments.get(position); holder.bindView(comment,position); } catch(Exception ex){ex.printStackTrace();} } @Override public int getItemCount() { if(comments==null) {return 0;} return comments.size(); //return comments.length(); } public class CommentsViewHolder extends RecyclerView.ViewHolder { TextView score ; TextView commentText; TextView commentTime; TextView avatarId; ImageButton minusOne; ImageButton plusOne; ParseObject model; public CommentsViewHolder(View itemView) { super(itemView); //itemView.setBackgroundColor(Color.DKGRAY); minusOne =(ImageButton)itemView.findViewById(R.id.decScore); plusOne =(ImageButton)itemView.findViewById(R.id.incScore); commentText = (TextView)itemView.findViewById(R.id.comment); score = (TextView)itemView.findViewById(R.id.commentScore); commentTime =(TextView)itemView.findViewById(R.id.commentTime); avatarId = (TextView)itemView.findViewById(R.id.ivUserAvatar); } public void bindView(Comments comment, int position) { commentText.setText(comment.getCommentText()); score.setText(Integer.toString(comment.getScore())); String timeText = DateUtils.getRelativeTimeSpanString( comment.getCreatedAt().getTime(), System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString(); timeText = timeText.replace("hours","hrs"); timeText = timeText.replace("seconds","secs"); timeText = timeText.replace("minutes","mins"); commentTime.setText(timeText); int commentHandler = comment.getCommenterHandle(); String commenterNumber = ""; if(commentHandler==0) { commenterNumber = "OP"; } else{ commenterNumber = "#"+commentHandler; } avatarId.setText( commenterNumber); model = comment; String choice = "none"; minusOne.setEnabled(true); plusOne.setEnabled(true); minusOne.setVisibility(View.VISIBLE); plusOne.setVisibility(View.VISIBLE); for (ParseObject choiceIter : choices) { if ((choiceIter.getParseObject("comment").getObjectId()).equals(comment.getObjectId())) { choice = choiceIter.getString("userChoice"); break; } } Log.i("debug",comment.getCommentText()+" "+comment.getScore()+" "+choice); switch (choice) { case "plusOne": Log.i("darkplus","setting darkplus"); plusOne.setImageResource(R.drawable.ic_add_circle_black_18dp); plusOne.setOnClickListener(reversePlusOneOnClickListener); //minusOne.setOnClickListener(minusOneOnClickListener); minusOne.setVisibility(View.GONE); break; case "minusOne": Log.i("darkminus","setting darkminus"); minusOne.setImageResource(R.drawable.ic_remove_circle_black_18dp); minusOne.setOnClickListener(reverseMinusOneOnClickListener); //plusOne.setOnClickListener(plusOneOnClickListener); plusOne.setVisibility(View.GONE); break; case "none": Log.i("darkregular","setting regular"); minusOne.setImageResource(R.drawable.ic_remove_black_18dp); plusOne.setImageResource(R.drawable.ic_add_black_18dp); plusOne.setOnClickListener(plusOneOnClickListener); minusOne.setOnClickListener(minusOneOnClickListener); break; } } View.OnClickListener reversePlusOneOnClickListener = new View.OnClickListener() { @Override public void onClick(View v) { if (!FourUtils.isConnected(v.getContext())) { return; } minusOne.setEnabled(false); plusOne.setEnabled(false); model.increment("plusOne", -1); model.increment("score", -1); model.saveEventually(new SaveCallback() { @Override public void done(ParseException e) { if (e == null) { ParseQuery<ParseObject> query = ParseQuery.getQuery("CommentChoice"); query.whereEqualTo("user", ParseUser.getCurrentUser()); query.whereEqualTo("comment", model); query.fromPin(Four.COMMENT_CHOICE); query.getFirstInBackground(new GetCallback<ParseObject>() { @Override public void done(ParseObject parseObject, ParseException e) { if (e == null) { if (parseObject == null) { parseObject = ParseObject.create("CommentChoice"); parseObject.put("comment", model); parseObject.put("user", ParseUser.getCurrentUser()); } parseObject.put("userChoice", "none"); parseObject.pinInBackground(Four.COMMENT_CHOICE, new SaveCallback() { @Override public void done(ParseException e) { if (e == null) { score.setText(Integer.toString(model.getInt("score"))); //votes.setText((model.getInt("minusOne") + model.getInt("plusOne")) + " votes"); minusOne.setVisibility(View.VISIBLE); plusOne.setImageResource(R.drawable.ic_add_black_18dp); plusOne.setOnClickListener(plusOneOnClickListener); minusOne.setEnabled(true); plusOne.setEnabled(true); // minusOne.setOnClickListener(minusOneOnClickListener); BusProvider.getInstance().post(new NewCommentChoicesAdded()); } else { e.printStackTrace(); } } }); } else{e.printStackTrace();} } }); } else { e.printStackTrace(); Log.i("plus1 error", e.getMessage()); } } }); } }; 

Solutions Collecting From Web of "Обновление элемента RecyclerView + асинхронный сетевой вызов"

Когда асинхронный код завершен, вы должны обновить данные, а не представления. После обновления данных сообщите адаптеру, что данные изменены. RecyclerView принимает это к сведению и повторно отображает ваше мнение.
При работе с видами просмотра (ListView или RecyclerView) вы не можете знать, какой элемент представляет представление. В вашем случае это представление перерабатывается до завершения работы async и назначается другому элементу ваших данных.
Поэтому никогда не изменяйте представление. Всегда изменяйте данные и уведомляйте адаптер. BindView должен быть местом, где вы рассматриваете эти случаи.

Чет Гаазе из Google обсуждает вашу точную проблему в этом видео DevBytes .

Короче говоря, структура должна быть уведомлена о том, что одно из Представлений находится в «переходном» состоянии. После уведомления структура не будет перерабатывать этот вид до тех пор, пока его флаг «переходный» не будет очищен.

В вашем случае перед выполнением асинхронного действия вызовите setHasTransientState(true) в дочернем представлении, который должен измениться при завершении действия async. Этот вид не будет переработан, пока вы явно не назовете setHasTransientState(false) на нем.

Не по теме:

Похоже, вы можете манипулировать элементами пользовательского интерфейса из фоновых потоков. Не делай этого! Если у вас есть ссылка на Activity то вместо этого используйте его API-интерфейс runOnUiThread(Runnable action) . Если получить ссылку на « Activity сложно, вы можете получить Handler потока пользовательского интерфейса и использовать его API-интерфейс post(Runnable action) .

Без кода, чтобы посмотреть, это будет трудным (если не невозможным) для людей, чтобы дать точный ответ. Однако на основе этого описания звучит так, как если бы ваша асинхронная загрузка сети (с использованием AsyncTask или пользовательского Loader ?) Не была специально привязана к элементу, отслеживаемому вашим адаптером. Вам нужно будет связать их друг с другом, так как дочерние объекты View показанные RecyclerView , повторно используются, чтобы быть более эффективными. Это также означает, что если View повторно используется и активная операция async привязана к нему, эта операция aync должна быть отменена. В противном случае вы увидите, что вы видите сейчас: неправильный дочерний View , обновляемый содержимым от более старого асинхронного вызова.