2010년 9월 13일 월요일

커스텀 ArrayAdapter 사용하기 (Use custom ArrayAdapter)

이전 포스트에서 /res/layout에 레이아웃 xml 파일을 사용하여 ListView의 레이아웃을 변경해 보았었다.
하지만 그 경우에도 ListView의 각 엔트리에 한개의 값만 넣을 수 있었고 텍스트 뷰 이외를 사용하지 못했었다. 그래서 이번 포스팅에서는 ArrayAdapter를 상속받은 커스텀 ArrayAdapter를 만들고 getView() 메소드를 오버라이드 하여 TextView 외에 ImageView도 같이 집어넣어 보도록 하겠다.



간단한 주소록을 보여주는 앱을 만들것이므로 먼저 String 대신 주소록 엔트리를 위한 클래스를 정의한다.

/src/.../ABEntry.java

package app.arsviator;

public class ABEntry {
    private String name;
    private String phoneNo;
    private int photo;
   
    public ABEntry(String _name, String _pn, int _photo) {
        this.name = _name;
        this.phoneNo = _pn;
        this.photo = _photo;
    }
   
    public String getName() {
        return name;
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public int getPhotoId() {
        return photo;
    }
}

위의 소스코드를 src 디렉토리에 ABEntry.java 란 이름으로 저장해주면 된다.

그 다음은 ListView의 각 엔트리를 위한 레이아웃을 정의한다.


여기서는 위와 같이 레이아웃을 정의했다.

/res/layout/entry.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:paddingTop="5px"
    android:paddingBottom="5px"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >
  <ImageView android:id="@+id/ePhoto"
    android:layout_width="48px"
    android:layout_height="48px"
    android:src="@drawable/nophoto" />
  <LinearLayout android:orientation="vertical"
      android:paddingLeft="10px"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/eName"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="22sp" />
    <TextView android:id="@+id/ePhoneNo"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="16sp" />
  </LinearLayout>
  <TextView android:id="@+id/eNull"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content" />
</LinearLayout>

위의 소스를 /res/layout/entry.xml 로 저장해주면 된다.

    private class ABArrayAdapter extends ArrayAdapter<ABEntry> {
        private ArrayList<ABEntry> items;
        private int rsrc;
       
        public ABArrayAdapter(Context ctx, int rsrcId, int txtId, ArrayList<ABEntry> data) {
            super(ctx, rsrcId, txtId, data);
            this.items = data;
            this.rsrc = rsrcId;
        }
       
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = li.inflate(rsrc, null);
            }
            ABEntry e = items.get(position);
            if (e != null) {
                ((TextView)v.findViewById(R.id.eName)).setText(e.getName());
                ((TextView)v.findViewById(R.id.ePhoneNo)).setText(e.getPhoneNo());
                if (e.getPhotoId() != -1) {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(e.getPhotoId());               
                } else {
 ((ImageView)v.findViewById(R.id.ePhoto)).setImageResource(R.drawable.nophoto); 
                }
            }
            return v;
        }
    }
}

이번 포스팅의 가장 핵심 부분인 ArrayAdapter를 상속받은 ABArrayAdapter 클래스이다. 오버라이드 한 getView() 안에서 우선 LayoutInflator를 사용해서 /res/layout/entry.xml을 View로 inflate한다.

LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(rsrc, null);

그 다음 ABEntry 오브젝트에 있는 이름, 전화번호, 사진을 각각 eName, ePhoneNo, ePhoto에 넣어준다. 사진이 없는 경우(e.getPhotoId() == -1)는 디폴트 사진(여기서는 강아지 그림)을 표시하고 사진이 지정되어 있는 경우는 /res/drawable에 있는 사진을 가져오도록 해 놓았다.

CustomAA.java의 전체 소스는 다음과 같다.

펼쳐두기..


그 이외에 필요한 파일은 다음과 같다.

펼쳐두기..


프로젝트에 필요한 이미지 파일은 아래 zip 파일을 다운받아 res 디렉토리에서 압축을 풀면 drawable디렉토리 아래에 만들어진다.



실행하면 위에서같이 간단한 주소록이 나오게 되고 그 중 한 항목을 선택하면 전화를 걸거나 항목을 삭제할 수 있도록 AlertDialog가 나오게 된다.


전화를 걸기 위해서는 AndroidManifest.xml  파일에 CALL_PHONE permission을 추가하는걸 잊으면 안된다.

펼쳐두기..







ListView의 레이아웃 변경 (Change Layout for ListView)

안드로이드에서는 ListView가 화면에 보이는 레이아웃을 XML로 정의할 수 있다.

대부분의 책에 보면 ListView의 예제 코드에서는 android.R.layout.simple_list_item_1을 사용하고 있다. 그 경우 레이아웃은 아래와 같다.

    adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, R.id.listentry, data);
 
    setListAdapter(adapter);


android.R로 시작하는 경우 안드로이드에 기본으로 들어있는 리소스들을 참조하는 것이다. 안드로이드에 기본으로 들어있는 layout의 목록은 다음과 같다.

android.R.layout.activity_list_item
android.R.layout.browser_link_context_header
android.R.layout.expandable_list_content
android.R.layout.preference_category
android.R.layout.select_dialog_item
android.R.layout.select_dialog_multichoice
android.R.layout.select_dialog_singlechoice
android.R.layout.simple_dropdown_item_1line
android.R.layout.simple_expandable_list_item_1
android.R.layout.simple_expandable_list_item_2
android.R.layout.simple_gallery_item
android.R.layout.simple_list_item_1
android.R.layout.simple_list_item_2
android.R.layout.simple_list_item_checked
android.R.layout.simple_list_item_multiple_choice
android.R.layout.simple_list_item_single_choice
android.R.layout.simple_spinner_dropdown_item
android.R.layout.simple_spinner_item
android.R.layout.test_list_item
android.R.layout.two_line_list_item


아래는 android.R.layout.simple_expandable_list_item_1을 사용한 경우이다.


아래는 android.R.layout.simple_expandable_list_item_single_choice를 사용한 경우이다.


안드로이드에 기본적으로 들어있는 레이아웃이 마음에 드는게 없으면 직접 레이아웃을 정의해 사용할 수도 있다.

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/listentry"
      android:textSize="16sp"
      android:textColor="#ff0000"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

위와같이 /res/layout 디렉토리에 xml파일을 넣어주고 ArrayAdapter를 만들때 레이아웃 파일을 지정해 주면 된다.

    adapter = new ArrayAdapter<String>(this, R.layout.simple_layout, R.id.listentry, data);
 
    setListAdapter(adapter);



레이아웃에 단순히 TextView 하나만 넣지 않고 컨테이너를 사용해서 여러 위젯을 집어넣을 수도 있다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >
  <TextView android:text="Airport:"
          android:textSize="14sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
  <TextView android:id="@+id/listentry"
          android:textSize="18sp"
          android:textColor="#ff0000"
          android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

    adapter = new ArrayAdapter<String>(this, R.layout.entrylayout, R.id.listentry, data);
 
    setListAdapter(adapter);