티스토리 뷰



이 자료들은 팁스소프트에서 제공하는 [ 알짜배기 ] 프로그램을 이용하면 더 편리하게 볼수 있습니다.
* 알짜배기 프로그램 받기 - http://www.tipssoft.com/bulletin/tb.php/QnA/8406
* 관리자의 Tipssoft 이야기를 들어보세요 ( 트위터 ID : tipssoft )
* 안드로이드 강좌 목록 - http://www.tipssoft.com/bulletin/tb.php/old_bbs/501
기본적으로 리스트뷰를 가장 간단하게 사용하는 방법은 String 객체 그룹과 텍스트뷰가 정의된 XML
리소스를 ArrayAdapter 클래스에 설정하여 사용하는 것입니다. ArrayAdapter 클래스는 기본적으로
하나의 텍스트뷰를 참조하도록 되어 있기때문에 String 데이터 그룹과 TextView 를 ArrayAdapter 로
전달하면 별도의 다른 작업없이 해당 텍스트뷰의 출력형태로 문자열들이 리스트뷰에 출력되기
때문입니다.
그러나 단순히 문자열 하나만을 출력하는 간단한 출력형태로는 어플리케이션을 구성하는데에는 한계가
있기때문에 리스트뷰의 한 항목에 여러가지 정보를 출력하거나 아이콘, 버튼같은 여러 컨트롤들을 함께
사용하는 등 다양한 출력형태의 항목을 표현할 수 있어야합니다.
이번 강좌에서는 사용자가 리스트뷰의 출력형태를 직접 구성하여 사용자정의 리스트뷰(Custom
ListView) 를 사용하는 방법에 대하여 알아보도록 하겠습니다.
1. 데이터 클래스 생성하기
리스트뷰의 각 항목마다 여러가지 데이터를 출력하려면 해당 데이터 묶음을 하나의 클래스로
구성하여 객체화시킬 수 있어야 합니다. 예를 들어 아래의 그림과 같은 항목을 출력하는 리스트뷰의
경우에는 아래의 MyData 처럼 데이터를 구성할 수 있을 것입니다.
// 리스트뷰에서 사용할 데이터 클래스
public class MyData
{
private int m_icon = 0; // 아이콘의 리소스 ID
private String m_name = null; // 이름
private String m_data = null; // 전화번호
private String m_ex = null; // 성별
// 생성자
public MyData(int parm_icon, String parm_name, String parm_data, String parm_ex)
{
m_icon = parm_icon;
m_name = parm_name;
m_data = parm_data;
m_ex = parm_ex;
}

// 데이터를 입력받는 인터페이스 메서드들...
public void setIcon(int parm_icon) { m_icon = parm_icon; }
public void setName(String parm_name) { m_name = parm_name; }
public void setData(String parm_data) { m_data = parm_data; }
public void setEx(String parm_ex) { m_ex = parm_ex; }
// 데이터를 반환하는 인터페이스 메서드들...
public int getIcon() { return m_icon; }
public String getName() { return m_name; }
public String getData() { return m_data; }
public String getEx() { return m_ex; }
}
위의 MyData 클래스에서는 멤버 변수들이 private 으로 선언되어 인터페이스 메서드들을 일일히
정의하였지만 C 언어에서 사용하는 구조체처럼 사용하고 싶은 경우에는 public 으로 선언하고,
인터페이스 메서드들을 정의하지 않아도 됩니다.
2. XML 형식으로 리소스 구성하기
리스트뷰의 각 항목이 출력될 리소스 형식을 아래와 같이 정의합니다.
// list_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom"
>
<!-- 좌측의 아이콘이 출력될 이미지뷰 -->
<ImageView
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<!-- 이름이 출력될 텍스트뷰 -->
<TextView
android:id="@+id/name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00FFFFFF"
/>
<!-- 전화번호가 출력될 텍스트뷰 -->
<TextView
android:id="@+id/data_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:textColor="#000000"
android:textSize="20dp"
/>
</LinearLayout>
<!-- 성별이 출력될 텍스트뷰 -->
<TextView
android:id="@+id/ex_tv"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:gravity="center"
/>
</LinearLayout>
3. 어댑터(Adapter) 클래스 재정의하기
ListView 클래스에서는 아래의 그림처럼 리스트의 항목 뷰를 얻을 때 ListAdapter 인터페이스를
사용합니다. ListAdapter 인터페이스는 BaseAdapter 클래스에서 구현되어 있고, BaseAdapter
클래스는 ArrayAdapter 클래스에 상속되는 구조를 가집니다.
그래서 리스트뷰의 출력 형태를 재정의하려면 직접 데이터를 관리하고 항목 뷰를 생성 및 관리
해주는 BaseAdapter 클래스나 ArrayAdapter 클래스를 재정의해야합니다.
BaseAdapter 클래스와 ArrayAdapter 클래스에서는 Adapter 인터페이스의 getView 메서드를
재정의하여 리스트뷰의 각 항목에 출력될 View 객체를 구성하고 반환해주어야 합니다.
3.1 ArrayAdapter 클래스 재정의하기
ArrayAdapter 클래스를 재정의하는 경우에는 간단하게 출력 형태에 변화를 줄 때 간단하고 쉽게
사용할 수 있지만 뷰 항목에 변동성을 요구한다거나 구조에 변화를 요구하는 경우에는 제약이 생길
수도 있습니다.
public class CustomListActivity extends Activity
{
private TipsAdapter m_adapter = null;
private ListView m_list = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// MyData 객체를 저장하는 리스트를 생성한다.
ArrayList<MyData> data_list = new ArrayList<MyData>();
// TipsAdapter 를 생성하여 리스트와 XML 레이아웃을 설정한다.
m_adapter = new TipsAdapter(this, R.layout.list_item, data_list);

// 어댑터를 이용하여 데이터를 추가한다.
m_adapter.add(new MyData(R.drawable.red, "고소한", "010-xxxx-xxxx", "여자"));
m_adapter.add(new MyData(R.drawable.blue, "김수형", "010-yyyy-yyyy", null));
m_adapter.add(new MyData(R.drawable.red, "원빈대", "010-zzzz-zzzz", "남자"));
m_adapter.add(new MyData(R.drawable.yellow, "한효녀", "010-wwww-wwww", "여자"));

// 리스트를 얻어서 어댑터를 설정한다.
m_list = (ListView) findViewById(R.id.custom_list);
m_list.setAdapter(m_adapter);
}

