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을 추가하는걸 잊으면 안된다.

펼쳐두기..







댓글 12개:

  1. Hey really nice website, I noticed your website when doing study on some methods to develop my web log. I was simply inquiring which spam software system you use for comments as I get tons on my site.

    I never comment on blogs, but this one is awesome! Thanks.

    답글삭제
  2. 안녕하세요. 올리신 글로 공부를 하고 있습니다.

    궁금한 내용이 있어서요.

    어드레스북을 리스트형식으로 해서 보여주는데 xml을 보면 main.xml의

    <ListView android:id="@android:id/list"

    android:layout_width="fill_parent"

    android:layout_height="320px"

    android:drawSelectorOnTop="false" />

    이 부분에 entry.xml이 적용이 되는것 같습니다.

    그럼 위 코드부분과 entry.xml과 연결을 해주는 부분이 있을것 같은데 그부분을 찾을 수 가 없습니다.

    공부하는 단계라 초급 수준입니다. ^^;;

    설명 부탁드립니다.

    답글삭제
  3. @공부중 - 2010/11/08 13:56
    접혀있는 CustomAA.java 소스에 보면 ABArrayAdapter 인스턴스를 만드는 부분인 abAdapter = new ABArrayAdapter(this, R.layout.entry, R.id.eName, abList); 에서 entry.xml을 사용하도록 해 주고 있습니다.

    답글삭제
  4. Good Afternoon



    Great share, thanks for your time

    답글삭제
  5. 올려주신 좋은 예제 보고 공부 열심히 하고있습니다

    감사드립니다~

    근대 이미지가 들어있는 zip 파일은; 어디에 있는지 찾을수가 없네요~

    답글삭제
  6. @lunablues - 2010/12/10 10:19
    올려놨는데 이유는 몰라도 잘 안 보이는 경우가 있나보더군요.

    다시 올려놨으니 위쪽에 링크가 있나 확인해 보시고 혹시 계속 안 보이시면 다시 알려주시면 메일로라도 보내 드리겠습니다.

    답글삭제
  7. 정말 정말 감사합니다^^
    정말 큰 도움이 되었습니다,

    답글삭제
  8. 전 자꾸 에러가나서 아예 실행조차 안됩니다...ㅠㅠ

    자꾸 ABEntry.java 파일에서 오류가 난다고 하는 것 같은데...ㅠㅠ

    어떻게 해야하는지 도움좀 받고 싶습니다...ㅠㅠ

    답글삭제
  9. 완전 감사합니다. 어제 하루 종일 시간 허비했는데 덕분에 해결했네요.

    가끔 응원할께요 ㅋ..

    답글삭제
  10. ㅠㅠ 해결못했어요..ㅠㅠ
    ActionBar ( ActionBar.NAVIGATION_MODE_LIST) 에 다가 이미지들어간 IcsSpinner 를 넣는게 불가능한거같네요.ㅠㅠ 혹시... 아세요??ㅠㅠ

    답글삭제
  11. abAdapter = new ABArrayAdapter(this, R.layout.entry, R.id.eName, abList);

    이부분에서 R.id.eName 이부분 만 왜 넣어주는건가요??

    답글삭제