기록

AndroidStudio/java/ 멀티뷰 타입 Recyclerview 본문

Moblie/Android

AndroidStudio/java/ 멀티뷰 타입 Recyclerview

youngyin 2022. 5. 29. 19:01

들어가면서 

리사이클러뷰는 가장 많이 쓰는 뷰 중 하나로, 여러 개의 리사이클러뷰를 겹쳐서 쓰거나 반복해서 쓰는 경우가 있었다. 더 나은 구조를 고민하던 중에 리사이클러뷰에 여러개의 view를 연결할 수 있음을 알게 되었다.

개요

  1. 데이터 클래스 만들기
  2. 아이템뷰 만들기
  3. 리사이클러뷰 어댑터 만들기
  4. viewholder에서 viewType별 binding 하기
  5. Activity/Fragment에 리사이클러뷰 추가하기

Code

(1) 데이터 클래스 만들기

package com.example.myapp;

public class MessageData {
    private String timestamp;
    private String message;
    private String name;

    public MessageData(String timestamp, String message, String name) {
        this.timestamp = timestamp;
        this.message = message;
        this.name = name;
    }

    public String getTimestamp() {
        return timestamp;
    }
    public String getMessage() {
        return message;
    }
    public String getName() {
        return name;
    }
}

(2) 아이템뷰 만들기

item view

더보기
// recycler_sent_message.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="8dp">

    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffff"
        app:cardCornerRadius="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" >
        <TextView
            android:id="@+id/tv_sent_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:text="보낸 메세지"
            android:padding="8dp" />
    </androidx.cardview.widget.CardView>

    <TextView
        android:id="@+id/tv_timestamp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:text="hh:ss"
        app:layout_constraintBottom_toBottomOf="@id/cardView"
        app:layout_constraintEnd_toStartOf="@id/cardView"
        app:layout_constraintTop_toTopOf="@id/cardView" />

</androidx.constraintlayout.widget.ConstraintLayout>
// recycler_received_message.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="user_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:background="#ffff"
        app:cardCornerRadius="20dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_name">

        <TextView
            android:id="@+id/tv_received_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:padding="8dp"
            android:text="받은 메세지" />
    </androidx.cardview.widget.CardView>

    <TextView
        android:id="@+id/tv_timestamp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="hh:ss"
        app:layout_constraintBottom_toBottomOf="@id/cardView"
        app:layout_constraintStart_toEndOf="@id/cardView"
        app:layout_constraintTop_toTopOf="@id/cardView" />
</androidx.constraintlayout.widget.ConstraintLayout>

(3) 리사이클러뷰 어댑터 만들기

package com.example.myapp;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
    @NonNull
    @Override
    public MessageAdapter.ViewHolder onCreateViewHolder(
            @NonNull ViewGroup parent,
            int viewType
    ) {
        View view = LayoutInflater
                .from(parent.getContext())
                .inflate(getViewSrc(viewType), parent, false);
        return new ViewHolder(view, viewType);
    }

    @Override
    public void onBindViewHolder(
            @NonNull MessageAdapter.ViewHolder holder,
            int position
    ) {
        holder.bind(dataSet.get(position));
    }

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

    // view holder
    public class ViewHolder extends RecyclerView.ViewHolder {
        private int viewType;
        public ViewHolder(@NonNull View itemView, int viewType){
            super(itemView);
            this.viewType = viewType;
        }

        public void bind(MessageData item){
            // todo : show data
        }
    }

    // data
    private ArrayList<MessageData> dataSet = new ArrayList();
    public void submitData(ArrayList<MessageData> newData){
        dataSet = newData;
        notifyDataSetChanged();
    }

    // view type
    private int getViewSrc(int viewType){
        // todo : connect xml
        return 0
    }
}

(4) viewholder에서 viewType별 binding 하기

  • [getItemViewType] 각 data에 따라 viewType를 연산한다.
    • 아래에서는 "a"가 보낸 메세지는 sent massage로
    • "a"를 제외한 사람이 보낸 메세지는 received massage로 분류하고 있다.
  • [getViewSrc] ViewType에 따라 xml파일을 연결한다.
  • [bindSentMessage/bindReceivedMessage] view에 데이터를 연결하여 보여준다.
// MessageAdapter...
// view holder
public class ViewHolder extends RecyclerView.ViewHolder {
    private int viewType;
    public ViewHolder(@NonNull View itemView, int viewType){
        super(itemView);
        this.viewType = viewType;
    }

    public void bind(MessageData item){
        if (viewType==TYPE_SENT_MESSAGE){
            bindSentMessage(item);
        } else if(viewType==TYPE_RECEIVED_MESSAGE) {
            bindReceivedMessage(item);
        }
    }

    private void bindSentMessage(MessageData item){
        TextView tv_sent_message = itemView.findViewById(R.id.tv_sent_message);
        TextView tv_timestamp = itemView.findViewById(R.id.tv_timestamp);
        tv_sent_message.setText(item.getMessage());
        tv_timestamp.setText(item.getTimestamp());
    }

    private void bindReceivedMessage(MessageData item){
        TextView tv_received_message = itemView.findViewById(R.id.tv_received_message);
        TextView tv_timestamp = itemView.findViewById(R.id.tv_timestamp);
        TextView tv_name = itemView.findViewById(R.id.tv_name);
        tv_received_message.setText(item.getMessage());
        tv_timestamp.setText(item.getTimestamp());
        tv_name.setText(item.getName());
    }
}

// view type
private int TYPE_SENT_MESSAGE = 101;
private int TYPE_RECEIVED_MESSAGE = 102;
private int getViewSrc(int viewType){
    if (viewType==TYPE_RECEIVED_MESSAGE){
        return R.layout.recycler_received_message;
    } else {
        return R.layout.recycler_sent_message;
    }
}

@Override
public int getItemViewType(int position) {
    if (dataSet.get(position).getName().equals("user1")){
        return TYPE_SENT_MESSAGE;
    } else {
        return TYPE_RECEIVED_MESSAGE;
    }
}

(5) Activity/Fragment에 리사이클러뷰 추가하기

// activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".MainActivity"
    android:background="#9bbbd4">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:orientation="vertical"/>

</LinearLayout>
package com.example.myapp;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RecyclerView recyclerView = findViewById(R.id.recycler);
        MessageAdapter messageAdapter = new MessageAdapter();
        messageAdapter.submitData(getData());
        recyclerView.setAdapter(messageAdapter);
    }

    private ArrayList<MessageData> getData(){
        ArrayList<MessageData> data = new ArrayList();
        data.add(new MessageData("05:22", "hello", "user1"));
        data.add(new MessageData("05:23", "can I have your phone number?", "user1"));
        data.add(new MessageData("06:23", "000000", "user2"));
        data.add(new MessageData("07:01", "111111", "user3"));
        data.add(new MessageData("11:20", "I'll let you know later.", "user4"));
        return data;
    }
}

결과

참고자료

2022.03.29 - [안드로이드/예제] - androidStudio/kotliln/recyclerview 사용하기

Comments