땀이 삐질삐질 나는 개발 일기

Recyclerview의 아이템 클릭을 CallbackListener를 통해 Activity까지 가져오기 본문

개발 Tip

Recyclerview의 아이템 클릭을 CallbackListener를 통해 Activity까지 가져오기

삐질 2019. 7. 8. 19:50
안녕하세요. 삐질삐질 개발하는 개발자 삐질입니다.

오늘은 리사이클러뷰의 아이템을 클릭했을 때 간혹 Activity까지 클릭 된 정보를 전달하고싶은 경우가 있습니다.
아마도 리사이클러 뷰가 가지는 뷰홀더 이외의 정보를 같은 액티비티에 있는 다른 뷰에게 전달하고자 함일때 쓰일 것 같습니다.


    


긴말 않고 코드로 살펴보도록하겠습니다.
  • 이번 시간의 키포인트는 Callback Listener( Interface )입니다. -> 인터페이스의 개념에 대해서는 따로 학습하시길 바랍니다.
먼저 필요한 Class 및 Interface입니다.

다음으로 
  1. MainActivity.java
public class MainActivity extends AppCompatActivity {

    private RecyAdapter adapter;
    private ActivityMainBinding binding;
    private ClickCallbackListener callbackListener = new ClickCallbackListener() {
        @Override
        public void callBack(int pos) {
            Toast.makeText(MainActivity.this, pos + " 번째 아이템을 클릭했슴둥", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding=DataBindingUtil.setContentView(this,R.layout.activity_main);
        init();

    }

    public void init() {
        adapter = new RecyAdapter(); // 어댑터 생성

        List<String> items = new ArrayList<>();
        for (int i = 0; i < 30; i++) { //가상으로 아이템 만들어주는 포문
            items.add("테스트 영역 Index :" + i);
        }
        adapter.setItems(items);
        adapter.setCallbackListener(callbackListener);

        /*리사이클러뷰에 레이아웃 매니저 할당 및 어탭터 셋 */
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
        binding.recyclerview.setLayoutManager(linearLayoutManager);
        binding.recyclerview.setAdapter(adapter);
    }
}

  1. RecyAdapter.java
public class RecyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<String> items;
    private ClickCallbackListener callbackListener;

    public RecyAdapter() {
        items = new ArrayList<>();
    }

    public void setItems(List<String> items) {
        this.items.addAll(items);
        notifyDataSetChanged();
    }

    //메인액티비티에서 전달 받은 콜백메서드를 set 하는 메서드
    public void setCallbackListener(ClickCallbackListener callbackListener) {
        this.callbackListener = callbackListener;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemTextBinding binding = ItemTextBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        return new ItemViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ItemViewHolder) {
            String item = items.get(position);
            /*해당 아이템과 callbackListener를 바인드 된 뷰홀더에 전달한다.*/
            ((ItemViewHolder) holder).bind(item, callbackListener);
        }
    }

    @Override
    public int getItemCount() {
        return items.size();
    }


    public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private ItemTextBinding binding;
        private ClickCallbackListener callbackListener;

        public ItemViewHolder(ItemTextBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
            setListener(); //부모 뷰그룹에 클릭리스너를 등록
        }

        public void setListener() {
            binding.rootView.setOnClickListener(this);
        }

        public void bind(String item, ClickCallbackListener callbackListener) {
            binding.textTitle.setText(item);// 전달받은 String setText
            this.callbackListener = callbackListener; //전달받은 콜백을 해당 뷰홀더의 멤버로 가지고있음.

        }

        @Override
        public void onClick(View v) {
            int id = v.getId();
            /*클릭 후 메인액티비티에서 구현되어 전달 된 콜백 메서드를 호출하여 인자로 클릭 포지션을 담았다.*/
            callbackListener.callBack(getAdapterPosition());
        }
    }
}

  1. ClickCallbackListener.interface
public interface ClickCallbackListener {
    void callBack(int pos);
}

  1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

  1. Item_text.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/rootView"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#E76363"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textTitle"
            android:text="테스트 영역입니다."
            android:textSize="28sp"
            android:gravity="center"
            android:textColor="#ffffff"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</layout>

어때요 생각보다 별게 없죠?? 

중요한것은 앞서 말씀드린 것 처럼  Callback Listener( Interface )입니다

콜백 리스너란 무엇인가?  => 내가 전달한 무언가(이때는 리스너객체)가 다시 호출되어 돌아오는 양상을 띄어 콜백 !! 이라고합니다. 

즉 쉽게 말해, 나는 CallbackListener Interface안에  callBack(int pos)라는 메서드를 내용 없이 선언해 주었고, MainActivity에서는 해당 CallbackListener의 객체를 선언함과 동시에 callBack(int pos) 내용을 구현해주었습니다.

이때 우리는 흔히  MainActivity에 구현해준 ClickCabllbackListener 객체를  구현체 라는 단어로 부르게 됩니다.
MainActivity에서 리스너 구현체를 구현한 뒤 , 이것을  Recyclerview Adapter에게 set 해줄 수있도록 합니다. 
이렇게 되면 실제로 MainActivity에 있는 콜백 객체와 리사이클러뷰에  set되어진 콜백 객체는 동일한 값을 가지게 됩니다.

위 리사이클러 어탭터의 bind부분 처럼 부모 리사이클러뷰 어댑터 -> 각 아이템의 VIewHolder로 전달할 수 있도록 합니다.
그렇다면 아이템이 형성되는 각각의 ViewHolder 가 Callback객체를 가지게됩니다.

결론적으로 MainActivity에서 시작 된 콜백 객체가 리사이클러뷰 각각의 VIewHolder 할당됩니다. 

그림으로 그릴 수있는 형태가 있는데, 어떤 형태가 떠오르시나요? 
바로 트리 형태 입니다.  



( 발 그림 죄송합니다..ㅠㅠ)

자 Callback은 이렇게 할당이 되었고 ,  ViewHolder의 Callback 객체에서 callBack(int pos)함수를 호출하는 순간 
역으로 다시 ViewHolder -> Recyclerview -> MainActivity의 Callback(int pos){} 구현체로 레퍼런스가 이동하게 됩니다.

이렇게 전달받은 콜백 값을 통해, MainActivity까지 클릭 이벤트를 끌고왔습니다. 이후 원하는 값을 전달하고 필요한 다른 View들의 속성을 연달아 수정할 수 있게 됩니다.


안드로이드 초보 개발자를 위해 아래와 같은 카카오 오픈톡을 운영 중입니다


Comments