티스토리 뷰


이 글은 PC 버전 TISTORY에 최적화 되어있습니다.




[목차]


    1.  리스트뷰의 개념 및 사용법

    2.  리스트뷰의 성능 최적화



서론

 안드로이드에서 가장 많이 사용하는 위젯 중 하나가 '리스트뷰'이다. 일반적으로 리스트 형태로 된 컨트롤을 말하는데 이 위젯은 특히 안드로이드나 아이폰처럼 손가락으로 터치하는 방식의 단말기에서 쉽고 직관적으로 여러개 아이템을 보여주고 선택하게 해주기 때문에 많이 사용된다. 안드로이드 앱을 개발하기 위해 필수적으로 알아야하는 리스트뷰를 구현하는 방법을 소개한다. 

 

선택위젯

 안드로이드에서 특별히 여러 개의 아이템을 선택할 수 있는 위젯들을 '선택 위젯'이라고 부른다. 다른 위젯과 다르게 특별히 이름을 붙이는 이유는 사용되는 방식이 기존 위젯과는 사뭇 다르기 때문이다. 선택 위젯은 어댑터(Adapter) 패턴을 사용하므로 직접 위젯의 각 아이템에 이미지나 텍스트를 넣을 수 없을 뿐더러, 어댑터에서 만들어주는 뷰를 이용해 리스트 뷰의 한 아이템을 보여줘야한다. 




사용자가 직접 선택 위젯의 각 아이템에 데이터를 입력하는 것이 아니다. 


어댑터(Adapter)에 데이터를 설정해야하고 어댑터가 또한 설정된 데이터를 관리한다.


 선택 위젯에 보이는 각 아이템은 화면이 디스플레이 되기 전에 어댑터의 getView( ) 메소드가 호출된다. 화면에 보여져야할 아이템 Item 1, Item 2가 있다면 getView( )는 2번 실행된다. 이 메소드는 어댑터에서 가장 중요한 메소드로 리턴하는 뷰가 하나의 아이템이 된다. 예를들어 getView( )에서 리턴 객체가 이미지뷰 객체라면 선택 위젯의 각 아이템은 이미지뷰로, 리턴 객체가 레이아웃이라면 각 아이템은 레이아웃으로 표시되는 것이다.



[참고]

  

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


  • int position : 아이템의 인덱스를 의미하는 것으로 리스트뷰에서 보일 아이템의 위치 정보이다. 0부터 시작하여 아이템 개수만큼 파라미터로 전달된다.
  • View convertView : 전달되는 뷰는 현재 인덱스에 해당하는 뷰 객체를 의미하는데 안드로이드에서는 선택 위젯이 데이터가 많아 스크롤 될 때 뷰를 재활용하는 메커니즘을 가지고 있어 한 번 만들어진 뷰가 화면 상에 그대로 다시 보일 수 있도록 하고 있다. 따라서 이 뷰가 널 값이 아니면 재활용할 수 있는 것이다. 예를 들어 리스트가 100 개의 아이템을 가지고 있는데 화면에서 보여줄 수 있는 아이템의 개수가 4개면 이 4개의 아이템이 먼저 보여진 후 스크롤 될 때마다 이미 만들어진 4개의 뷰들을 그대로 사용하면서 데이터만 바꾸어 보여주는 방식이다.
  • ViewGroup parent : 이 뷰를 포함하고 있는 부모 컨테이너 객체이다.






리스트뷰

이번에는 선택 위젯 중 가장 많이 사용하는 리스트 뷰의 제작 순서를 알아보겠다.


  1.  아이템을 위한 XML 레이아웃 정의하기 
  2.  아이템을 위한 뷰 정의하기 (부분화면이므로 수동 inflation이 필요하다.)
  3.  어댑터 정의하기 (데이터 관리하는 어댑터를 만들고 그 안에 각 아이템에 표시할 뷰를 리턴하는 getView( )를 정의)
  4.  리스트뷰 정의하기 (화면에 보여줄 리스트뷰를 만듭니다.)
리스트 뷰 구현의 전체적인 큰 틀이고 자세한 것은 아래 예제의 코드의 주석에 남겨 놓았다.




예제


[activity_main.xml]
: 리스트 뷰가 들어있는 메인 액티비티




    





1. 아이템을 위한 XML 레이아웃 정의하기


[singer_item.xml]

: getView( )가 리턴하는 객체가 하나의 텍스트 뷰나, 버튼 같은 하나의 뷰가 아니라 레이아웃일 수도 있다. 필자는 사진과 이름, 나이를 넣도록 했다.




    

    

        
        


    



[SingleItem.java]

: 어댑터의 추가할 아이템인 SingerItem 클래스 생성


