티스토리 뷰

728x90

안드로이드에서 리스트뷰는 아주 자주 사용하는 뷰중에 하나인데요. 보통 커스텀한 리스트뷰를 생성하고 아이템클릭 리스너를 구현합니다. 하지만 여기서 각 리스트 항목에 스위치, 버튼 등 또 다른 클릭리스너가 필요한 뷰가 속해있으면 버튼 클릭 리스너는 잘 동작하지만 기존의 리스트뷰 아이템 클릭 리스너가 동작하지 않는 문제 발생하는데요. 

 

오늘은 커스텀 리스트 뷰에 버튼을 추가하고 리스트뷰 클릭 리스너도 같이 동작하도록 하는것에 대해 알아보겠습니다.

 

우선 액티비티 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"
    android:paddingStart="12dp"
    android:paddingEnd="12dp"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>


</androidx.constraintlayout.widget.ConstraintLayout>

 

리스트뷰의 각 항목이 될 레이아웃 xml파일을 생성하고 여러분들에게 맞게 커스텀하여 작성합니다.

저는 제목텍스트뷰와 내용 텍스트뷰 1개씩, 그리고 우측에 스위치버튼 하나씩이 오게 하였습니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/body"
        android:paddingStart="8dp"
        android:gravity="center|start"
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="8">

        <TextView
            android:id="@+id/title"
            android:textSize="20sp"
            android:text="Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

        </TextView>

        <TextView
            android:id="@+id/contents"
            android:textSize="15sp"
            android:text="This is List Example"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

        </TextView>
    </LinearLayout>

    <LinearLayout
        android:paddingEnd="12dp"
        android:layout_width="0dp"
        android:gravity="end|center"
        android:layout_weight="2"
        android:layout_height="match_parent">

        <Switch
            android:id="@+id/exSwitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

        </Switch>

    </LinearLayout>
</LinearLayout>

리스트뷰의 각 아이템이 될 레이아웃 예시입니다. 이는 여러분들의 개발 앱 취지에 맞게 다양하게 커스텀하시면 됩니다.

 

그 다음 리스트 어댑터 클래스를 작성합니다. 이 클래스는 BaseAdapter 클래스를 상속받아야합니다.

BaseAdapter를 상속받을 경우 4개의 메서드를 반드시 오버라이드 해야하는데요. 쉬운방법으로는 오류가 발생하는 곳에 커서를 두고 alt + enter를 눌러 바로 필요한 메서드를 오버라이드 할 수 있습니다.

 

package c.guitar.listviewexample;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.reflect.Array;
import java.util.ArrayList;

public class ButtonListAdapter extends BaseAdapter {
    Context context;
    LayoutInflater layoutInflater;
    ArrayList<String> data;
    public ButtonListAdapter(Context context, ArrayList<String> data){
        this.context = context;
        this.layoutInflater = LayoutInflater.from(context);
        this.data = data;
    }
    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = layoutInflater.inflate(R.layout.list_layout, null);

        TextView textView = view.findViewById(R.id.title);
        textView.setText(data.get(position));

        View bodyView = view.findViewById(R.id.body);
        Switch switchView = view.findViewById(R.id.exSwitch);

        bodyView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Toast.makeText(context, "click list body", Toast.LENGTH_SHORT).show();
            }
        });

        switchView.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener(){
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Toast.makeText(context, "click switch view", Toast.LENGTH_SHORT).show();}
        });
        return view;
    }
}

여기서 중요한점은 초기에 4개의 메서드의 반환값이 모두 null이거나 0으로 되어있는데 모두 커스텀 데이터에 맞게 제대로된 값을 리턴해줘야 리스트뷰를 inflate할 때 정상적으로 작동합니다.

 

layoutInflater는 리스트뷰를 생성할 밖의 액티비티의 context에서 가져옵니다.

그 후 커스텀한 각 아이템 레이아웃 xml파일을 inflate해주고 반드시 getView메서드에서 inflate한 View를 리턴해줘야합니다. 만약 기본값 그대로 null을 리턴하면 오류가 발생합니다. 위의 나머지 세개의 메서드도 모두 마찬가지입니다.

 

여기서 이제 중요한점은 리스트뷰의 아이템에 버튼, 스위치 같은 클릭이벤트가 필요한 뷰를 추가하면 기존의 리스트뷰 아이템 클릭리스너가 동작하지 않게됩니다. 그래서 제가 예제로 작성한 xml에서 스위치뷰를 제외한 나머지 body에 해당하는 부분의 linearlayout에 id값을 주고 이 body의 클릭리스너를 별도로 구현하였습니다.

 

만약 여기서 기존에 쓰던 리스트뷰의 onItemClickListener를 사용하면 switch의 클릭 리스너 때문에 동작하지 않습니다.

이렇게 따로 클릭리스너를 구현하면 스위치의 클릭 리스너와 body부분의 클릭리스너가 모두 정상적으로 동작합니다.

 

이제 액티비티에서 리스트뷰에 어댑터를 적용시키면 완료입니다.

저는 샘플데이터로 단순한 스트링 어레이 리스트를 만들어 어댑터의 생성자로 넘겨 세개의 리스트항목을 만들었습니다.

각 세개의 항목은 모두 스위치 클릭 리스너와 그 외의 바디부분의 클릭 리스너를 갖고 정상적으로 동작합니다.

 

 

package c.guitar.listviewexample;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    ListView exampleList;
    ArrayList<String> dataSample;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dataSample = new ArrayList<String>();

        dataSample.add("Test1");
        dataSample.add("Test2");
        dataSample.add("Test3");

        exampleList = findViewById(R.id.listView);
        ButtonListAdapter buttonListAdapter = new ButtonListAdapter(this, dataSample);

        exampleList.setAdapter(buttonListAdapter);
    }
}

 

각 세개의 항목은 모두 스위치 클릭 리스너와 그 외의 바디부분의 클릭 리스너를 갖고 정상적으로 동작합니다.

 

다음은 아이템의 바디부분과 스위치를 눌렀을 때 토스트메세지를 띄운 동작화면입니다.

잘 안되시는분은 댓글 남기시면 정성껏 한분한분 답변드리도록 하겠습니다. 감사합니다

 

잘 안되시는분은 댓글 남기시면 정성껏 한분한분 답변드리도록 하겠습니다. 감사합니다

댓글