본문 바로가기

유저 인터페이스/뷰(View)

많은 양의 데이터 표시는 내게! - ListView

[어플리케이션 정보]

액티비티
  • ListViewExample (ListViewExample.java)

레이아웃
  • main.xml (ListViewExample)

API Level
  • 7 : Android 2.1

어플리케이션 소스 :

안드로이드에서 일련의 데이터를 표시할 때에는 대부분 리스트를 사용합니다. 사실, 안드로이드 뿐만 아니라 모든 곳에서 데이터를 표시할 때에는 리스트를 사용하죠.:)

안드로이드에서 리스트를 사용하는 예

데이터를 리스트 형태로 표시해 주기 위해서는 아래와 같은 세 가지 요소가 필요합니다.
 
  • ListView 
  • 어댑터 
  • 원본 데이터 


앞의 AutoCompleteTextView에서와 마찬가지로, ListView에서도 원본 데이터와 그 데이터를 표시해주는 ListView 사이에 어댑터가 필요합니다. 여기에서도 마찬가지로 어댑터는 원본 데이터를 ListView와 연결시켜줌과 동시에, 리스트에 원본 데이터를 어떻게 표시할 지 정의해줍니다.

탭을 사용할 때와 마찬가지로, ListView를 조금 더 편하게 사용하기 위해 액티비티를 구현할 때 List
 안드로이드에서 ListView를 표시할 때 그냥 Activity를 상속하는 클래스를 사용할 수도 있지만, List에 관련된 여러 메소드들이 추가되어있는 ListActivity를 주로 사용합니다.

ListActivity를 상속하는 액티비티를 작성할 경우 ListView의 어댑터를 설정할 때 ListView를 참조하는 객체를 만들지 않아도 어댑터를 지정할 수 있지만, ListView의 id를 필히 @android:id/list로 설정하셔야 합니다. 그렇지 않으면 아래와 같이 런타임 오류가 발생하게 됩니다.

ListView의 ID를 제데로 설정해주지 않으면 발생하는 런타임 오류



ListActivity를 상속하게 되면 리스트에 표시할 항목이 없을 때 사용자에게 보여줄 화면을 설정하는 것도 비교적 쉽게 할 수 있습니다. 일반적으로 아래와 같은 화면이 되겠죠.

리스트에 표시할 내용이 없을 때 사용자에게 보여주는 문구



이처럼 리스트에 표시할 내용이 없을 때, 빈 화면을 보여주는 대신 사용자에게 항목을 추가하는 방법에 대한 안내를 해주거나, 최소한 "항목이 없습니다" 정도는 표시해 주는 것이 예의(?) 입니다. :)

위와 같이 표시할 항목이 없을 때 대체로 표시할 화면을 설정하려면, 해당 화면 (레이아웃, 뷰)의 id를 @android:id/empty로 설정해주면 됩니다. 정말 매우 간단하죠? 예제에서는 표시할 항목이 없을 경우 아래와 같이 간단한 문구를 표시해주도록 구현하였습니다.


한번 예제의 레이아웃 코드 중 일부를 보도록 하겠습니다.


[main.xml]


<ListView android:layout_height="wrap_content"
                android:id="@android:id/list" 
		android:layout_width="fill_parent"/>
	
	<TextView android:layout_width="wrap_content" 
		android:id="@android:id/empty" 
		android:text="표시할 내용이 없습니다." 
		android:layout_height="wrap_content" 
		android:layout_gravity="center_horizontal"/>
	


ListActivity를 상속하므로 ListView의 id는 @android:id/list 로, 리스트에 표시할 항목이 없을 경우 표시할 TextView의 id는 @android:id/empty로 설정한 모습을 확인할 수 있습니다.

그리고, 이 예제의 윗부분에 EditText와 Button의 레이아웃을 보면 두 위젯이 각각 일정한 비율의 가로 크기를 가지고 있는 것을 볼 수 있습니다. 이는 LinearLayout 내의 요소에 적용되는 속성인 weight 속성을 적용했기 때문입니다.

<EditText android:layout_height="wrap_content" 
	android:id="@+id/inputText" 
	android:layout_width="0dp" 
	android:layout_weight="5" 
	android:hint="추가할 단어를 입력하세요."/>
		
<Button android:layout_height="wrap_content" 
	android:layout_width="0dp" 
	android:layout_weight="1" 
	android:id="@+id/inputButton" 
	android:text="Add"/>

위와 같이 각각의 너비 (layout_width)를 0으로 하고, layout_weight 속성을 지정해 주면, weight 의 크기에 따라 레이아웃 내에서 해당 요소의 너비가 정해지게 됩니다. 위의 경우 전체 너비에서 EditText와 Button이 5:1 비율로 화면을 차지하게 구성된 모습입니다.

그럼 이제 본격적으로 예제 어플리케이션의 코드를 보도록 하겠습니다. 예제 어플리케이션은 위의 EditText에 텍스트를 입력하고, 버튼을 누르면 입력한 항목이 List에 추가되도록 하였습니다.

