안녕하세요 오늘은 리사이클러뷰notifyItemChanged(position) 메서드와 notifyItemChanged(position,payload) 메서드를 사용하여 좋아요 기능 예제를 만들어 보고
notifyItemChanged(position) 과 notifyItemChanged(position,payload) 의 차이에 대해서 한번 설명해 보겠습니다.
우선 notifyItemChanged(position) 로 아이템을 변경하는 로직을 만들어 보겠습니다.
레이아웃을 만들기 전에 좋아요 기능에 필요한 이미지부터 세팅하겠습니다.
우선 res > drawble 우클릭 > Vector Asset에 들어가 주세요
그다음 상단 검색창에 favorite이라고 검색하시면 꽉 찬 하트와 가운데가 빈 하트가 나옵니다.
꽉 찬 하트의 이름을 liked로 변경해 주시고 색상도 빨간색으로 변경하시고 해당 Drawable 이미지 리소스를 생성해 주세요
같은 방법으로 빈 하트도 만들어 주세요 이름은 empty 색상도 회색으로 변경했습니다.
이제 사용할 drawable 리소스를 만들었으니 다음으로 레이아웃을 만들어 보겠습니다.
activity_main.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"
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/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
그리고 해당 리사이클러뷰에 담을 아이템 뷰 레이아웃입니다.
item_view.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/Like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Content"
app:srcCompat="@drawable/empty" />
<TextView
android:id="@+id/Content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/Name"
app:layout_constraintTop_toBottomOf="@+id/Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
리사이클러뷰 데이터 리스트 DTO 클래스입니다
Board.java
package com.example.stickcodeex;
public class Board {
String name;
String text;
boolean like;
public Board(String name, String text, boolean like) {
this.name = name;
this.text = text;
this.like = like;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public boolean isLike() {
return like;
}
public void setLike(boolean like) {
this.like = like;
}
}
MainActivity.java
package com.example.stickcodeex;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
RecyclerView recycler;
Adepter adapter;
ArrayList<Board> dataList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
// 액티비티가 생성될 때 초기 데이터를 생성하고 dataList에 추가합니다.
for (int i = 0; i < 10; i++) {
int j = i + 1;
dataList.add(new Board(j + "번째 유저", j + "번째 안녕하세요", false));
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recycler = findViewById(R.id.recycler); // RecyclerView 초기화
}
@Override
protected void onResume() {
super.onResume();
createRecyclerView(); // RecyclerView 생성 함수 호출
}
private void createRecyclerView() {
// Adapter를 생성하고 RecyclerView에 설정하여 데이터를 표시합니다.
adapter = new Adepter(dataList); // Adapter 생성
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recycler.setLayoutManager(layoutManager); // RecyclerView에 레이아웃 매니저 설정 (선택적)
recycler.setAdapter(adapter); // RecyclerView에 Adapter 설정
}
}
리사이클러뷰 Adepter 구현 부분입니다.
Adepter.java
package com.example.stickcodeex;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Adepter extends RecyclerView.Adapter<Adepter.ViewHolder> {
private ArrayList<Board> dataList;
public Adepter( ArrayList<Board> dataList) {
this.dataList = dataList;
}
// ViewHolder 클래스: 각 아이템의 뷰 요소를 보유
class ViewHolder extends RecyclerView.ViewHolder {
TextView Name, Content;
ImageView Like;
// ViewHolder 생성자
public ViewHolder(@NonNull View itemView) {
super(itemView);
// 뷰 요소 초기화
Name = itemView.findViewById(R.id.Name); // 이름 텍스트뷰
Content = itemView.findViewById(R.id.Content); // 내용 텍스트뷰
Like = itemView.findViewById(R.id.Like); // 좋아요 이미지뷰
// 좋아요 이미지뷰 클릭 리스너 설정
Like.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 아이템의 좋아요 상태 변경
if (dataList.get(getAdapterPosition()).isLike()) {
dataList.get(getAdapterPosition()).setLike(false);
} else {
dataList.get(getAdapterPosition()).setLike(true);
}
// 아이템 변경 사항을 알리고 화면 갱신
notifyItemChanged(getAdapterPosition());
}
});
}
}
// onCreateViewHolder: 뷰홀더 생성
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.item_view, parent, false);
return new ViewHolder(view);
}
// onBindViewHolder: 뷰홀더에 데이터 바인딩
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// 아이템 데이터 설정
holder.Name.setText(dataList.get(position).getName()); // 이름 설정
holder.Content.setText(dataList.get(position).getText()); // 내용 설정
// 아이템의 좋아요 상태에 따라 좋아요 이미지 설정
if (dataList.get(position).isLike()) {
holder.Like.setImageResource(R.drawable.liked); // 좋아요 이미지
} else {
holder.Like.setImageResource(R.drawable.empty); // 빈 하트 이미지
}
}
// getItemCount: 아이템 수 반환
@Override
public int getItemCount() {
return dataList.size();
}
}
결과물을 한번 볼까요? Payload 파라미터를 넣지 않았을 때는 어떻게 동작할까요?
Payload 파라미터를 넣은 notifyItemChanged 메서드는 어떻게 동작하는지 결과를 먼저 보고 비교를 해봅시다.
뭐가 다른지 보이시나요? 잘 보시면 notifyItemChanged(position) 로 리사이클러뷰를 수정했을 때 분명 하트 요소만 변경했는데 해당 아이템의 다른 요소들도 깜빡 거리며 다시 그려지는 것이 보이시나요?
notifyItemChanged(position, payload)로 리사이클러뷰를 수정할 때는 다른 요소들은 깜빡거리지 않습니다.
이게 payload 파라미터가 있고 없고의 차이인데요 차이점은 다음과 같습니다.
notifyItemChanged(position)
- 해당 메서드는 지정된 위치(position)의 아이템이 변경되었음을 RecyclerView에 알립니다.
- 변경된 아이템은 RecyclerView에 다시 그려집니다.
- 이 메서드는 단순히 아이템이 변경되었음을 알릴 때 사용하며, 변경된 내용에 대한 추가적인 정보를 제공하지 않습니다.
notifyItemChanged(position, payload)
- 이 메서드는 지정된 위치(position)의 아이템이 변경되었음을 알리면서 변경된 내용에 대한 추가 정보(payload)를 제공할 수 있습니다.
- 변경 내용이 무엇인지 알리기 위해 payload를 사용할 수 있습니다. 예를 들어, 아이템의 일부 속성만 변경되었을 때, 이 변경 내용을 알리기 위해 payload를 사용할 수 있습니다.
- 변경된 아이템은 RecyclerView에 다시 그려집니다. 그러나 payload를 사용하면 RecyclerView는 변경 내용을 레이아웃 업데이트에 사용할 수 있으므로 불필요한 작업을 줄일 수 있습니다. 이것은 효율적인 업데이트를 가능하게 합니다.
조금 더 쉽게 풀어서 설명해 보자면 notifyItemChanged(position)은 아이템 자체가 변경되었음을 알릴 때 사용되는 것이 좋고
notifyItemChanged(position, payload)는 변경된 내용에 대한 추가 정보 (Payload)를 RecyclerView에 제공하여 아이템 내의 요소 만을 빠르게 변경할 수 있다. 정도로 알고 계시면 좋겠습니다.
좀 더 자세히 공부하고 싶다면 아래의 링크에 들어가셔서 문서를 한번 읽어보시면 좋아요
자 이제 notifyItemChanged(position, payload)는 어떻게 사용하면 될까요? 한번 구현해 보겠습니다.
위 코드에 Adepter.java 파일만 수정하시면 됩니다.
Adepter.java
package com.example.stickcodeex;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Adepter extends RecyclerView.Adapter<Adepter.ViewHolder> {
private ArrayList<Board> dataList;
public Adepter( ArrayList<Board> dataList) {
this.dataList = dataList;
}
// ViewHolder 클래스: 각 아이템의 뷰 요소를 보유
class ViewHolder extends RecyclerView.ViewHolder {
TextView Name, Content;
ImageView Like;
// ViewHolder 생성자
public ViewHolder(@NonNull View itemView) {
super(itemView);
// 뷰 요소 초기화
Name = itemView.findViewById(R.id.Name); // 이름 텍스트뷰
Content = itemView.findViewById(R.id.Content); // 내용 텍스트뷰
Like = itemView.findViewById(R.id.Like); // 좋아요 이미지뷰
// 좋아요 이미지뷰 클릭 리스너 설정
Like.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// "LikeChange" payload를 사용하여 아이템의 좋아요 상태 변경을 알립니다.
notifyItemChanged(getAdapterPosition(), "LikeChange");
}
});
}
}
// onCreateViewHolder: 뷰홀더 생성
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.item_view, parent, false);
return new ViewHolder(view);
}
// onBindViewHolder: 뷰홀더에 데이터 바인딩
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// 아이템 데이터 설정
holder.Name.setText(dataList.get(position).getName()); // 이름 설정
holder.Content.setText(dataList.get(position).getText()); // 내용 설정
// 아이템의 좋아요 상태에 따라 좋아요 이미지 설정
if (dataList.get(position).isLike()) {
holder.Like.setImageResource(R.drawable.liked); // 좋아요 이미지
} else {
holder.Like.setImageResource(R.drawable.empty); // 빈 하트 이미지
}
}
// onBindViewHolder: 뷰홀더에 데이터 바인딩 (payloads 사용)
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads); // payloads가 비어 있으면 기본 onBindViewHolder 호출
} else {
for (Object payload : payloads) {
if (payload instanceof String) {
String type = (String) payload;
if (TextUtils.equals(type, "LikeChange")) {
// "LikeChange" payload를 사용하여 아이템의 좋아요 상태 변경
if (dataList.get(position).isLike()) {
dataList.get(position).setLike(false);
holder.Like.setImageResource(R.drawable.empty);
} else {
dataList.get(position).setLike(true);
holder.Like.setImageResource(R.drawable.liked);
}
}
}
}
}
}
// getItemCount: 아이템 수 반환
@Override
public int getItemCount() {
return dataList.size();
}
}
복사 붙여 넣기로 끝내지 마시고 코드들이 어떤 순서로 동작하는지 하나하나 잘 살펴보시며 사용하고 싶은 대로 사용하시면 좋겠습니다. 감사합니다!
'안드로이드 자바' 카테고리의 다른 글
[Android][java] 서비스 onStartCommand()와 플래그 (0) | 2023.09.26 |
---|---|
[Android][Java] 비트맵 합치기 PorterDuff.Mode (0) | 2023.09.23 |
[Android][Java] 내 기기에서 오디오 파일 선택해 복사본 생성 및 파일 이름 가져오기 (0) | 2023.09.16 |
[Android][Java] 안드로이드에서 socket.io 로 SSL 통신할 때 Trust all certificates 설정해주기 (2) | 2023.09.07 |
[Android][Java] ConnectivityManager를 활용하여 현재 인터넷 상태 가져오기 (0) | 2023.09.06 |