Intereting Posts
В Google Play я могу перенести приложение из одной учетной записи разработчика в другую? Android: есть ли способ получить размер объекта RemoteViews? Android: отключить вращение на 90 °, но разрешить поворот на 180 ° Студия Android / Gradle build не работает в выпуске: не удалось определить зависимости задачи: app: crashlyticsStoreDeobsRelease ' Android: разница между невидимыми и ушла? Android: Где скрывается атрибут цвета текста виджета Spinner? Android-эмулятор не запускается вообще Должен ли я комментировать мои запросы на журнал при создании моего последнего пакета? Разница между close () и отключением () в Android Bluetooth API? Как создать EditText с закругленными углами? Сбой TabLayout после обновления библиотеки поддержки до 23.2.1 Как исходный код Android не имеет основного метода и все еще работает? AlertDialog с положительной кнопкой и проверка пользовательского EditText AlertDialog: как удалить черные границы над и под ним Предупреждение. Экспортированная деятельность не требует разрешения.

Обновление элемента 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 , обновляемый содержимым от более старого асинхронного вызова.