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

Android ListView에서 체크된 모든 항목 가져오기

이 튜토리얼은 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 대신 RecyclerViewViewBinding/DataBinding, 그리고 Kotlin 코루틴/Flow를 조합해 더 깔끔하게 구현합니다. 이 예제는 레거시 코드베이스 유지보수나 기초 개념 학습용으로 참고하세요.