2015-07-31 13 views
15

Tôi có một recyclerview hoạt động như mong đợi. Tôi có một nút trong bố cục lấp đầy danh sách. Nút được cho là thực hiện cuộc gọi không đồng bộ và kết quả là tôi thay đổi giao diện của nút. Tất cả điều này xảy ra tốt đẹp.Cập nhật phần tử RecyclerView + cuộc gọi mạng không đồng bộ

Nhưng khi tôi nhấp vào nút và cuộn xuống nhanh danh sách, kết quả của cuộc gọi không đồng bộ sẽ cập nhật nút của chế độ xem mới (chế độ xem thay cho nút cũ). Làm cách nào để xử lý việc này? Tôi có thể xử lý khi một chế độ xem cụ thể được sử dụng lại không?

Cập nhật:

Phần mã của lớp bộ điều hợp thực hiện cuộc gọi async và cập nhật 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()); 
        } 

       } 
      }); 
     } 
    }; 
+3

Bạn nên cung cấp bố cục các mục danh sách (mã xml) và mã của Bộ điều hợp. –

+0

... đặc biệt là mã như thế nào công việc async của bạn thay đổi botton và làm thế nào MyAdapter.onBindViewHolder (ViewHolder chủ, int vị trí) trông giống như – k3b

+0

có buddy mà không xml và mã adapter, chúng tôi không thể hỗ trợ bạn .... mã đầu tiên – TheGreat004

Trả lời

17

Khi mã async được hoàn tất, bạn nên cập nhật dữ liệu chứ không phải là chế độ xem. Sau khi cập nhật dữ liệu, hãy cho bộ điều hợp biết rằng dữ liệu đã thay đổi. RecyclerView lưu ý về điều này và hiển thị lại chế độ xem của bạn.
Khi làm việc với chế độ xem tái chế (ListView hoặc RecyclerView), bạn không thể biết mục nào đại diện cho chế độ xem. Trong trường hợp của bạn, chế độ xem đó được tái chế trước khi công việc async được thực hiện và được gán cho một mục khác của dữ liệu của bạn.
Vì vậy, không bao giờ sửa đổi chế độ xem. Luôn sửa đổi dữ liệu và thông báo cho bộ điều hợp. bindView phải là nơi bạn xử lý các trường hợp này.

0

Không có mã để xem, điều này sẽ khó (nếu không thể) để mọi người đưa ra câu trả lời chính xác. Tuy nhiên, dựa trên mô tả này có vẻ như tải mạng không đồng bộ của bạn (sử dụng một số AsyncTask hoặc tùy chỉnh Loader?) Không được gắn cụ thể với yếu tố đang được bộ điều hợp của bạn theo dõi. Bạn sẽ cần phải có một số cách để buộc cả hai lại với nhau vì các đối tượng con View được hiển thị bởi RecyclerView được sử dụng lại để có hiệu quả hơn. Điều này cũng có nghĩa là nếu View đang được sử dụng lại và có một hoạt động không đồng bộ hoạt động liên kết với nó, hoạt động async sẽ cần phải bị hủy. Nếu không, bạn sẽ thấy những gì bạn thấy bây giờ: con sai View đang được cập nhật với nội dung từ cuộc gọi không đồng bộ cũ hơn.

3

Chet Haase từ Google thảo luận về vấn đề chính xác của bạn trong this DevBytes video.

Tóm lại, khuôn khổ cần được thông báo rằng một trong các Chế độ xem ở trạng thái "tạm thời". Sau khi được thông báo, khung sẽ không tái chế Chế độ xem này cho đến khi cờ "tạm thời" của nó bị xóa.

Trong trường hợp của bạn, trước khi bạn thực hiện hành động không đồng bộ, hãy gọi setHasTransientState(true) trên chế độ xem con sẽ thay đổi khi hành động không đồng bộ hoàn tất. Chế độ xem này sẽ không được tái chế cho đến khi bạn gọi một cách rõ ràng setHasTransientState(false) trên đó.

Offtopic:

Dường như bạn có thể thao tác với các yếu tố giao diện người dùng từ chủ đề nền. Đừng làm thế! Nếu bạn có thể tham chiếu đến Activity thì hãy sử dụng API runOnUiThread(Runnable action) của mình thay thế. Nếu việc tham chiếu đến Activity rất khó, bạn có thể nhận được số Handler của giao diện người dùng và sử dụng API post(Runnable action) của nó.

Các vấn đề liên quan