이 튜토리얼은 Android ListView에서 사용자가 체크한 모든 항목을 가져오는 방법을 단계별로 설명합니다. 커스텀 어댑터와 모델 클래스를 활용해 체크 상태를 관리하고, 버튼 클릭 시 선택된 항목 목록을 확인하는 예제를 다룹니다.
1. 새 프로젝트 생성
Android Studio에서 File → New → New Project를 선택해 빈 액티비티(Empty Activity) 프로젝트를 생성합니다. 언어는 Java로 설정합니다.
2. 메인 레이아웃 구성 (activity_main.xml)
전체 선택 버튼과 리스트뷰를 세로로 배치합니다. ListView에는 layout_weight를 주어 남은 공간을 모두 차지하도록 설정했습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btnSelectAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="전체 선택" />
<Button
android:id="@+id/btnDeselectAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="전체 해제" />
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_weight="1" />
</LinearLayout>
3. 리스트 아이템 레이아웃 (list_item.xml)
각 행에 체크박스와 텍스트뷰를 가로로 배치합니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvPlayerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textSize="18sp" />
</LinearLayout>
4. 데이터 모델 클래스 (Model.java)
선수 이름과 선택 상태를 보관하는 간단한 POJO 클래스입니다.
public class Model {
private boolean isSelected;
private String playerName;
public String getPlayerName() {
return playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
5. 커스텀 어댑터 구현 (CustomAdapter.java)
BaseAdapter를 상속받아 뷰 홀더 패턴으로 최적화했습니다. 체크박스 클릭 시 모델의 상태를 갱신합니다.
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class CustomAdapter extends BaseAdapter {
private final Context context;
private final ArrayList modelList;
public CustomAdapter(Context context, ArrayList modelList) {
this.context = context;
this.modelList = modelList;
}
@Override
public int getCount() {
return modelList.size();
}
@Override
public Object getItem(int position) {
return modelList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
holder.checkBox = convertView.findViewById(R.id.checkBox);
holder.tvPlayerName = convertView.findViewById(R.id.tvPlayerName);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Model model = modelList.get(position);
holder.tvPlayerName.setText(model.getPlayerName());
holder.checkBox.setChecked(model.isSelected());
// 체크박스 태그에 위치 정보 저장
holder.checkBox.setTag(position);
holder.checkBox.setOnClickListener(v -> {
int pos = (int) v.getTag();
Model clickedModel = modelList.get(pos);
clickedModel.setSelected(!clickedModel.isSelected());
Toast.makeText(context, "항목 " + pos + " 클릭됨", Toast.LENGTH_SHORT).show();
});
return convertView;
}
static class ViewHolder {
CheckBox checkBox;
TextView tvPlayerName;
}
/** 현재 체크된 모든 항목의 위치 인덱스 반환 */
public ArrayList getCheckedPositions() {
ArrayList checked = new ArrayList<>();
for (int i = 0; i < modelList.size(); i++) {
if (modelList.get(i).isSelected()) {
checked.add(i);
}
}
return checked;
}
}
6. 문자열 리소스 (strings.xml)
<resources>
<string name="app_name">CheckedListViewDemo</string>
</resources>
7. 메인 액티비티 구현 (MainActivity.java)
데이터 초기화, 어댑터 연결, 버튼 이벤트 처리, 그리고 체크된 항목 가져오기 로직을 포함합니다.
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private CustomAdapter adapter;
private ArrayList modelList;
private Button btnSelectAll, btnDeselectAll, btnShowChecked;
private static final String[] PLAYERS = {
"손흥민 - 대한민국",
"크리스티아누 호날두 - 포르투갈",
"리오넬 메시 - 아르헨티나",
"네이마르 - 브라질",
"에당 아자르 - 벨기에",
"잔루이지 부폰 - 이탈리아",
"하메스 로드리게스 - 콜롬비아",
"사디오 마네 - 세네갈",
"토니 크로스 - 독일"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listView);
btnSelectAll = findViewById(R.id.btnSelectAll);
btnDeselectAll = findViewById(R.id.btnDeselectAll);
btnShowChecked = findViewById(R.id.btnShowChecked); // XML에 추가 필요
modelList = createModelList(false);
adapter = new CustomAdapter(this, modelList);
listView.setAdapter(adapter);
// 전체 선택
btnSelectAll.setOnClickListener(v -> {
modelList = createModelList(true);
adapter = new CustomAdapter(this, modelList);
listView.setAdapter(adapter);
Toast.makeText(this, "전체 선택됨", Toast.LENGTH_SHORT).show();
});
// 전체 해제
btnDeselectAll.setOnClickListener(v -> {
modelList = createModelList(false);
adapter = new CustomAdapter(this, modelList);
listView.setAdapter(adapter);
Toast.makeText(this, "전체 해제됨", Toast.LENGTH_SHORT).show();
});
// 체크된 항목 확인 (핵심 기능)
btnShowChecked.setOnClickListener(v -> {
ArrayList checkedPositions = adapter.getCheckedPositions();
if (checkedPositions.isEmpty()) {
Toast.makeText(this, "선택된 항목이 없습니다.", Toast.LENGTH_SHORT).show();
} else {
StringBuilder sb = new StringBuilder("선택된 인덱스: ");
for (int pos : checkedPositions) {
sb.append(pos).append(", ");
}
Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
// 실제 앱에서는 이 리스트를 이용해 비즈니스 로직 수행
}
});
}
private ArrayList createModelList(boolean isSelected) {
ArrayList list = new ArrayList<>();
for (String player : PLAYERS) {
Model model = new Model();
model.setPlayerName(player);
model.setSelected(isSelected);
list.add(model);
}
return list;
}
}
8. 체크된 항목 가져오기 핵심 포인트
- 모델에 상태 저장: 체크박스 상태를 뷰가 아닌 데이터 모델(
Model.isSelected)에 저장해야 스크롤 시 상태가 유지됩니다. - 어댑터에 조회 메서드 제공:
CustomAdapter.getCheckedPositions()처럼 어댑터가 현재 데이터셋을 순회하며 선택된 항목을 반환하도록 구현합니다. - 어댑터 재생성 주의: 전체 선택/해제 시 새 어댑터를 만들어
setAdapter()를 호출하면 스크롤 위치가 초기화됩니다. 실무에서는notifyDataSetChanged()로 갱신하는 것이 더 자연스럽습니다.
9. 실행 결과 확인
앱을 실행하면 선수 목록이 체크박스와 함께 표시됩니다. 개별 항목을 체크하거나 "전체 선택/해제" 버튼을 누른 뒤 "체크된 항목 보기" 버튼을 누르면 선택된 항목의 인덱스가 토스트로 나타납니다.
참고: 최신 Android 개발에서는
ListView대신RecyclerView와ViewBinding/DataBinding, 그리고 Kotlin 코루틴/Flow를 조합해 더 깔끔하게 구현합니다. 이 예제는 레거시 코드베이스 유지보수나 기초 개념 학습용으로 참고하세요.