Computer >> 컴퓨터 >  >> 프로그래밍 >> Android

Android RecyclerView에서 선택한 항목 강조 표시하기: 단계별 완벽 가이드

이 튜토리얼에서는 Android RecyclerView에서 사용자가 선택한 항목을 올바르게 강조 표시(highlight)하는 방법을 단계별로 알아봅니다. 항목을 탭하면 배경색이 변경되고, 다시 탭하면 선택이 해제되는 토글 방식의 선택 기능을 구현할 수 있습니다.

Step 1 — 새 프로젝트 생성 및 의존성 추가

Android Studio에서 File → New Project를 선택하여 새 프로젝트를 생성하고, 필요한 모든 정보를 입력합니다.

그다음 build.gradle (Module: app) 파일에 아래 의존성을 추가합니다.

implementation 'com.android.support:recyclerview-v7:28.0.0'

Step 2 — 메인 레이아웃 작성 (activity_main.xml)

res/layout/activity_main.xml 파일에 다음 코드를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="https://schemas.android.com/apk/res/android"
   xmlns:tools="https://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:padding="4dp"
   tools:context=".MainActivity">
   <androidx.recyclerview.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:paddingBottom="8dp" />
</RelativeLayout>

Step 3 — 리스트 항목 레이아웃 작성 (list_layout.xml)

새로운 레이아웃 리소스 파일(list_layout.xml)을 생성하고 아래 코드를 추가합니다. 이 레이아웃은 각 리스트 항목의 UI를 정의하며, 컨테이너에 선택 상태에 따라 배경이 바뀌도록 background_selector를 적용한 것이 핵심입니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="https://schemas.android.com/apk/res/android"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@drawable/background_selector"
   android:padding="16dp">
   <TextView
      android:id="@+id/awardTitle"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="10dp"
      android:text="New Text"
      android:textSize="18sp"
      android:textStyle="bold" />
   <TextView
      android:id="@+id/awardYear"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@+id/awardTitle"
      android:layout_alignStart="@+id/awardTitle"
      android:layout_marginTop="20dp"
      android:text="New Text"
      android:textColor="#000" />
   <TextView
      android:id="@+id/playerName"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentEnd="true"
      android:text="New Text" />
</RelativeLayout>

Step 4 — Drawable 리소스 파일 생성

선택 상태에 따라 배경을 전환해 주는 셀렉터와, 항목 사이를 구분해 주는 구분선 드로어블을 각각 생성합니다.

background_selector.xml

항목이 선택(selected)된 상태일 때는 액센트 컬러를, 그렇지 않을 때는 흰색 배경을 표시합니다.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
   <item android:drawable="@color/colorAccent" android:state_pressed="false" android:state_selected="true" />
   <item android:drawable="@android:color/white" android:state_selected="false" />
</selector>

line_divider.xml

리스트 항목 사이에 표시될 얇은 구분선을 정의합니다.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="https://schemas.android.com/apk/res/android" android:shape="rectangle">
   <size
      android:width="2dp"
      android:height="2dp" />
   <solid android:color="@color/colorPrimaryDark" />
</shape>

Step 5 — Java 클래스 파일 작성

데이터 모델, 어댑터, 뷰홀더, 아이템 데코레이션 클래스를 차례대로 생성합니다.

ItemObject.java — 데이터 모델

리스트에 표시할 데이터(수상 제목, 수상 연도, 선수 이름)를 담는 모델 클래스입니다.

class ItemObject {
   private String awardTitle;
   private String awardYear;
   private String player;
   ItemObject(String awardTitle, String awardYear, String player) {
      this.awardTitle = awardTitle;
      this.awardYear = awardYear;
      this.player = player;
   }
   String getAwardTitle() {
      return awardTitle;
   }
   String getAwardYear() {
      return awardYear;
   }
   String getPlayer() {
      return player;
   }
}

RecyclerViewAdapter.java — 어댑터

RecyclerView에 데이터를 바인딩하는 어댑터 클래스입니다.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class RecyclerViewAdapter extends
RecyclerView.Adapter<RecyclerViewHolders> {
   private List<ItemObject> itemList;
   RecyclerViewAdapter(Context context, List<ItemObject> itemList) {
      this.itemList = itemList;
   }
   @NonNull
   @Override
   public RecyclerViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
      View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_layout, null);
      return new RecyclerViewHolders(layoutView);
   }
   @Override
   public void onBindViewHolder(RecyclerViewHolders holder, int position) {
      holder.awardTitle.setText("Award Title: " + itemList.get(position).getAwardTitle());
      holder.awardYear.setText("Award Year: " + itemList.get(position).getAwardYear());
      holder.player.setText("Player Name: " + itemList.get(position).getPlayer());
   }
   @Override
   public int getItemCount() {
      return this.itemList.size();
   }
}

