안드로이드 리스트뷰 기본 사용법. (Android ListView)

2016. 1. 6. 15:43


1. 안드로이드 ListView

ListView는 사용자가 정의한 데이터 목록을 아이템 단위로 구성하여 화면에 출력하는 ViewGroup의 한 종류입니다. ListView의 아이템들은 세로 방향으로 나열되며, 아이템의 개수가 많아짐에 따라 ListView에 표시될 내용이 ListView의 크기(width,height)를 넘어서게 되면 스크롤 기능을 사용해 ListView의 표시 기준 위치를 이동시킬 수 있습니다.


ListView에 표시되는 아이템은 단순히 Text만 출력하는 구조가 될 수 있고, Image, Button, CheckBox 등 여러 View의 조합으로 구성되는 좀 더 복잡한 형태(Custom)가 될 수도 있습니다.


ListView는 UI(User Interface)를 구성함에 있어 가장 많이 사용되는 컴포넌트 중 하나이며 안드로이드 시스템의 연락처, 환경설정 등이 ListView의 대표적인 예입니다.

대표적인 리스트뷰 예제

!참고 : 안드로이드 ViewViewGroup

안드로이드에서 UI 화면은 View와 ViewGroup을 사용하여 구성됩니다.
View는 사용자에게 어떤 내용을 보여주거나 입력을 받아들이기 위해 화면에 그려지는 기본 객체를 말합니다. TextView, Button, CheckBox 등이 대표적이죠.


반면에 ViewGroup은 이름 그대로 View들이 모인 Group을 말합니다. 즉, 화면에 표시할 하나 이상의 View를 포함하는 컨테이너 역할을 수행하는 것이죠. Layout, ListView 등이 ViewGroup에 속하며 View뿐만 아니라 ViewGroup 또한 포함할 수 있습니다.


ListView는 ViewGroup에 속합니다. 즉, 데이터를 화면에 표시하기 위해서는 View(또는 ViewGroup) 객체가 ListView에 추가되어야 하며, 하나의 View(또는 ViewGroup)는 하나의 아이템 정보를 표시하게 됩니다.

리스트뷰 계층구조

1.1 Adapter

ListView에 데이터를 추가하여 화면에 표시하기 위해서는 Adapter를 사용해야 합니다. 일반적으로 "어댑터"라는 의미는 "장치 또는 기계의 다른 부분을 연결하는 장치로, 적합하지 않은 두 개의 부분을 전기적 또는 기계적으로 접속하기 위한 장치 또는 도구"를 말합니다. ListView에서 사용하는 Adapter 또한 의미적으로 크게 다르지 않습니다. 유사한 문장으로 표현하자면 "사용자가 정의한 데이터를 ListView에 출력하기 위해 사용하는 객체로, 사용자 데이터와 화면 출력 View로 이루어진 두 개의 부분을 이어주는 객체" 정도가 되겠네요. 즉, Adapter가 하는 역할은 사용자 데이터를 입력받아 View를 생성하는 것이며 Adapter에서 생성되는 View는 ListView 내 하나의 아이템 영역에 표시되는 것입니다.

어댑터 역할


2. ListView 기본 사용법.

안드로이드에서 기본적으로 제공하는 ListView는 문자열을 출력하기 위해 사용하는 TextView만을 포함하는 단순한 구조를 가지고 있습니다. 물론 문자열 목록을 출력하는 기본적인 ListView만으로도 충분히 유용한 기능을 가진 앱을 만들 수 있지만 좀 더 편리하고 아름다운 UI 제공을 위해 기본 ListView를 확장하여 일반적인 UI 화면에 사용하는 컴포넌트들을 그대로 사용하여 구성할 수 있습니다. 즉, TextView, ImageView, Button 등을 마음대로 배치하여 ListView의 내용을 구성할 수 있다는 뜻이죠. 


하지만 다양한 컴포넌트들로 구성된 ListView를 만들기 위해서는 조금 번거로운(?) 작업을 해줘야 합니다.

ListView 아이템에 대한 Layout을 구성해야 하고, Layout에 출력될 데이터에 대한 새로운 클래스를 정의해야 하며, 사용자 데이터를 Layout에 정의된 형태의 View로 변환하기 위한 Adapter 클래스도 상속받아 필요한 함수를 채워줘야 합니다. 