[ListViewExample.java]

package com.androidhuman.example.ListViewExample;

import java.util.ArrayList;

import android.app.ListActivity;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;

public class ListViewExample extends ListActivity {
    private ArrayList<String> list;
    private ArrayAdapter<String> adapter;
    private EditText inputText;
    private Button inputButton;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        inputText = (EditText)findViewById(R.id.inputText);
        inputButton = (Button)findViewById(R.id.inputButton);
        list = new ArrayList<String>();
        
        inputButton.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				list.add(inputText.getText().toString());
				inputText.setText("");
				adapter.notifyDataSetChanged();
			}
        	
        });
        
        adapter = new ArrayAdapter<String>(this, 
        		android.R.layout.simple_list_item_1, list);
        
        setListAdapter(adapter);
    }
}


주요 부분에 대해 한번 다시 살펴보도록 하겠습니다. 어댑터를 생성하는 과정은 기존에 AutoCompleteTextView에서 봤던 과정과 동일합니다. 표시할 데이터 및 표시될 레이아웃을 설정해주고 있습니다. 

그리고, 해당 어댑터를 ListView의 어댑터로 지정하기 위해 setListAdapter()메소드에 우리가 만든 어댑터를 인자로 넘겨주고 있습니다. 이렇게 해서야 비로소 우리가 표시하고 싶은 데이터와 데이터를 표시해줄 ListView가 연결이 된 셈입니다.

그리고, 버튼의 리스너 부분을 한번 보도록 하죠.


@Override
public void onClick(View v) {
	list.add(inputText.getText().toString());
	inputText.setText("");
	adapter.notifyDataSetChanged();
}

버튼을 클릭하면 EditText에 입력된 내용을 데이터가 담긴 ArrayList인 list에 추가해주고, EditText 필드를 초기화시켜주는 것을 확인할 수 있습니다. 여기에서 무엇보다 주요한 것은 바로 notifyDataSetChanged() 메소드입니다.

우리가 표시할 데이터를 담고 있는 리스트인 list 객체에 데이터를 추가함으로써 데이터에 변화가 생기게 되었고, 이러한 변화는 어댑터가 자동으로 감지하지 못하므로 어댑터에게 이 사실을 알려야 합니다. notifyDataSetChanged() 메소드를 호출하게 되면 어댑터가 다시 리스트로부터 최신의 데이터를 받아오고, 업데이트된 내용이 다시 ListView에 표시될 수 있게 해줍니다.

따라서, 표시할 데이터를 수정하였다면 이 메소드를 호출해야만 ListView에 수정된 내용이 정상적으로 표시되므로 데이터를 변경하였을 때에는 이 메소드를 호출해주는 것을 잊지 말아야 합니다.

예제의 실행 모습은 아래와 같습니다.

ListViewExample 예제의 실행 모습


ListView 선택 이벤트 처리

ListView에 표시된 항목을 클릭했을 때 어떤 동작을 구현하고 싶다면 어떻게 해야할까요? 어렵게 생각하실지도 모르겠지만, 정말 간단합니다. ListActivity 내의 메소드인 onListItemClick() 메소드를 오버라이드한 후 구현하면 됩니다. 아래에서는 위의 예제에 이어 항목을 선택하면 선택한 항목의 문자열을 토스트(Toast)를 통해 표시해주도록 하고 있습니다.

    @Override
    protected void onListItemClick (ListView l, View v, int position, long id){
    	super.onListItemClick(l, v, position, id);
    	Toast.makeText(this, list.get(position), Toast.LENGTH_SHORT).show();
    }


onListItemClick() 메소드는 총 4개의 인자를 받으며, 각각은 아래와 같은 값을 가집니다.

  • ListView : 리스트 항목 선택 이벤트가 발생한 ListView
  • View : 선택한 "항목"의 뷰 인스턴스 (리스트의 한 항목)
  • position : 선택한 항목의 인덱스 값
  • id : 데이터베이스 등을 표시하는 ListView일 경우 선택한 항목의 id값

위의 메소드를 추가한 후 예제를 실행시켜 보면 아래와 같은 결과를 볼 수 있습니다.

두 번째 항목을 클릭했을 때의 화면


어때요? 생각보다 쉽지 않나요? 어댑터라는 개념 때문에 처음에는 생소하고 어려워 보일지 몰라도 알고보면 그리 어렵지 않은녀석(?)이랍니다. :)

그럼, 이제 문제는 ListView가 아니라 갑자기 튀어나온 토스트(Toast)가 되겠군요. 도데체 이놈은 어디에서 갑자기 펑~ 하고 튀어나온걸까요? 뭔가 화면에 뜨니 신기하긴 한데.. 이놈의 정체를 모르겠다구요?

궁금한건 또 못 참지요. 바로 다음 장으로 넘어가서 이놈의 정체를 한번 제데로! 파헤쳐보도록 합시다.