RecyclerViewHolders.java — 뷰홀더 (선택 로직의 핵심)

이 예제의 핵심 부분입니다. SparseBooleanArray를 사용해 각 항목의 선택 여부를 추적하고, 클릭 시 view.setSelected()를 호출하여 배경 셀렉터가 동작하도록 합니다. 이미 선택된 항목을 다시 클릭하면 선택이 해제되는 토글 방식으로 동작합니다.

import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
class RecyclerViewHolders extends RecyclerView.ViewHolder implements
View.OnClickListener {
   TextView awardTitle;
   TextView awardYear;
   TextView player;
   private SparseBooleanArray selectedItems = new SparseBooleanArray();
   RecyclerViewHolders(View itemView) {
      super(itemView);
      itemView.setOnClickListener(this);
      awardTitle = itemView.findViewById(R.id.awardTitle);
      awardYear = itemView.findViewById(R.id.awardYear);
      player = itemView.findViewById(R.id.playerName);
   }
   @Override
   public void onClick(View view) {
      if (selectedItems.get(getAdapterPosition(), false)) {
         selectedItems.delete(getAdapterPosition());
         view.setSelected(false);
      }
      else {
         selectedItems.put(getAdapterPosition(), true);
         view.setSelected(true);
      }
   }
}

SimpleItemDecoration.java — 항목 구분선

RecyclerView의 항목 사이에 구분선을 그려주는 커스텀 ItemDecoration 클래스입니다.

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
class SimpleItemDecoration extends RecyclerView.ItemDecoration {
   private Drawable drawable;
   SimpleItemDecoration(Context context) {
      drawable = ContextCompat.getDrawable(context, R.drawable.line_divider);
   }
   @Override
   public void onDrawOver(@NonNull Canvas canvas, RecyclerView parent, @NonNull RecyclerView.State state) {
      int left = parent.getPaddingLeft();
      int right = parent.getWidth() - parent.getPaddingRight();
      int childCount = parent.getChildCount();
      for (int i = 0; i < childCount; i++) {
         View child = parent.getChildAt(i);
         RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
         int top = child.getBottom() + params.bottomMargin;
         int bottom = top + drawable.getIntrinsicHeight();
         drawable.setBounds(left, top, right, bottom);
         drawable.draw(canvas);
      }
   }
}

Step 6 — MainActivity 작성

src/MainActivity.java에 다음 코드를 추가합니다. RecyclerView에 레이아웃 매니저와 어댑터, 그리고 구분선 데코레이션을 설정합니다.

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      RecyclerView recyclerView = findViewById(R.id.recyclerView);
      recyclerView.addItemDecoration(new SimpleItemDecoration(this));
      LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
      recyclerView.setLayoutManager(layoutManager);
      List<ItemObject> posts = returnListItems();
      RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainActivity.this, posts);
      recyclerView.setAdapter(adapter);
   }
   private List<ItemObject> returnListItems() {
      List<ItemObject> items = new ArrayList<>();
      items.add(new ItemObject("Ballon'd'or", "2007", "Ricardo KaKa"));
      items.add(new ItemObject("Ballon'd'or", "2008", "Cristiano Ronaldo"));
      items.add(new ItemObject("Ballon'd'or", "2009 - 2012, 2015", "Lionel Messi"));
      items.add(new ItemObject("Ballon'd'or", "2013, 2014, 2016, 2017", "Cristiano Ronaldo"));
      items.add(new ItemObject("Ballon'd'or", "2018", "Luca Modric"));
      items.add(new ItemObject("Ballon'd'or", "2019", "Haven't decided yet"));
      return items;
   }
}

Step 7 — AndroidManifest.xml 설정

AndroidManifest.xml에 아래 코드를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="app.com.sample">
   <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
      <activity android:name=".MainActivity">
         <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
   </application>
</manifest>

앱 실행 및 결과 확인

이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 스마트폰이 컴퓨터와 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바의 Run(실행) 아이콘을 클릭하세요. 실행 기기 목록에서 본인의 모바일 기기를 선택하면, 앱이 설치되고 아래와 같은 화면이 표시됩니다.

리스트에서 항목을 탭하면 해당 항목의 배경이 액센트 컬러로 강조 표시되고, 다시 탭하면 흰색 배경으로 돌아가는 것을 확인할 수 있습니다.

Android RecyclerView에서 선택한 항목 강조 표시하기: 단계별 완벽 가이드