// MyData 객체를 사용하는 ArrayAdapter 를 상속하여 어댑터 클래스를 재정의한다.
private class TipsAdapter extends ArrayAdapter<MyData>
{
private LayoutInflater m_inflager = null;
// MyData 객체를 관리하는 ArrayList
private ArrayList<MyData> m_data_list = null;
// 이 어댑터가 뷰를 생성할 때 적용할 리소스 ID
private int m_recource_id;

// 생성자
public TipsAdapter(Context context, int textViewResourceId, ArrayList<MyData> items)
{
// ArrayAdapter 클래스의 생성자를 호출한다.
super(context, textViewResourceId, items);

// ArrayList 객체와 리소스 ID를 저장해둔다.
m_data_list = items;
m_recource_id = textViewResourceId;

m_inflager = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

// 각 항목에 출력될 뷰를 구성하여 반환하는 메서드
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view;

// convertView 뷰는 어댑터가 현재 가지고 있는 해당 항목의 뷰객체이다.
// null 이 넘어오는 경우에만 새로 생성하고, 그렇지않은 경우에는 그대로 사용한다.
if(convertView == null) {
view = m_inflager.inflate(m_recource_id, null);
} else {
view = convertView;
}

// 요청하는 항목에 해당하는 데이터 객체를 얻는다.
MyData data = m_data_list.get(position);

// 데이터가 존재하는 경우
if(data != null) {
// 항목을 구성하는 컨트롤 위젯들을 얻는다.
ImageView image_view = (ImageView) view.findViewById(R.id.image_view);
TextView name_view = (TextView) view.findViewById(R.id.name_tv);
TextView data_view = (TextView)view.findViewById(R.id.data_tv);
TextView ex_view = (TextView)view.findViewById(R.id.ex_tv);

// 이미지뷰에 이미지를 설정한다.
image_view.setImageResource(data.getIcon());
// 이름을 설정한다.
name_view.setText(data.getName());
// 전화번호 정보를 저장한다.
data_view.setText(data.getData());

// 성별 정보를 가져온다.
String str = data.getEx();

if(str == null) {
// 성별 정보가 없는 경우에는 텍스트뷰를 보이지 않게 한다.
ex_view.setVisibility(View.INVISIBLE);
} else if(str.equals("남자")) {
// 성별이 남자인 경우에는 텍스트뷰의 배경을 하늘색으로 설정한다.
ex_view.setBackgroundColor(0xff0099ff);
// 성별을 설정한다.
ex_view.setText(data.getEx());
} else {
// 그 밖에는 텍스트뷰의 배경을 하늘색으로 설정한다.
ex_view.setBackgroundColor(0xffff5050);
// 성별을 설정한다.
ex_view.setText(data.getEx());
}
}
// 구성한 뷰를 반환한다.
return view;
}
}
}
3.2 BaseAdapter 클래스 재정의하기
BaseAdapter 클래스를 재정의하면 반드시 재정의해야할 메서드들이 많아져서 조금 복잡할 수
있지만 그만큼 구현할 수 있는 표현의 폭이 넓어진다는 장점이 있습니다.
public class CustomListActivity extends Activity
{
private TipsAdapter m_adapter = null;
private ListView m_list = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// MyData 객체를 저장하는 리스트를 생성한다.
ArrayList<MyData> data_list = new ArrayList<MyData>();
// TipsAdapter 를 생성하여 리스트 설정한다.
m_adapter = new TipsAdapter(data_list);

// 어댑터를 이용하여 데이터를 추가한다.
m_adapter.add(new MyData(R.drawable.red, "고소한", "010-xxxx-xxxx", "여자"));
m_adapter.add(new MyData(R.drawable.blue, "김수형", "010-yyyy-yyyy", null));
m_adapter.add(new MyData(R.drawable.red, "원빈대", "010-zzzz-zzzz", "남자"));
m_adapter.add(new MyData(R.drawable.yellow, "한효녀", "010-wwww-wwww", "여자"));

// 리스트를 얻어서 어댑터를 설정한다.
m_list = (ListView) findViewById(R.id.custom_list);
m_list.setAdapter(m_adapter);
}

