펩시티433
158
2019-11-25 20:19:42
5
847

Android RecyclerView 질문 있습니다.


현재 리싸이클러뷰에서 각 홀더마다 버튼을 하나씩 주고 클릭했을 때 다이얼로그를 띄워서 데이터를 입력하면 그 데이터가  홀더에 있는 텍스트뷰에 띄워지게끔 만들어 놓았는데 notifyItemChanged(getAdapterPosition)으로 해당 데이터가 변경 되었다는 걸 알려주고 있는데 계속해서 첫번째 뷰만 변경이 되네요 두번째 세번째 데이터를 수정해도..

위의 설명만으로는 잘 이해 안되실 수도 있는데 어쨋든 notifyItemChanged() 이 메소드를 사용하면 해당 position에 있는 holder에 데이터를 다시 바인딩 해주는게 아닌가요? 구글링해보아도 잘 모르겠네요.. 원리가 조금 궁금합니다.

0
  • 답변 5

  • OkkyActivity
    443
    2019-11-25 21:05:13

    코드를 보지 않아서 정확한 원인은 모르겠지만..

    onBind 쪽에 로그를 한번 찍어보시는게 좋을것같네요


    RecyclerView 를 사용하면서 주로 실수하시는 부분들이

    1. position 을 제대로 이용하고 있는가

    2. recycle 된 뷰를 제대로 다시 세팅하고 있는가 

    정도가 많이 하시는 실수라서여


    확인하셔야 할 작업은


    1. notify() 호출 후 onBind() 되는 부분에 제대로 들어오는지 (로그를 찍거나, 디버거로 확인)

    2. onBind() 로 제대로 들어온다면, 의도한 position 에 맞는 데이터를 정확히 가져오는지 

    3. 의도한 데이터를 recycle 된 뷰에 제대로 다시 세팅하는지 



    요 세개 확인해보시면 될것같네여

  • 펩시티433
    158
    2019-11-26 00:25:23 작성 2019-11-26 00:34:19 수정됨

    답변 감사드립니다. 전부 확인해 보았는데 position 값은 문제가 없더라구요.

    onBindViewHolder에서도 holder로 넘겨주는 객체도 이상이 없구요. 헌데 왜 다른 곳 데이터를 수정을 해도 처음 수정한 뷰에서 수정한 값이 적용 되는지 이해할 수가 없네요... 

    예를 들어 두번째 값 수정 -> 두번째 뷰에 수정한 내용 적용 완료 -> 세번째 값 수정 -> 수정한 값 두번째 뷰에 적용됨

    저 마지막 과정이 문제인데 holder가 계속 잘못 넘어오는건지 순서를 바꿔서 해보아도

    세번째 값 수정 -> 세번째 뷰에 수정한 내용 적용 완료 -> 두번째 값 수정 -> 수정한 값 세번째 뷰에 적용됨

    이런식으로 액티비티 진입 후에 맨처음 수정한 뷰에만 계속 적용이 되네요.. 혹시 짐작 가시는게 있을까요.

    아래에 adpater 코드 전부 올려봅니다. 아직 개발 초보라 정리가 잘 되어 있어도 양해 부탁드립니다ㅜㅜ


    import android.app.AlertDialog;
    import android.app.Dialog;
    import android.content.Context;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.Window;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;
    
    import com.example.footballlive.data.MatchResult;
    import com.google.android.gms.tasks.OnSuccessListener;
    import com.google.firebase.database.DatabaseReference;
    import com.google.firebase.database.FirebaseDatabase;
    
    import java.util.ArrayList;
    import java.util.LinkedHashMap;
    
    public class MatchResultAdapter extends RecyclerView.Adapter<MatchResultAdapter.Viewholder> {
    
        final private String TAG = "MatchResultAdapter";
    
        Context context;
        LinkedHashMap<String, MatchResult> mData;
        ArrayList<MatchResult> results = new ArrayList<>();
        TextView my_team_name_tv, opp_team_name_tv, my_team_score_tv, opp_team_score_tv, input_result_tv, match_result_hide_tv;
        LinearLayout score_ll;
    
        public MatchResultAdapter(Context context, LinkedHashMap<String, MatchResult> mData) {
            this.context = context;
            this.mData = mData;
    
            for(MatchResult r : mData.values()){
                results.add(r);
            }
        }
    
        @NonNull
        @Override
        public Viewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
            View itemView = layoutInflater.inflate(R.layout.match_result_item, parent, false);
    
            return new Viewholder(itemView);
        }
    
        @Override
        public void onBindViewHolder(@NonNull Viewholder holder, int position) {
            MatchResult m = results.get(position);
            holder.setItem(m);
    
            Log.e(TAG, "onBindViewHolder - myTeamScore : " + m.getMyTeam_score() + " / oppTeamScore : " + m.getOppTeam_score());
        }
    
    
    
        @Override
        public int getItemCount() {
            return results.size();
        }
    
        public class Viewholder extends RecyclerView.ViewHolder{
    
            public Viewholder(@NonNull View itemView) {
                super(itemView);
    
                my_team_name_tv = itemView.findViewById(R.id.my_team_name_tv);
                opp_team_name_tv = itemView.findViewById(R.id.opp_team_name_tv);
                my_team_score_tv = itemView.findViewById(R.id.my_team_score_tv);
                opp_team_score_tv = itemView.findViewById(R.id.opp_team_score_tv);
                input_result_tv = itemView.findViewById(R.id.input_result_tv);
                score_ll = itemView.findViewById(R.id.score_ll);
                match_result_hide_tv = itemView.findViewById(R.id.match_result_hide_tv);
    
                input_result_tv.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        /* Dialog 설정 */
                        int position = getAdapterPosition();
                        Log.e(TAG, "position : " + position);
                        AlertDialog.Builder builder = new AlertDialog.Builder(context);
                        View view = LayoutInflater.from(context).inflate(R.layout.match_result_dialog,null, false);
                        builder.setView(view);
                        
                        final EditText myTeam_score_et = view.findViewById(R.id.myTeam_score_et);
                        final EditText oppTeam_score_et = view.findViewById(R.id.oppTeam_score_et);
                        Button match_result_input_btn = view.findViewById(R.id.matchresult_input_btn);
                        Button match_result_cancel_btn = view.findViewById(R.id.matchresult_cancel_btn);
                        TextView myTeam_name_tv = view.findViewById(R.id.myTeam_name_tv);
                        TextView oppTeam_name_tv = view.findViewById(R.id.oppTeam_name_tv);
                        final MatchResult matchResult = results.get(position);
                        myTeam_name_tv.setText(matchResult.getMyTeam());
                        oppTeam_name_tv.setText(matchResult.getOppTeam());
    
                        final AlertDialog dlg = builder.create();
    
                        /* Dialog 확인 버튼 클릭리스너 구현 */
                        match_result_input_btn.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                String my_team_score = myTeam_score_et.getText().toString();
                                String opp_team_score = oppTeam_score_et.getText().toString();
    
                                matchResult.setMyTeam_score(my_team_score);
                                matchResult.setOppTeam_score(opp_team_score);
    
    
                                results.set(getAdapterPosition(), matchResult);
                                for(MatchResult m : results){
                                    Log.e(TAG, "my_team_score" + m.getMyTeam_score() + "opp_team_score" + m.getOppTeam_score());
    
                                }
    
                                notifyItemChanged(getAdapterPosition());
    
    
                                dlg.dismiss();
                            }
                        });
    
                        dlg.show();
                    }
    
                });
    
            }
    
    
            public void setItem(MatchResult m) {
                /* holder에 있는 뷰에 데이터 보여주기 */
                my_team_name_tv.setText(m.getMyTeam());
                opp_team_name_tv.setText(m.getOppTeam());
                Log.e(TAG, "setItem - myTeamScore : " + m.getMyTeam_score() + " / oppTeamScore : " + m.getOppTeam_score());
                if(m.getMyTeam_score() != null && m.getOppTeam_score() != null){
                    my_team_score_tv.setText(m.getMyTeam_score());
                    opp_team_score_tv.setText(m.getOppTeam_score());
                    input_result_tv.setVisibility(View.INVISIBLE);
                    score_ll.setVisibility(View.VISIBLE);
                    match_result_hide_tv.setVisibility(View.INVISIBLE);
                }else{
                    input_result_tv.setVisibility(View.VISIBLE);
                    score_ll.setVisibility(View.INVISIBLE);
                    match_result_hide_tv.setVisibility(View.VISIBLE);
                }
            }
        }
    }


  • 밤아홉시
    433
    2019-11-26 02:24:35 작성 2019-11-26 02:24:47 수정됨

    아래 변수 선언을 Viewholder 클래스에서 하세요.

    TextView my_team_name_tv, opp_team_name_tv, my_team_score_tv, opp_team_score_tv, input_result_tv, match_result_hide_tv;

    LinearLayout score_ll;

  • 펩시티433
    158
    2019-11-26 18:40:17

    밤아홉시 

    아주 잘 작동합니다! 답변 감사드립니다. 헌데 어떻게 해서 저게 해결 방법이 될 수 있는 지 혹시 알려주실 수 있을까요?

    단지 Viewholder 클래스 안에서 선언해준 차이 인데 어떻게 해결이 되는건지 궁금합니다.


  • 밤아홉시
    433
    2019-11-27 09:14:33

    전자는 MatchResultAdapter 클래스의 멤버 변수이고, 후자는 Viewholder 클래스의 멤버 변수입니다.

    즉, 예를 들어 Viewholder 객체가 10개라면 후자의 경우 각자의 멤버 변수에 값을 저장하는데

    전자는 MatchResultAdapter 객체 1개의 멤버 변수에

    10개의 Viewholder 객체가 값을 쓰고, 덮고, 읽고 하기 때문에 원하는 결과가 보이지 않는 것입니다.

    실제 결과물을 만들며 공부하는 것도 좋지만, 시간을 내어 기본서를 조금씩 보시는 걸 추천드립니다.

  • 로그인을 하시면 답변을 등록할 수 있습니다.