Giới thiệu
Ví dụ này minh họa cách.highlight (tô sáng) item được chọn trên RecyclerView một cách đúng đắn, sử dụng SparseBooleanArray để theo dõi trạng thái chọn và StateListDrawable (selector) cho background.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project. Thêm dependency RecyclerView:
dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
}
Bước 2: Giao diện chính (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>
Bước 3: Layout item (res/layout/list_layout.xml)
<?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="wrap_content"
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="Award Title"
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="Award Year"
android:textColor="#000" />
<TextView
android:id="@+id/playerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Player Name" />
</RelativeLayout>
Bước 4: Selector background (res/drawable/background_selector.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
<!-- Item được chọn (selected) -->
<item android:drawable="@color/colorAccent" android:state_selected="true" />
<!-- Item bình thường -->
<item android:drawable="@android:color/white" android:state_selected="false" />
</selector>
Bước 5: Divider (res/drawable/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>
Bước 6: Model Item (ItemObject.java)
package com.app.sample;
public class ItemObject {
private String awardTitle;
private String awardYear;
private String player;
public ItemObject(String awardTitle, String awardYear, String player) {
this.awardTitle = awardTitle;
this.awardYear = awardYear;
this.player = player;
}
public String getAwardTitle() { return awardTitle; }
public String getAwardYear() { return awardYear; }
public String getPlayer() { return player; }
}
Bước 7: RecyclerViewAdapter (RecyclerViewAdapter.java)
package com.app.sample;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolders> {
private List<ItemObject> itemList;
public 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, parent, false);
return new RecyclerViewHolders(layoutView);
}
@Override
public void onBindViewHolder(RecyclerViewHolders holder, int position) {
ItemObject item = itemList.get(position);
holder.awardTitle.setText("Award Title: " + item.getAwardTitle());
holder.awardYear.setText("Award Year: " + item.getAwardYear());
holder.player.setText("Player Name: " + item.getPlayer());
}
@Override
public int getItemCount() {
return itemList.size();
}
}
Bước 8: ViewHolder xử lý click (RecyclerViewHolders.java)
package com.app.sample;
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, awardYear, 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) {
int pos = getAdapterPosition();
if (selectedItems.get(pos, false)) {
selectedItems.delete(pos);
view.setSelected(false);
} else {
selectedItems.put(pos, true);
view.setSelected(true);
}
}
}
Bước 9: ItemDecoration (SimpleItemDecoration.java)
package com.app.sample;
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);
}
}
}
Bước 10: MainActivity (src/MainActivity.java)
package com.app.sample;
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(this);
recyclerView.setLayoutManager(layoutManager);
List<ItemObject> posts = returnListItems();
RecyclerViewAdapter adapter = new RecyclerViewAdapter(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", "Chưa quyết định"));
return items;
}
}
Bước 11: Manifest (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>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Nhấn vào các item để chọn/bỏ chọn, background sẽ đổi màu theo trạng thái.

Cải tiến: Chế độ chọn đơn (Single Choice) vs chọn nhiều (Multi Choice)
Code hiện tại cho phép chọn nhiều item (Multi Choice). Để chỉ cho chọn 1 item (Single Choice), sửa ViewHolder:
// Trong Adapter hoặc ViewHolder
private int selectedPosition = RecyclerView.NO_POSITION;
@Override
public void onClick(View view) {
int pos = getAdapterPosition();
int oldSelected = selectedPosition;
selectedPosition = (selectedPosition == pos) ? RecyclerView.NO_POSITION : pos;
notifyItemChanged(oldSelected);
notifyItemChanged(selectedPosition);
}
// Trong onBindViewHolder
holder.itemView.setSelected(position == selectedPosition);
Best Practices
- Dùng
SparseBooleanArraythay vìHashMap<Integer, Boolean>để tiết kiệm bộ nhớ. - Kết hợp với
ItemTouchHelpernếu cần drag-drop hoặc swipe-to-dismiss.
view.setSelected() trong onBindViewHolder để khôi phục đúng khi recycle.