리스트뷰(ListView)는 안드로이드에서 가장 많이 사용되는 위젯(widget)들 중의 하나이다. 리스트뷰와 같이 여러 아이템들 중 하나를 선택하는 위젯을 선택위젯이라 부른다. 그리고 선택 위젯은 다른 위젯들과 사용하는 방식이 다르다. 일반 위젯은 데이터를 위젯 위에 바로 표시하지만 선택 위젯은 일반적으로 어댑터(Adapter) 패턴을 사용하며 데이터를 위젯이 아닌 어댑터에 설정하고 뷰도 어댑터에서 만든다.

1

어댑터에서는 데이터를 설정하고 뷰를 구성하여 getView() 메소드를 통해 뷰 객체를 return 한다.



리스트뷰는 사용 패턴이 정해져있어 한 번 익혀두면 계속 쓸 수 있다. 리스트뷰를 사용하는 과정을 설명하면서 간단한 앱을 하나 만들어보자.


1. 각 아이템을 보여줄 레이아웃 정의하기

리스트뷰에는 몇 개의 아이템이 있던지간에 각 아이템이 표시되는 레이아웃은 동일하다. 예를들면 전화번호부는 왼쪽에 사진이 있고, 오른쪽에 이름과 전화번호, 나이가 있는 형식일 수 있다. 이 단계에서는 이 레이아웃을 정의한다.


<person_item.xml\>

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/panho"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginLeft="10dp">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="New Text"
            android:textSize="40dp"
            android:textStyle="bold"
            android:textColor="#ff5805"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:text="New Text"
                android:textSize="20dp"
                android:textColor="#0223e0"
                android:layout_marginTop="6dp"/>

            <TextView
                android:id="@+id/textView3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:text="New Text"
                android:textSize="20dp"
                android:textColor="#4902d6" />

        </RelativeLayout>

    </LinearLayout>

</LinearLayout>

이 레이아웃은 왼쪽에 사진을 넣고 오른쪽에는 수직 리니어 레이아웃을 넣어 윗쪽에는 이름을, 아랫쪽에는 휴대폰 번호와 나이를 넣기위해 구성하였다.

형태는 아래 사진과 같이 나온다.

2

구성된 레이아웃을 메모리에 인플레이션하고 컨트롤 하기위한 Java 클래스도 정의해야 한다.



<PersonItemView.java\>

package io.swnomad.samplelistview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class PersonItemView extends LinearLayout {

    TextView textView1;
    TextView textView2;
    TextView textView3;
    ImageView imageView;

    public PersonItemView(Context context){
        super(context);
        init(context);
    }

    public PersonItemView(Context context, AttributeSet attrs){
        super(context, attrs);
        init(context);
    }

    public void init(Context context){

        /* 아이템 보여줄 뷰를 객체로 인플레이션 */
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.person_item, this, true);

        textView1 = findViewById(R.id.textView1);
        textView2 = findViewById(R.id.textView2);
        textView3 = findViewById(R.id.textView3);
        imageView = findViewById(R.id.imageView);
    }

    public void setImage(int resId){
        imageView.setImageResource(resId);
    }

    public void setName(String name){
        textView1.setText(name);
    }

    public void setMobile(String mobile){
        textView2.setText(mobile);
    }

    public void setAge(int age){
        textView3.setText(String.valueOf(age));
    }
}


2. 실제 데이터를 위한 클래스 정의하기

각 아이템을 위한 뷰를 구성했기때문에 이제 각 아이템에 추가할 데이터를 위한 클래스를 정의해야한다.



<PersonItem.java\>

package io.swnomad.samplelistview;

public class PersonItem {
    String name;
    String mobile;
    int age;
    int resId;

    public PersonItem(String name, String mobile, int age, int resId){
        this.name =  name;
        this.mobile = mobile;
        this.age = age;
        this.resId = resId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getResId() {
        return resId;
    }

    public void setResId(int resId) {
        this.resId = resId;
    }
}


3. 어댑터 클래스 정의

리스트뷰와 같은 선택위젯은 데이터를 어댑터에 설정하고 뷰도 어댑터에서 구성하여 뿌려준다고 했다. BaseAdapter 클래스는 어댑터를 만들 때 상속받는 가장 일반적인 클래스이다. 이 클래스는 MainActivity 클래스의 내부 클래스로 정의해야한다. 그래야 Context를 가져올 수 있기 때문이다.


/* BaseAdapter 클래스 상속받아 SingerAdapter 클래스 정의 */
class PersonAdapter extends BaseAdapter{
    ArrayList<PersonItem> items = new ArrayList<>(); //SingerItem 객체를 저장할 ArrayList 객체 생성

    @Override
    public int getCount() { //데이터의 갯수 리턴
        return items.size();
    }

    public void addItem(PersonItem item){ //ArrayList에 새로운 SingerItem 객체 추가하는 메소드
        items.add(item);
    }

    public Object getItem(int position){ // index를 통해 ArrayList의 SingerItem 객체 반환
        return items.get(position);
    }

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

    /* getView() 메소드는 어댑터에 데이터를 설정하고 뷰를 구성하여 리턴해주는 메소드이다. */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        PersonItemView view = new PersonItemView(getApplicationContext()); //뷰 객체 생성
        PersonItem item = items.get(position); //뷰에 적용할 데이터 가져오기

        /* 뷰에 데이터 설정 */
        view.setName(item.getName());
        view.setAge(item.getAge());
        view.setMobile(item.getMobile());
        view.setImage(item.getResId());

        return view; //뷰를 리턴
    }
}

여기서 중요한 건 getCount() 메소드와 getView() 메소드이다. getCount() 메소드는 어댑터에 설정된 데이터의 갯수를 관리한다. getView() 메소드는 리스트뷰의 각 아이템의 뷰를 구성해주므로 이 메소드가 잘못되면 화면이 의도하지 않은대로 보일 수 있다. 그리고 어댑터에 아이템을 추가하기 위한 addItem()와 아이템을 가져오기 위해서 getItem() 메소드도 정의하였다.





이제 리스트뷰를 위한 뷰와 데이터클래스, 그리고 어댑터를 다 만들었으므로 실제로 리스트뷰에 데이터를 나타내기 위해서 MainActivity 클래스에서 코드를 작성해보자.

public class MainActivity extends AppCompatActivity {

    ListView listView;
    PersonAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        adapter = new PersonAdapter(); //어댑터 객체 생성
        /* 어댑터에 데이터 추가 */
        adapter.addItem(new PersonItem("김판호", "123-1234-1234", 30, R.drawable.panho));
        adapter.addItem(new PersonItem("최형배", "321-3211-3211", 30, R.drawable.hangebae));
        adapter.addItem(new PersonItem("최익현", "567-5675-5676", 50, R.drawable.ikhyeon));

        listView = findViewById(R.id.listView);
        listView.setAdapter(adapter); //리스트뷰에 어댑터 적용

        /* 리스트의 아이템 클릭 이벤트 리스너 객체 설정 */
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                PersonItem item = (PersonItem) adapter.getItem(position);
                Toast.makeText(getApplicationContext(), item.getName(), Toast.LENGTH_LONG).show();
            }
        });
    }
}

어댑터 객체를 생성하고 아이템을 추가한다. 그리고 아이템이 선택되었을 때 원하는 효과를 구현하기 위해 setOnItemClickListener() 메소드를 통해서 리스너 객체를 달아주었다. 아이템을 선택하면 토스트 메시지로 아이템의 이름이 뜨도록 만들었다.



앱을 실행하면 다음과 같은 화면을 볼 수 있다.

3

+ Recent posts