안드로이드

Kotlin ListView 구현하기

start1a 2019. 12. 28. 16:11

ListView

  • 스크롤할 수 있는 view 항목을 나열한 것
  • ListView를 사용하기 위해서는 3가지가 필요함
    1. 리스트 Model 클래스
    2. ListView
    3. 리스트-ListView를 연결할 Adapter 클래스
      • 데이터를 관리하고 목록에 표시

구현

  1. xml에 ListView 생성
  2. 리스트 Model 클래스 생성
  3. 각 리스트 멤버를 표시할 xml 레이아웃 디자인
  4. 각 리스트의 View 객체들을 받을 Adapter Class 생성
  5. Activity에서 List - Adapter - ListView 연결

1. xml에 ListView 생성

ListView가 필요한 위치에 추가

ListView 생성

2. 리스트 Model 클래스 생성

  • 리스트의 멤버가 가질 데이터 클래스
  • 패키지 우클릭 -> New -> Kotlin File/Class
1
2
class People (val name : String, val age : Int)
 
 

 

3. 각 리스트 멤버를 표시할 레이아웃 디자인

 

LinearLayout을 horizontal로 하여 TextView 2개를 나란히 붙였다.

리스트 멤버 디자인

 

4. 각 리스트의 View 객체들을 받을 Adapter Class 생성

Adapter 클래스를 생성하고 생성자로 ListView를 구현할 Activity의 context와 리스트 데이터로 쓸 객체를 선언한다. BaseAdapter 클래스로부터 상속받아 Adapter의 필수 내부 메서드 4개를 구현한다.

  1. getView() : 각 리스트의 멤버 View로 보여 줄 객체를 초기화 후 반환
  2. getItem() : 해당 리스트 position 인덱스의 아이템 반환
  3. getItemId() : position 인덱스 아이템의 Id를 Long형으로 반환
  4. getCount() : 리스트에 저장된 아이템의 개수 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class listAdapter (val context : Context, val list : List<People>) : BaseAdapter() {
 
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view = LayoutInflater.from(context).inflate(R.layout.list_people, null)
        val name = view.findViewById<TextView>(R.id.textName)
        val age = view.findViewById<TextView>(R.id.textAge)
        name.text = list[position].name
        age.text = list[position].age.toString()
        return view
    }
 
    override fun getItem(position: Int): Any {
        return list[position]
    }
 
    override fun getItemId(position: Int): Long {
        return 0
    }
 
    override fun getCount(): Int {
        return list.count()
    }
}
 

getView

  • view 객체를 생성
    • LayoutInflater가 from() 메서드로 리스트가 표시될 Activity로부터 context를 사용
      View로 부풀릴 Layout을 설정
    • 데이터를 표시할 name, age TextView를 연결
    • TextView 객체로부터 텍스트를 입력
    • View 반환

5. Activity에서 List - Adapter - ListView 연결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        val peopleList = listOf (
            People("a", 1),
            People("b", 2),
            People("c", 3),
            People("d", 4),
            People("e", 5)
        )
 
        val peopleAdapter = listAdapter(this, peopleList)
        peopleListView.adapter = peopleAdapter
    }
}
 

 

ViewHolder로 구현하기

  • 기존 ListView의 문제점
    • ListView의 스크롤을 움직여 보이던 View가 사라지고 다시 보일 때 getView()에서 findViewById를 통해 convertView에 들어갈 View를 다시 찾아야 됨
    • 스크롤이 자주 일어나면 View를 찾는 리소스를 많이 사용하게 되어 속도가 떨어짐
    • ViewHolder를 통해 한 번 찾은 View를 계속 사용하여 해결함

ViewHolder 클래스 생성

ViewHolder 클래스 생성자에서 두 멤버를 모두 null로 초기화하였다. null을 쓰지 않고 생성자에 직접 View를 넣어도 된다. (holder = viewHolder(view.findViewById(R.id.textName), view.findViewById(R.id.textAge) ))

 

convertView가 처음 생성될 때는 null이므로 새로운 View 객체와 ViewHolder 객체를 초기화 후 View에 tag로 ViewHolder를 저장한다. tag는 Any라는 최상위 클래스로 어느 타입이든 저장이 가능하다.

나중에 스크롤 발생 이후 다시 호출될 때 convertView는 이미 View가 생성되어 있으므로 View의 tag로 저장했던 ViewHolder 객체를 이용하여 리스트 멤버를 초기화한다.

 

1
private class viewHolder (var name : TextView? = null, var age : TextView? = null)
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
 
        val view : View
        val holder : viewHolder
 
        // convertView가 최초로 생성
        if (convertView == null)
        {
            view = LayoutInflater.from(context).inflate(R.layout.list_people, null)
            holder = viewHolder()
            holder.name = view.findViewById(R.id.textName)
            holder.age = view.findViewById(R.id.textAge)
            view.tag = holder
        }
        // 보이지 않던 View가 다시 보여짐
        else
        {
            view = convertView
            holder = view.tag as viewHolder
        }
        
        val people = list[position]
        holder.name?.text = people.name
        holder.age?.text = people.age.toString()
 
        return view
    }