태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

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

2009.06.04 03:28

[어플리케이션 정보]

액티비티
  • 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)가 되겠군요. 도데체 이놈은 어디에서 갑자기 펑~ 하고 튀어나온걸까요? 뭔가 화면에 뜨니 신기하긴 한데.. 이놈의 정체를 모르겠다구요?

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

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

  1. Blog Icon

    비밀댓글입니다

  2. 죄송합니다^^;
    초대장이 남은게 없네용.

  3. Blog Icon
    하마로이드

    안녕하세요 제가 안드로이드 1.1 기반으로 간단한 LBS프로젝트 진행중인데...
    위에서 예제로 보여주신 리스트 그림 중에 한줄의 리스트에 텍스트가 두줄로 들어가 있는 부분
    ----------------------
    새 메세지
    새 메세지 작성
    ----------------------
    이런식으로 쓰고 싶은데 쏘스에 안나와 있네요..
    이건 어떻게 하면 되는거죠?
    그리고 리스트 한줄에 사진이랑 텍스트 같이 나오게 하는 방법 강의좀 빨리 해주실순 없을까요.ㅠ

  4. 오늘 올린 3번 강좌를 보세요~

  5. Blog Icon
    캠블

    커니님 덕분에 안드로이드 기초 쌓는데 정말 많은 도움이 되는거 같아요.

    잘보고 갑니다~ 짱이에요~^^

  6. 감사합니다 :)

  7. Blog Icon
    제코바

    array.xml을 이용하려면 어떻게 하나요???

  8. 잘 보고 가요~

  9. Blog Icon
    Zerosouth

    항상 감사하게 보고 있습니다.
    제가 이 예제를 하다 보니 저는 위에 에디트박스와 버튼이 나오지 않고 화면의 제일 마지막에 <표시할 내용이 없습니다.>라는 텍스트만 나오게 되더군요.
    저는 SDK 2.1, ADT 0.9.5 환경에서 개발 중이었습니다.

  10. 방금 테스트를 다시 해보았는데, 이상없이 잘 나옵니다.
    레이아웃 에디터에서도 잘 나오구요.

    소스 파일을 다운받으신 후 테스트해보시겠어요?

  11. Blog Icon
    Zerosouth

    소스코드를 다운받아 테스트 하니 정상적으로 동작 하네요..

    위에 에디트텍스트 부분과 버튼 부분을 새로운 LinearLayout으로 감싸워야 했었군요..
    감사합니다 ^^

  12. ㅎㅎ 감사합니다 ListView에 대해서 많이 알게 되었습니다... 요즘 안드로이드를 새로 시작하는데, 너무 재밌네요 ㅎ

  13. Blog Icon
    rolen

    질문있는데요
    void onListItemClick (ListView l, View v, int position, long id);
    함수에서요......
    position말고... 그 리스트에서 눌린 버튼의 이름 String값을 아는방법은없나요?
    액티비티를 바꿔가지않으면서 파싱해서 만든 LIST로 눌린 이벤트에한해서
    LOG를 기록시키려고하거든요
    POSITION은 오로직 n번째 만 알려주기떄문에 내용값을 아는방법이 알고싶어서요

  14. Blog Icon
    진인

    궁금한게 있는데요~
    listView 에다가.. spinner 에서 선택한 글을 표시하고 싶은데요 잘안돼네요;;
    spinner에 내용들은 values폴더에 xml 파일로 따로 만들어서 <item>어쩌구저쩌구</item> 이렇케 해서 몇개
    만들었어요~

  15. 어디에서 잘 안되는것인가요??
    Spinner에서 선택할 수 있는 모든 항목이 listview에 표시되고, 선택한 항목이 listview에 하이라이트되는 방식인가요??

  16. Blog Icon
    진인

    커니님 관심 가져 주셔서 감사합니다 ^^
    우선 질문이 좀 어눌한점 죄송하구용 ;;;
    spinner에서 나오는 항목을 고르고 버튼을 누르면 listview 에 다표시하는 방법을 생각하고 있습니다.

  17. Blog Icon
    양정화

    안녕하세요. 글 잘 봤습니다.
    제가 JSONArray를 이용하여 php 파일을 파싱후 뿌려줄때, textview로는 뿌려지더라구요
    근대 listview로 뿌려줘야겟다 싶어 이 소스를 이용하여 보았습니다.

    package kr.or.ketti;

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;

    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;

    import android.app.Activity;
    import android.app.ListActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;

    import android.widget.Toast;



    public class JSONArrayTest extends ListActivity {
    private ArrayList<String> list;
    private ArrayAdapter<String> adapter;
    private TextView mResult;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mResult = (TextView)findViewById(R.id.result);
    list = new ArrayList<String>();

    Button btn = (Button)findViewById(R.id.parse);
    btn.setOnClickListener(new Button.OnClickListener(){

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub


    // 웹에서 Json 형식으로 만들어진 php문서 받아옴
    String Json = DownloadHtml("http://203.241.246.113/convert_jason.php";);
    Log.i("Json : " , Json);
    try{


    String Result = "sss";
    JSONArray ja = new JSONArray(Json);
    for(int j=0; j<ja.length(); j++){
    JSONObject order = ja.getJSONObject(j);
    Result += "번호 : " + order.getInt("uid";) + " " +
    "제목 : " + order.getString("title";) + " " +
    "글 : " + order.getString("content";) + " " +
    "이미지 : " + order.getString("link";) + "\n\n ";
    }
    list.add(mResult.getText().toString());
    mResult.setText(Result);
    adapter.notifyDataSetChanged();

    // mResult.setText(Result);

    } catch(JSONException e) {
    Toast.makeText(v.getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
    }
    }
    });
    adapter = new ArrayAdapter<String>(this,
    android.R.layout.simple_list_item_1, list);

    setListAdapter(adapter);
    }

    String DownloadHtml(String addr){
    StringBuilder jsonHtml = new StringBuilder();
    try{
    // 연결 url 설정
    URL url = new URL(addr);
    // 커넥션 객체 생성
    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
    // 연결되었으면.
    if(conn != null){
    conn.setConnectTimeout(10000);
    conn.setUseCaches(false);
    // 연결되었음 코드가 리턴되면.
    if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "EUC-KR";));

    for(;;){

    // 웹상에 보여지는 텍스트를 라인단위로 읽어 저장.

    String line = br.readLine();

    if(line == null) break;

    // 저장된 텍스트 라인을 jsonHtml에 붙여넣음

    jsonHtml.append(line + "\n";);

    }

    br.close();

    }

    conn.disconnect();

    }

    } catch(Exception ex){

    Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();

    }

    return jsonHtml.toString();

    }
    }


    DDMS를 이용하여 보니
    tag는 global message 내용이Default buffer size used in BufferedReader constructor 이렇게뜨면서
    결과값이 표시되지않는대 어떡해야 할까요 ㅜㅜ

  18. 해당 메시지는 별로 상관없는 메시지 아닌가요? 에러가 뜨는건가요??
    코드는 전체를 다 올려도 문제를 알기느 ㄴ어렵고... 로그를 자세히 올려주시는게 오히려 문제 해결에 도움이 됩니다.

  19. 상세한 설명 감사드립니다.^^

  20. Blog Icon
    taek

    제 블로그에 퍼놓고 자주 참고하고 싶은데 긁어가도 될까요??

  21. 출처 표기만 해주시면 됩니다 :)

  22. Blog Icon
    궁금합니다

    tabactivity 의 액티비티를 listactivity 로 사용하지 못하는 건가요
    일반 activity는 잘 되는데 listactivity 를 쓰면 런타임 에러가 ...