본문 바로가기

안드로이드/[Kotlin]

[Kotlin] ListView 생성. Custom Adapter 사용하기

ListView란 리스트 형태로 된 스크롤 가능한 항목 data들을 나타낼 때 사용하는 뷰 그룹이다. ListView는 adapter를 반드시 연결 해야만 사용이 가능한데, adapter는 기본적으로 kotlin에서 기본적으로 제공되는 게 있고, 직접 만들수도 있다.

  • 전자로는 ArrayAdapter를 사용해 보고,
  • 후자로는 BaseAdapter를 상속 받아서 적접 Custom Adapter를 만들어 보겠다.

ArrayAdapter

package com.example.listviewkt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import com.example.listviewkt.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) { // 액티비티의 실행 시작 지점!
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val item = arrayOf("사과", "배", "딸기", "키위", "석준")
        //context란 한 액티비티의 모든 정보를 담고있다.
        binding.listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, item)
        //listview는 adapter를 생성 해야만 사용이 가능하다
    }
}

별 내용 없다. activity_main.xml 파일에 listview 하나를 만들어 놓고 ArrayAdapter로 연결 시키면 된다. 결과는 아래와 같다.


Custom Adapter 만들기

위에서 언급 한 대로 ListView는 adapter를 연결 해야만 사용이 가능하다. 우리는 우리가 원하는 대로 Custom Adapter를 만들어서 ListView에 연결할 것이다.

 

친구들의 리스트를 보여주는 ListView를 만들어 보자.

전체적인 큰 틀을 살펴보자. 

  1. 메인 레이아웃에 ListView 추가 (activity_main.xml)
  2. 데이터 클래스 정의 (User.kt)
  3. ListView의 item 생성 (list_item_user.xml)
  4. Custom Adapter 생성 (UserAdapter.kt)
  5. ListView에 Custom Adapter 연결 (MainActivity.kt)

이러한 흐름으로 위의 ListView를 설계 해 보도록 하자

 

 

1. 메인 레이아웃에 ListView 추가

MainActivity.kt 에서 작업 할 계획이기 때문에 activity_main.xml에서 ListView를 추가하고 id를 listView로 설정한다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

2. 데이터 클래스 정의

 User.kt 파일에서 데이터 클래스 모델 객체를 선언한다. 이 모델 객체는 우리가 만들고자 하는 ListView에서 각 column에다가 뿌려주고자 하는 data의 틀을 잡는 것이다.

 우리는 User의 구성요소를 profile, name, age, greet 로 구성 할 것이다. 여기서 profile은 이미지인데, res/drawble 폴더에 이미지파일을 넣고 사용할 수 있다. 이미지느 보통 Int형의 resourse를 가지므로 Int형으로 선언 하였다.

//User.kt
package com.example.listviewpractice

class User (val profile: Int, val name: String, val age: Int, val greet: String)

 

 

3. ListView의 item 생성

ListView 내부에서 항목 하나하나를 담당하게 될 item을 만든다. list_item_user.xml 파일을 따로 만든 뒤에 원하는 구성대로 아래와 같이 profile, name, greet, age를 커스텀했다.

 

 

 

4. Custom Adapter 생성

profile, name, age, greet의 요소를 어느 View에 넣을 것인지 연결을 해 주는 역할을 하는 것이 Adapter 이다. 우리가 직접 Custom 할 UserAdapter 는 BaseAdapter() 를 상속받는다. 그러므로 Alt+Enter키를 눌러서 필수 메소드 4개를 Override 할 수 있다.

 

Adapter에서 각 메소드가 하는 기능을 대략 알아보자.

  • getView( Int, View, ViewGroup ) : xml파일의 View와 데이터를 연결하는 핵심적인 메소드이다. 
  • getItem( Int ) : 해당 위치의 item을 반환하는 메소드이다.
  • getItemId( Int ) : 해당 위치의 item id를 반환하는 메소드이다.
  • getCount( ) : ListView에 속한 item의 전체 수를 반환한다.