// BaseAdapter 를 상속하여 어댑터 클래스를 재정의한다.
private class TipsAdapter extends BaseAdapter
{
private LayoutInflater m_inflater = null;
// MyData 객체를 관리하는 ArrayList
private ArrayList<MyData> m_data_list = null;

public TipsAdapter(ArrayList<MyData> items)
{
// ArrayList 객체를 저장한다.
m_data_list = items;

// 인플레이터를 얻는다.
m_inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

// ArrayList 에 MyData 객체를 추가하는 메서드
public void add(MyData parm_data)
{
m_data_list.add(parm_data);
// 데이터가 변화됨을 알려준다.
notifyDataSetChanged();
}

// 어댑터에서 참조하는 ArrayList 가 가진 데이터의 개수를 반환하는 함수
@Override
public int getCount()
{
return m_data_list.size();
}

// 인자로 넘어온 값에 해당하는 데이터를 반환하는 함수
@Override
public MyData getItem(int position)
{
return m_data_list.get(position);
}

// 인자로 넘어온 값에 해당하는 행 ID 를 반환하는 함수
@Override
public long getItemId(int position)
{
return position;
}

// 각 항목에 출력될 뷰를 구성하여 반환하는 메서드
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view;
// convertView 뷰는 어댑터가 현재 가지고 있는 해당 항목의 뷰객체이다.
// null 이 넘어오는 경우에만 새로 생성하고, 그렇지않은 경우에는 그대로 사용한다.
if(convertView == null) {
view = m_inflater.inflate(R.layout.list_item, null);
} else {
view = convertView;
}

// 요청하는 항목에 해당하는 데이터 객체를 얻는다.
MyData data = m_data_list.get(position);

// 데이터가 존재하는 경우
if(data != null) {
// 항목을 구성하는 컨트롤 위젯들을 얻는다.
ImageView image_view = (ImageView) view.findViewById(R.id.image_view);
TextView name_view = (TextView) view.findViewById(R.id.name_tv);
TextView data_view = (TextView)view.findViewById(R.id.data_tv);
TextView ex_view = (TextView)view.findViewById(R.id.ex_tv);

// 이미지뷰에 이미지를 설정한다.
image_view.setImageResource(data.getIcon());
// 이름을 설정한다.
name_view.setText(data.getName());
// 전화번호 정보를 저장한다.
data_view.setText(data.getData());

// 성별 정보를 가져온다.
String str = data.getEx();

if(str == null) {
// 성별 정보가 없는 경우에는 텍스트뷰를 보이지 않게 한다.
ex_view.setVisibility(View.INVISIBLE);
} else if(str.equals("남자")) {
// 성별이 남자인 경우에는 텍스트뷰의 배경을 하늘색으로 설정한다.
ex_view.setBackgroundColor(0xff0099ff);
// 성별을 설정한다.
ex_view.setText(data.getEx());
} else {
// 그 밖에는 텍스트뷰의 배경을 하늘색으로 설정한다.
ex_view.setBackgroundColor(0xffff5050);
// 성별을 설정한다.
ex_view.setText(data.getEx());
}
}
// 구성한 뷰를 반환한다.
return view;
}
}
}
4. 실행 화면
이 강좌에서 설명한 두가지 방법 모두 아래의 그림과 같은 동일한 출력 형태를 가집니다.


댓글