RecyclerView元素更新+asynchronousnetworking调用

我有一个像预期的那样工作的回收站。 我在填充列表的布局中有一个button。 该button应该做一个asynchronous调用,结果,我改变了button的外观。 所有这一切都很好。

但是,当我点击button并快速向下滚动列表时,asynchronous调用的结果会更新新视图的button(替代旧视图的视图)。 我该如何处理? 当特定视图被重用时,我可以处理吗?

更新:

执行asynchronous调用和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元素更新+asynchronousnetworking调用"

当asynchronous代码完成后,您应该更新数据,而不是视图。 更新数据后,告诉适配器数据已更改。 RecyclerView获取这个logging并重新呈现您的视图。
在处理回收视图(ListView或RecyclerView)时,您无法知道视图代表什么项目。 在你的情况下,该视图在asynchronous工作完成之前得到回收,并被分配到不同的数据项。
所以不要修改视图。 始终修改数据并通知适配器。 bindView应该是你处理这些案件的地方。

Google的Chet Haase在这个DevBytesvideo中讨论了您确切的问题。

简而言之,需要通知框架其中一个视图处于“瞬态”状态。 一旦通知,框架将不会回收此视图,直到它的“瞬态”标志被清除。

在你的情况下,在你执行asynchronous动作之前,在子视图上调用setHasTransientState(true) ,当asynchronous动作完成时,它应该改变。 直到你明确地调用setHasTransientState(false) ,这个视图才会被回收。

无关:

看起来你可能正在从后台线程操纵UI元素。 不要这样做! 如果你可以有一个对Activity的引用,那就使用它的runOnUiThread(Runnable action) API。 如果获取对Activity的引用是困难的,则可以获取UI线程的Handler并使用post(Runnable action) API。

没有代码看,这将是困难的(如果不是不可能的),让人们提供一个确切的答案。 然而,根据这个描述,听起来好像你的asynchronousnetworking加载(使用AsyncTask或自定义Loader ?)并不是特别绑定到你的适配器正在跟踪的元素。 由于RecyclerView所显示的子View对象被重用,效率更高,因此需要RecyclerView两者绑定在一起。 这也意味着,如果一个View被重用,并且有一个活动的asynchronous操作绑定在它上面,asynchronous操作将需要被取消。 否则,您将看到您现在看到的内容:错误的子View正在使用旧的asynchronous调用的内容进行更新。