리스트뷰 그리는 방법2 : ListView

2021. 5. 28. 21:14

리스트뷰는 유사하게 반복되는 뷰를 그리기 위한 도구이다.

 

리스트뷰를 그리는 방법에는 세가지가 있다.

1. addView : 실제로 리스트뷰를 그리기 위해 잘 사용되지 않는다.

2. ListView : 예전에 많이 사용되었다.

3. RecycleView : 최근에 가장 많이 사용되고있고 가장 효율이 높다.

 

위 세가지를 구현하는 방법 중 ListView에 대해 알아본다.

 

ListView

1. 리스트로 만들고 싶은 아이템의 리스트를 준비한다.

2. Adapter를 이용한다.

 

addView와 Listview의 차이점은 크게 만드는 방식과 그리는 방식이 있다.

 

addView는 인플레이터로 아이템 하나씩 만들어 뷰를 붙여주는 방식으로 만들고

리스트 개수와 상관없이 한번에 다 그리는 방식이다.

ListView는 Adapter를 통해 만드는 방식이고

보여지는 부분 + 알파만 그리고 필요한 경우 더 그리는 방식이다.

 

XML 파일에는 ListView를 만든다.

<?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=".AddViewActivity">

    <ListView
        android:id="@+id/listview_container"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

 

activity에 Adapter를 만든다.

Adapter를 만들 때는 BaseAdapter 형식으로 만드는데 이는 4개의 함수를 override 해야한다.

  • getView(position, convertView, parent) : 아이템이 보여지는 뷰를 return 한다.
  • getItem(position) : 리스트 중 해당 아이템 정보를 return 한다.
  • getItemId(position) : 리스트 중 해당 아이템의 아이디를 position으로 설정해 return 한다.
  • getCount() : 리스트의 개수를 return 한다.
class ListViewAdapter(
    val carForList : ArrayList<CarForList>,
    val layoutInflater: LayoutInflater
) : BaseAdapter() {
    // 뷰 리턴
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view = layoutInflater.inflate(R.layout.item_view, null)
        val carNameTextview = view.findViewById<TextView>(R.id.car_name)
        val carEngineTextview = view.findViewById<TextView>(R.id.car_engine)

        carNameTextview.text = carForList[position].name
        carEngineTextview.text = carForList[position].engine

        return view

    }
    // 해당 인덱스의 아이템 정보 리턴
    override fun getItem(position: Int): Any {
        return carForList[position]
    }
    // 몇번째 아이디인지 리턴
    override fun getItemId(position: Int): Long {
        return position.toLong()
    }
    // 리스트가 몇개 있는지 리턴, 리스트의 전체 개수
    override fun getCount(): Int {
        return carForList.size
    }
}

 

activity의 onCreate()에서는 뷰바인딩 후

아이템 리스트를 준비하고

위에서 만든 adapter를 만들어 listview에 적용한다.

private lateinit var binding: ActivityListviewBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //뷰바인딩
        binding = ActivityListviewBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        // 아이템 리스트 준비
        val carList = ArrayList<CarForList>()
        for (i in 1 .. 10){
            carList.add(CarForList(""+i+"번째 자동차", ""+i+"순위 엔진"))
        }

        val adapter = ListViewAdapter(carList, LayoutInflater.from(this@ListViewActivity))
        binding.listviewContainer.adapter = adapter
    }

 

✔ listview에 리스너 달기

리스트 하나를 클릭할 때마다 해당 정보가 토스트 형식으로 띄워지도록 리스너를 달아보았다.

onCreate()안에 setOnItemClickListener를 추가한다.

binding.listviewContainer.setOnItemClickListener { parent, view, position, id ->
            val carName = (adapter.getItem(position) as CarForList).name
            val carEngine = (adapter.getItem(position) as CarForList).engine

            Toast.makeText(this@ListViewActivity, carName +" "+ carEngine, Toast.LENGTH_SHORT).show()
        }

 

✔ Holder를 이용하여 뷰 재사용하기

findViewById는 리소스를 많이 사용하는 함수 중 하나이다.

이를 개선시키기 위해 ViewHolder를 사용하여 뷰를 재사용한다.

getView()에서 작업한다.

 

convertView가 비어있다면 holder에 findViewById를 사용하여 view를 만들어주고

찾을 수 있도록 view에 태그를 holder로 달아준다.

한번 실행한 후에는 convertView가 비어있지 않으므로

이후에는 convertView에서 tag를 가져와 holder로 캐스팅한다.

// 뷰 리턴
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view : View
        val holder : ViewHolder

        if(convertView == null) {
            view = layoutInflater.inflate(R.layout.item_view, null)
            holder = ViewHolder()

            holder.carName = view.findViewById(R.id.car_name)
            holder.carEngine = view.findViewById(R.id.car_engine)

            view.tag = holder
        } else {
            holder = convertView.tag as ViewHolder
            view = convertView
        }
        holder.carName?.text = carForList[position].name
        holder.carEngine?.text = carForList[position].engine

        return view
    }

 

 

 

정리된 activity는 아래와 같은 모습이다.

package com.example.prac_android

import android.app.Activity
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import com.example.prac_android.databinding.ActivityListviewBinding
import com.example.prac_android.databinding.ActivityMainBinding

class ListViewActivity : AppCompatActivity() {
    private lateinit var binding: ActivityListviewBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //뷰바인딩
        binding = ActivityListviewBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 아이템 리스트 준비
        val carList = ArrayList<CarForList>()
        for (i in 1 .. 10){
            carList.add(CarForList(""+i+"번째 자동차", ""+i+"순위 엔진"))
        }

        val adapter = ListViewAdapter(carList, LayoutInflater.from(this@ListViewActivity))
        binding.listviewContainer.adapter = adapter

        binding.listviewContainer.setOnItemClickListener { parent, view, position, id ->
            val carName = (adapter.getItem(position) as CarForList).name
            val carEngine = (adapter.getItem(position) as CarForList).engine

            Toast.makeText(this@ListViewActivity, carName +" "+ carEngine, Toast.LENGTH_SHORT).show()
        }
    }
}

class ListViewAdapter(
    val carForList : ArrayList<CarForList>,
    val layoutInflater: LayoutInflater
) : BaseAdapter() {
    // 뷰 리턴
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view : View
        val holder : ViewHolder

        if(convertView == null) {
            view = layoutInflater.inflate(R.layout.item_view, null)
            holder = ViewHolder()

            holder.carName = view.findViewById(R.id.car_name)
            holder.carEngine = view.findViewById(R.id.car_engine)

            view.tag = holder
        } else {
            holder = convertView.tag as ViewHolder
            view = convertView
        }
        holder.carName?.text = carForList[position].name
        holder.carEngine?.text = carForList[position].engine

        return view
    }
    // 해당 인덱스의 아이템 정보 리턴
    override fun getItem(position: Int): Any {
        return carForList[position]
    }
    // 몇번째 아이디인지 리턴
    override fun getItemId(position: Int): Long {
        return position.toLong()
    }
    // 리스트가 몇개 있는지 리턴, 리스트의 전체 개수
    override fun getCount(): Int {
        return carForList.size
    }
}

class ViewHolder{
    var carName : TextView? = null
    var carEngine : TextView? = null
}
728x90

'💻개발 > Android' 카테고리의 다른 글

TabLayout과 Pager  (0) 2021.05.29
리스트뷰 그리는 방법3 : RecyclerView  (0) 2021.05.28
리스트뷰 그리는 방법1 : addView  (0) 2021.05.28
Framework 와 Library  (0) 2021.05.28
Fragment 사용하기  (0) 2021.05.27

BELATED ARTICLES

more