ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] ListView 사용하기
    프로그래밍/Android 2018. 9. 14. 10:31

    그동안 개인적인 일로 인해 오랜만에 포스팅을 하네요..


    이번에 포스팅에서는 밀고 미루고 있던 ListView의 사용법을 알아보도록 하겠습니다.

    추가로 포스팅을 원하시는 부분이나 기술이 있다면, 댓글에 남겨주세요~


    포스팅 시작하겠습니다.




    ListView?

    ListVIew에 대한 기본적인 내용은 지난 포스팅에서 설명 드렸으니, 안 보신 분은 지난 포스팅을 먼저 보신 후에 오시길 추천드립니다.


    ListView 이해하기

    http://re-build.tistory.com/6



    Data들을 ListView로 표현하기 위해서는 중간에 징검다리 역할을 하는 Adapter가 필요합니다.

    아래 그림과 같이 Adapter는 정의된 데이터를 받아 List에 사용 될 View를 생성하고 관리하는 역할을 합니다. 실세라고 할 수 있죠.!




    그럼 차근차근 하나씩 구현해보도록 하겠습니다.



    레이아웃에 ListView 추가하기

    ListView를 사용하기 위해서 우선 레이아웃에 추가부터 해야겠죠?


    activity_main.xml



    <?xml version="1.0" encoding="utf-8"?>

    <LinearLayout 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"

        tools:context="rebuild.com.listview.MainActivity">


        <ListView

            android:id="@+id/list_mountain"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:divider="@android:color/transparent"

            android:dividerHeight="0dp"

            android:overScrollMode="never"

            android:scrollbars="none" />


    </LinearLayout>


    작성하는 김에 몇 가지 자주 쓰이는 옵션들도 같이 작성해 봤습니다.

    옵션들은 중요한 내용은 아니니, 굳이 따라하지 않으셔도 됩니다. 추후에 필요에 따라 추가하시면 됩니다.


    • divider

    - ListView는 기본적으로 아이템 사이에 구분선이 있습니다. 이 구분선을 divider라고 합니다.

      divider의 색상을 지정하는 옵션으로 사용 됩니다.


    • diverHeight

    - divider의 높이를 지정하는 옵션입니다.


    • overScrollMode

    - overScrollMode 사용 여부에 관한 옵션입니다.

      스크롤 하여 마지막 아이템에 도달 할 시에 화면을 오버하여 보여줄 것 인지에 대한 옵션입니다.

      옵션을 바꿔 실행해보시면 바로 알 수 있으실 겁니다.


    • scrollbars

    - 스크롤바의 스타일을 지정할 수 있습니다. 가로인지, 세로인지 또는 안 보이도록 설정이 가능합니다.



    이어서, Activity에서 데이터를 생성해 놓도록 하겠습니다.


    MainActivity.java



    package rebuild.com.listview;


    import android.content.Context;

    import android.support.v7.app.AppCompatActivity;

    import android.os.Bundle;

    import android.widget.ListView;


    import java.util.ArrayList;


    public class MainActivity extends AppCompatActivity {

        private Context mContext;


        private ArrayList<String> array_mountain;

        private ListView mListView;


        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            this.mContext = getApplicationContext();


            mListView = (ListView) findViewById(R.id.list_mountain);


            // 데이터 생성

            array_mountain = new ArrayList<>();

            array_mountain.add("한라산");

            array_mountain.add("백두산");

            array_mountain.add("월출산");

            array_mountain.add("금강산");

            array_mountain.add("마니산");

            array_mountain.add("설악산");

            array_mountain.add("관악산");

            array_mountain.add("지리산");

            array_mountain.add("대둔산");

            array_mountain.add("도봉산");

        }

    }


    데이터는 간단하게 ArrayList에 String타입으로 생성하였습니다. 필요에 따라 VO타입으로 변환하여 사용하셔도 무방합니다.


    자 이제, 데이터와 ListView의 껍데기가 준비 되었으니 Adapter를 생성하여 붙여주기만 하면 되겠군요.

    그럼 Adapter를 구현 볼까요?



    Adapter 클래스 구현하기

    먼저, Adapter를 통해 ListView에 붙일 View를 먼저 꾸며보겠습니다.


    layout_mountain_item.xml



    <?xml version="1.0" encoding="utf-8"?>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

        android:layout_width="match_parent"

        android:layout_height="match_parent">


        <TextView

            android:id="@+id/txt_name"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_margin="15dp"

            android:textColor="#000000"

            android:textSize="40dp" />

    </LinearLayout>


    예제에서는 간단하게 TextView 하나만 배치 하였지만, 필요에 따라 원하시는 모양으로 꾸미시면 됩니다.!

    그럼 오늘의 실세! Adapter를 작성하겠습니다.


    MountainAdapter.java



    package rebuild.com.listview;


    import android.content.Context;

    import android.view.LayoutInflater;

    import android.view.View;

    import android.view.ViewGroup;

    import android.widget.BaseAdapter;

    import android.widget.TextView;


    import java.util.ArrayList;


    public class MountainAdapter extends BaseAdapter {

        private Context mContext;

        private ArrayList<String> array_mountain;


        private ViewHolder mViewHolder;


        public MountainAdapter(Context mContext, ArrayList<String> array_mountain) {

            this.mContext = mContext;

            this.array_mountain = array_mountain;

        }


        @Override

        public int getCount() {

            return array_mountain.size();

        }


        @Override

        public Object getItem(int position) {

            return array_mountain.get(position);

        }


        @Override

        public long getItemId(int position) {

            return position;

        }


        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            // ViewHoldr 패턴

            if (convertView == null) {

                convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_mountain_item, parent, false);

                mViewHolder = new ViewHolder(convertView);

                convertView.setTag(mViewHolder);

            } else {

                mViewHolder = (ViewHolder) convertView.getTag();

            }


            // View에 Data 세팅

            mViewHolder.txt_name.setText(array_mountain.get(position));


            return convertView;

        }


        public class ViewHolder {

            private TextView txt_name;


            public ViewHolder(View convertView) {

                txt_name = (TextView) convertView.findViewById(R.id.txt_name);

            }

        }

    }


    Adapter는 BaseAdapter를 상속받아 생성하였으며, ViewHolder 패턴을 적용하여 작성하였는데요, 하나하나 살펴보도록 하겠습니다.


    • getCount()

    List의 Item 개수를 반환합니다. return 되는 개수에 따라 생성되는 View 개수가 결정된다고 보시면 됩니다. 특수한 경우가 아니라면, 데이터의 개수만큼 세팅을 하면 되기 때문에, 예제와 같이 ArrayList의 Size로 세팅을 하시면 되겠습니다.


    • getItem(int position)

    List의 position에 해당하는 데이터를 반환합니다. 예제에서는 ArrayList의 각 position에 해당하는 데이터를 반환하도록 작성하였습니다.


    • getItemId(int position)

    List의 position에 해당하는 Item의 ID를 반환합니다. 각각의 Item에 고유한 ID를 부여함으로써, 차후 ID를 이용한 검색, 삭제 등의 컨트롤이 가능하도록 합니다. 예제와 같이 position값을 ID로 반환하도록 작성해주시면 되겠습니다.


    • getView(int position, View convertView, ViewGroup parent)

    List의 position에 해당하는 View를 반환합니다. getView()는 실제로 화면에 보여질 View를 구성하기 위해, 미리 생성해두었던 레이아웃과 연결하고 데이터를 세팅하는 작업을 수행하도록 작성하시면 됩니다.



    추가로, 위의 예제에서는 ViewHolder 패턴을 적용하여 작성 되었는데요. ViewHolder 패턴은 무엇인지, 왜 사용을 하는 지에 대해서 간단히 알아보겠습니다.


    ViewHolder?


    'Holder'는 보관함이라는 뜻을 가지고 있는데요, 그렇다면 ViewHolder는 View를 담아두는 보관함이라는 뜻이 되겠네요.

    View를 보관한다? 어떤 상황에서 사용하면 될까요?


    화면에 한번에 7개의 아이템이 보이는 ListView가 있다고 가정하겠습니다. 최초에 화면에 보이는 7개의 View를 그리기 위해 getView()가 7번 실행되어 각각 View를 생성하여 반환하게 될 것입니다. 이 후에, 스크롤 하여 아래로 화면을 내려 8번째 아이템이 화면에 보이는 순간 다시 getView()가 실행되며 새로운 View를 생성하게 됩니다. 또 9번째, 10번째.. 새로운 아이템이 등장할 때마다 getView()에서는 새로운 View를 생성하게 됩니다.


    일단, 언뜻 생각해도 굉장히 비효율적이라는 생각이 듭니다. 매번 새롭게 만들고 버리고..

    사실 아이템을 구성하는 View가 간단하다면 별 차이가 없을 수도 있습니다. 화면을 내리는 순간, 순식간에 처리 할 테니까요. 하지만 반대로 하나하나의 View가 복잡하다면 어떨까요? ImageView에 TextView에 기타 등등.. 거기에 스크롤도 쭉쭉 내려버리면 ListView가 굉장히 바빠지겠군요!. 저희가 스크롤 중에 가끔 버벅이는 원인에 이러한 이유도 한 몫 한답니다. 실제로 findViewById()를 통해 위젯을 설정하는 작업은 메모리를 상당히 많이 사용하게 된다고 합니다.!


    자, 여기까지 얘기를 듣고 눈치채신 분들도 있으시죠?

    ViewHolder라는 건 결국, ListView의 일을 줄여 성능 향상에 그 목적이 있습니다. 위의 가정에서 최초 7개의 View에 대해서는 새로 만들지만, 이 후로 스크롤 시에 화면에서 사라지게 되는 맨 위의 View를 ViewHolder에 담아두었다가, 새로 보여지는 8번째 View를 만들 때는 새로 만들지 않고 ViewHolder에 담아두었던 View를 가져와 재사용 하게 됩니다. 그 다음 9번째 View를 생성 할 때는 2번째 View를, 10번째 View를 생성 할 때는 3번째 View를 ViewHolder에서 가져와 재사용 하게 되겠죠? ListView도 좀 수월해졌겠죠?


    이전 ListView이해하기에서도 말씀드렸지만, 여기서 재사용이라 함은, View를 재사용 할 뿐이지, 데이터까지 재사용하지 않는다는 점. 꼭 명심하시길 바랍니다.



    ListView와 Adapter 연결하기

    자, 이제 Adapter 작성이 모두 끝났습니다. 이제 MainActivity로 돌아가서, 만들어 두었던 데이터와 ListView사이에 넣어보겠습니다.


    MainActivity.java



    package rebuild.com.listview;


    import android.content.Context;

    import android.support.v7.app.AppCompatActivity;

    import android.os.Bundle;

    import android.widget.ListView;


    import java.util.ArrayList;


    public class MainActivity extends AppCompatActivity {

        private Context mContext;


        private ArrayList<String> array_mountain;

        private ListView mListView;

        private MountainAdapter mMountainAdapter;


        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            this.mContext = getApplicationContext();


            mListView = (ListView) findViewById(R.id.list_mountain);


            // 데이터 생성

            array_mountain = new ArrayList<>();

            array_mountain.add("한라산");

            array_mountain.add("백두산");

            array_mountain.add("월출산");

            array_mountain.add("금강산");

            array_mountain.add("마니산");

            array_mountain.add("설악산");

            array_mountain.add("관악산");

            array_mountain.add("지리산");

            array_mountain.add("대둔산");

            array_mountain.add("도봉산");


            // 아답터 연결

            mMountainAdapter = new MountainAdapter(mContext, array_mountain);

            mListView.setAdapter(mMountainAdapter)

        }

    }



    연결하는 부분은 아주 심플하죠? 생성자를 통해 데이터를 넘겨주고 ListView에는 setAdapter를 통해 연결만 해주면 된답니다.



    실행화면







    이번에는 간단하게 ListView의 사용법에 대해서 알아보았는데요, 기본적인 구조라 파악하시는데 어려움은 없으실 거라고 생각합니다.


    이외에도 Expandable ListView, Header 또는 Footer가 있는 ListView, 아이템 마다 View가 다른 ListVIew등등 여러가지 구조로 이루어진 ListView의 형태도 많답니다. 차후에 말씀드린 부분들도 포스팅하게 될 진 모르겠지만, 기본 구조만 잘 알고 계시면 이외의 구조라 하더라도 좀 더 응용 단계일 뿐이니, 큰 차이는 없다고 말씀드리고 싶습니다.!


    질문은 댓글로 해주시면 감사하겠습니다.

Designed by Tistory.