이렇게 기본 ListView로부터 확장된 기능을 가지는 ListView를 Custom ListView라고 말하는데, 이에 대한 설명은 추후 자세히 정리하도록 하겠습니다. 여기서는 ListView 사용법에 대한 이해를 위해 우선, 기본 ListView에 대한 작업 절차를 진행하도록 하겠습니다.

2.1 워크플로우

리스트뷰 만들기 절차


2.2 ListView가 표시될 위치 결정.

UI 화면을 구성할 때 가장 먼저 해야 할 일은 어떤 위젯을 사용할 것인지, 어디에 위치시킬 것인지 결정하는 것입니다. 보통 UI 화면을 구성할 때 여러 위젯들을 조합하는 경우가 대부분이어서 구성 작업에 많은 고민이 필요합니다. UI 화면 구성은 사용자의 사용 편의성과도 직결되는 내용이라 쉽게 결정하기 힘든 작업이죠. 이를 위해 보통 설계단계에서 프로토타이핑 도구 등을 사용하여 미리 화면에 대한 구성 설계를 진행하기도 합니다. 


음, 더 자세한 내용은 여기서 다룰 내용이 아니기에, 지금은 가장 기본적인 형태의 ListView 사용법에 대한 단순 예제로만 UI 화면을 구성하겠습니다.


간단하게 MainActivity에 ListView를 생성합니다. MainActivity에 대한 Layout Resource는 "activity_main.xml" 파일(또는 "content_main.xml")에 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.example.madwin.listviewexample1.MainActivity"
    tools:showIn="@layout/activity_main">

    <ListView
        android:id="@+id/listview1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

2.3 ListView 아이템에 표시될 사용자 데이터 정의.

이제 ListView가 표시될 위치를 결정하였으니 ListView에 어떤 내용을 보여줄 것인지 정의해야 합니다. ListView에 문자열만 표시할 것이므로 string 타입의 배열을 선언합니다.

public class MainActivity extends AppCompatActivity {
    static final String[] LIST_MENU = {"LIST1", "LIST2", "LIST3"} ;
    // ... 코드 계속
}
class MainActivity : AppCompatActivity() {
    val LIST_MENU = arrayOf("LIST1", "LIST2", "LIST3")
    // ... 코드 계속
}

2.4 Adapter 생성 후 ListView에 지정.

사용자 데이터가 준비되었으니 해당 데이터를 입력받아 View로 만들어줄 Adapter를 생성해야겠죠. 안드로이드 SDK에서 기본적으로 제공하는 Adapter는 여러 종류가 있고 사용자가 용도에 맞게 선택해서 사용할 수 있는데, 여기서는 string 배열을 입력으로 받으므로 ArrayAdapter를 사용하겠습니다.

[단계-5.2] onCreate()에서 1초 마다 시각을 갱신하는 코드 작성. 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState) ;
        setContentView(R.layout.activity_main) ;

        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, LIST_MENU) ;

        ListView listview = (ListView) findViewById(R.id.listview1) ;
        listview.setAdapter(adapter) ;
        // ... 코드 계속.
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, LIST_MENU)

        val listview = findViewById(R.id.listview1) as ListView
        listview.setAdapter(adapter)

        // ... 코드 계속.
    }

!코드 설명

위의 코드에서 사용한 ArrayAdapter의 생성자는 3개의 파라미터를 가집니다.

 ArrayAdapter(Context context, int resource, T[] objects)
  • context : 안드로이드 시스템에서 제공되는 어플리케이션 전역 환경 정보에 대한 인터페이스. (Activity를 통해 사용 가능)
  • resource : View로 매핑될 Resource Id. "android.R.layout.simple_list_item_1"은 TextView 위젯으로 구성된 ListView 아이템 리소스 Id.
  • objects : 배열로 선언된 사용자 데이터.

여기까지 작업이 완료되면 TextView 위젯들로 구성된 기본적인 ListView가 화면에 표시됩니다.

2.5 ListView 클릭 이벤트 처리