이외에도 데이터 추가, 삭제나 변경 등의 기능을 하는 메소드도 추가할 수 있지만, 기본적으로 override 하는 메소드는 위의 4개이다.

 

완성된 Adapter코드를 보도록 하자.  뷰 바인딩을 활용해서 코드를 짜 보았다.

package com.example.listviewpractice

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import com.example.listviewpractice.databinding.ListItemUserBinding

class UserAdapter (val context: Context, val userList: ArrayList<User>) : BaseAdapter() {
    override fun getCount(): Int {
        return userList.size
    }

    override fun getItem(position: Int): Any {
        return userList[position]
    }

    override fun getItemId(position: Int): Long {
        return 0
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

        val binding = ListItemUserBinding.inflate(LayoutInflater.from(context))
        //val view: View = LayoutInflater.from(context).inflate(R.layout.list_item_user, null)

        val view: View = binding.root

        val profile = binding.ivProfile
        val name = binding.tvName
        val greet = binding.tvGreet
        val age = binding.tvAge

        val user = userList[position]

        profile.setImageResource(user.profile)
        name.text = user.name
        greet.text = user.greet
        age.text = user.age.toString()

        return view
    }
}

 

 

5. ListView에 Custom Adapter 연결

이제 MainActivity.kt 파일에서 ListView를 우리가 만든 Adapter와 연결하는 일만 남았다. 그 전에 친구(User)들의 정보가 들어있는 userList에 데이터를 넣어주고, Adapter를 미리 초기화( userAdapter 변수 만들기 ) 를 해야 한다.

 

 

package com.example.listviewpractice

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.AdapterView
import android.widget.Toast
import com.example.listviewpractice.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    var userList = arrayListOf<User>(
        User(R.drawable.person, "권석준", 23, "나는쭌갓"),
        User(R.drawable.person, "이명수", 22, "나는돼지"),
        User(R.drawable.person, "김윤찬", 24, "나는찬"),
        User(R.drawable.person, "강승구", 25, "나는좆구"),
        User(R.drawable.person, "김진현", 21, "나는진매"),
        User(R.drawable.person, "권석준", 23, "나는쭌갓"),
        User(R.drawable.person, "이명수", 22, "나는돼지"),
        User(R.drawable.person, "김윤찬", 24, "나는찬"),
        User(R.drawable.person, "강승구", 25, "나는좆구"),
        User(R.drawable.person, "김진현", 21, "나는진매")
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val userAdapter: UserAdapter = UserAdapter(this, userList)  //Adapter 초기화
        binding.listView.adapter = userAdapter  // ListView와 Adapter 연결

		//각 item을 클릭했을 때 toast message 띄우기.
        binding.listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
            val selectItem = parent.getItemAtPosition(position) as User
            Toast.makeText(this, selectItem.name + "입니다", Toast.LENGTH_SHORT).show()
        }
    }
}

지금은 userList에 임의의 데이터를 직접 하드코딩 했지만, 실제로는 데이터베이스를 연동한다던가 데이터를 추가하는 버튼 등을 만들어서 사용한다. 나중에 더 공부를 해서 변형시켜 보도록 하자.

 


ListView 의 단점

 ListView 는 Adapter를 통해 getView 메소드를 호출하여 View를 만든다. 최초로 화면을 로딩한 후에도 스크롤을 통해서 이동할 때 마다 계속해서 View의 create가 발생하므로 리소스를 많이 사용하게 되고, 속도가 느려진다.

 

스크롤 할때 조금 더 빠르고 자연스러운 뷰를 보여주고 싶다면 View Holder를 사용하거나, 이를 사용하도록 설계된 RecyclerView가 있다. 구글의 권장사항이여서 강제적이지는 않지만, 설계하게 되는 ListView가 복잡할수록 성능에 크나큰 영향을 미치게 된다. 이에 대한 내용은 추후에 포스팅하도록 하겠다.!

 

 

 

 

 

 

참고 :  https://blog.yena.io/studynote/2017/12/01/Android-Kotlin-ListView.html