package com.example.hhj.imagelist; /** * Created by hhj on 16. 6. 30.. */ // 데이터 보관 클래스 public class SingerItem { String name; String age; // Default 생성자 public SingerItem() { } // 두 개가 들어온 생성자 public SingerItem(String name, String age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }





2. 아이템을 위한 뷰 정의하기 (부분화면이므로 수동 inflation이 필요하다.)


[SingleItemView.java]


package com.example.hhj.imagelist; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.LinearLayout; import android.widget.TextView; /** * Created by hhj on 16. 6. 30.. */ // 6. 리니어 레이아웃의 xml을 사용하므로 extends LinearLayout public class SingerItemView extends LinearLayout{ TextView nameTextView; TextView ageTetView; // 7. 부분 화면이므로 수동으로 inflation 해준다 public SingerItemView(Context context) { super(context); // 8. 어떤 객체가 이 클래스를 이용해 new 할 때 생성자 호출될테니 초기화 작업 init(context); } public SingerItemView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } // 8. xml 레이아웃을 inflation 해서 붙여줄 것이다. private void init(Context context){ LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE); // 9. singer_item 을 이 클래스에 붙여준다. 바로 붙여줄 때는 true 설정 inflater.inflate(R.layout.singer_item,this,true); // 10. 인플레이션 후 메모리 올라갔으니 찾는다 nameTextView = (TextView) findViewById(R.id.nameTextView); ageTetView = (TextView) findViewById(R.id.ageTextView); } // new SingerItemView 하면 호출할 수 있는 메소드들을 작성한다 public void setName(String name) { nameTextView.setText(name); } public void setAge(String age) { ageTetView.setText(age); } }




3. 어댑터 & 리스트뷰 정의하기


[MainActivity.java]


package com.example.hhj.imagelist;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    ListView listView;
    SingerAdapter adapter;

    // 읽어온 데이터
    String[] names = {"소녀시대", "걸스데이", "씨스타", "포미닛", "투애니원", "트와이스", "AOA"};
    String[] ages = {"20", "22", "21","25","32","13","43"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 리스트 뷰 생성 : 리스트 뷰는 단순한 껍데기이다.
        listView = (ListView) findViewById(R.id.listView);

        // 3. SingerAdapter 객체를 생성
        adapter = new SingerAdapter();

        // 11. 어댑터에 아이템 추가
        adapter.addItem(new SingerItem(names[0], ages[0]));
        adapter.addItem(new SingerItem(names[1], ages[1]));
        adapter.addItem(new SingerItem(names[2], ages[2]));
        adapter.addItem(new SingerItem(names[3], ages[3]));
        adapter.addItem(new SingerItem(names[4], ages[4]));
        adapter.addItem(new SingerItem(names[5], ages[5]));
        adapter.addItem(new SingerItem(names[6], ages[6]));

        // 4. ListView에 adapter 설정
        listView.setAdapter(adapter);
    }

    // 2. 어댑터 생성 : 데이터 생성,관리 (inner class로 생성)
    class SingerAdapter extends BaseAdapter {

        // 넣을 각각 아이템을 SingerItem으로 지정
        ArrayList items = new ArrayList();

        // 리스트 뷰가 자동으로 호출할 함수들

        @Override
        // 리스트 뷰가 어댑터에게 데이터 몇 개 가지고 있니? 물어보는 함수
        public int getCount() {
            return items.size();
        }

        // SingerItem 에 값을 넣어주는 메소드
        public void addItem(SingerItem item)
        {
            items.add(item);
        }

        @Override
        // 넘겨 받은 index의 값을 리턴
        public Object getItem(int position) {
            return items.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        // 각각의 아이템을 위한 뷰를 생성한다
        // convertView 리스트 뷰 용량 너무 많아지면 메모리 폭발 -> 아이템 재사용
        // 5. 뷰그룹을 만들기 위해 singer_item.xml 생성
        public View getView(int position, View convertView, ViewGroup parent) {

            SingerItemView view = null;

            // 컨버트 뷰가 널이면 다시 만들어주고 아니면 그대로 써라. 메모리 효율적 사용
           if(convertView == null) {
                view = new SingerItemView(getApplicationContext());
            }
            else
            {
                view = (SingerItemView) convertView;
            }

            // 현재 인덱스에서의 SingerItem
            SingerItem curItem = items.get(position);

            view.setName(curItem.getName());
            view.setAge(curItem.getAge());

            // 리턴되는 객체가 각각의 아이템으로 보인다.
            return view;
        }
    }
}


이렇게 간단한 리스트 뷰 만들기에 대한 개념적인 설명과 예제가 마무리 되었다. 

다음 포스팅에서는 convertview를 통한 뷰의 재사용과 리스트 뷰 성능 최적화에 대해 설명하도록 하겠다.




댓글
댓글쓰기 폼