보통 ListView는 단순히 데이터를 리스트(목록) 형태로 보여주기 위해 사용하기도 하지만, 리스트 아이템 자체를 선택 가능(클릭)하도록 만들어 메뉴처럼 사용할 수도 있습니다. ListView가 사용자의 아이템 터치 입력을 받을 수 있도록 만드려면 ListView에 Click 이벤트에 대한 리스너를 설정해주면 됩니다.

    listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        // 코드 계속 ...

        @Override
        public void onItemClick(AdapterView parent, View v, int position, long id) {

            // get TextView's Text.
            String strText = (String) parent.getItemAtPosition(position) ;

            // TODO : use strText
        }
    }) ;
    override fun onCreate(savedInstanceState: Bundle?) {
        // 코드 계속 ...

        listview.onItemClickListener = object : AdapterView.OnItemClickListener {
            override fun onItemClick(parent: AdapterView<*>, v: View, position: Int, id: Long) {

                // get TextView's Text.
                val strText = parent.getItemAtPosition(position) as String

                // TODO : use strText
            }
        }
    }

onItemClick() 함수에서 ListView 아이템인 TextView의 텍스트를 가져오려면 위의 주석에 있는 내용처럼 parent.getItemAtPosition() 함수를 사용하면 됩니다.

3. Fragment에서 ListView 사용하기

지금까지의 ListView 기본 사용법은 Activity에 ListView를 추가할 때 사용하는 방법입니다. 만약 동일한 코드를 사용하여 Fragment에 ListView를 추가하면 어떤 결과가 나올까요? 직접 코드를 작성하여 빌드해보면 아시겠지만 "에러가 발생"합니다.

    error: no suitable constructor found for ArrayAdapter(MainActivityFragment,int,String[])
        ...
    error: cannot find symbol method findViewById(int)

첫 번째 에러가 발생하는 원인은 ArrayAdapter 생성자의 첫번째 파라미터로 전달되는 Context에 대한 접근 문제 때문입니다. Activity는 android.content.Context로부터 상속받은 클래스이므로 Context의 기능을 그대로 이용할 수 있기 때문에 onCreate() 함수에서 ArrayAdapter 생성자의 첫번째 파라미터로 this를 전달할 수 있습니다. 하지만 Fragment는 java.lang.Object로부터 직접 상속받은 클래스입니다. 즉, Context와는 전혀 관계가 없는 상태라서 this를 사용할 수 없는거죠. 

그럼 Fragment에서는 ListView를 사용할 수 없는 걸까요? 아닙니다. 당연히 해결할 수 있는 방법이 존재합니다. 그것도 아주 간단하게 말이죠. 바로 Fragment에서 자신이 속한 Activity의 참조를 얻어온 다음 ArrayAdapter의 생성자로 전달하는 것입니다. 이미 Fragment에는 Activity의 참조를 획득할 수 있는 getActivity()라는 함수가 있습니다.


두 번째 에러는 findViewById() 함수 호출 때문입니다. findViewById() 함수는 Fragment의 멤버 함수가 아닙니다. View의 멤버 함수이죠. 즉, Fragment의 멤버 함수가 아닌 findViewById() 함수를 Fragment에서 호출하려고 하니 에러가 발생하는 것입니다.

해결 방법은 LayoutInflater를 사용하여 Resource Layout을 View로 변환한 다음 해당 View를 사용하여 findViewById()를 호출하는 것입니다.


Fragment에 ListView를 추가하는 코드는 다음처럼 작성하시면 됩니다.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment1, null) ;

        ArrayAdapter Adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, LIST_MENU) ;

        ListView listview = (ListView) view.findViewById(R.id.listview1) ;
        listview.setAdapter(adapter) ;

        return view ;
    }
    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment1, null) ;

        val adapter = ArrayAdapter((getActivity(), android.R.layout.simple_list_item_1, LIST_MENU)

        val listview = view.findViewById(R.id.listview1) as ListView
        listview.setAdapter(adapter)

        return view
    }

4. ListView 기본 사용법 예제 실행 화면

위의 예제 소스를 순서대로 작성한 뒤 실행하면 다음과 같은 화면이 출력됩니다.
안드로이드 기본 생성 코드에 Material Design이 적용되었는지 여부에 따라 화면이 조금 다르게 나올 수 있으며 기기는 넥서스7 2세대를 사용하였습니다.

리스트뷰 예제 실행화면

.END.


ANDROID 프로그래밍/LISTVIEW