[Android] ListView와 RecyclerView Adapter

HEETAE HEO·2022년 4월 6일
1

Android

목록 보기
4/12
post-thumbnail

ListView

ListView는 사용자가 정의한 데이터 목록을 세로 방향으로 나열하여 화면에 표시하는 뷰 그룹의 한 종류입니다. 또한 스크롤 기능을 지원하고 사용자가 배치된 각 항목(item)을 선택하는 것도 가능합니다.

ListView는 화면에 View를 출력할 때 한 화면에 보이는 item의 수 만큼 메모리에 Inflate시킵니다.
(* Inflate(인플레이트)란? : xml에 쓰여있는 view의 정의를 실제 view객체로 만드는 역할 )

예시를 들면 ArrayList에 10개의 item들이 들어있지만 한 화면에 최대로 보이는 List Size가 5라면 ArrayList에서 5까지만 메모리에 Inflate하고 나머지 부분은 대기하고 있습니다. 그리고 있다가 스크롤을 통해 6,7번째 데이터가 보여야한다면 그 순간 메모리에 Inflate하여 View를 생성한다.

ListView 구현법

  1. 데이터 클래스 정의

  2. Layout에 ListView의 추가

  3. item 생성

  4. 어댑터 생성

  5. 어댑터 설정이다

어댑터 생성쪽을 한 번 보겠다.
Adatper에는 담당하는 기능은 다음과 같다

getView(Int, View ViewGroup) : xml 파일의 View와 데이터를 연결하는 핵심 역할을 하는 메소드이다.

getItem(Int) : 해당 위치의 item을 가지고 오는 메소드이다. Int 형식으로 된 position을 파라미터로 갖는다.

getItemId(Int) : item의 id를 반환하는 메서드이다.

getCount() : ListView에 속한 Item의 전체 수를 반환한다.

getView 부분을 코드로 보자면

override fun getView(position : Int, convertView : View?, parent: ViewGroup?) : View?
{
// LayoutInflater는 item을 Adapter에서 사용할 view로 부풀려주는 역할을 한다. 
	val view: View = LayoutInflater.from(context).inflate(R....., null)

	// 위에서 생성된 View를 xml파일의 각 view와 연결한다.
	val ride = view.findViewById<TextView>(R.id.Taxi)'
    ...
    
    return view
}

위에 처럼 정의가 된다. 위에서 ArrayList가 들어가게 되면

override fun getView(position : Int, convertView : View?, parent: ViewGroup?) : View?
{
// LayoutInflater는 item을 Adapter에서 사용할 view로 부풀려주는 역할을 한다. 
	val view: View = LayoutInflater.from(context).inflate(R....., null)

	// 위에서 생성된 View를 xml파일의 각 view와 연결한다.
	val rideTarget = view.findViewById<TextView>(R.id.Taxi)'
    
    val ride = rideList[position]
    rideTarger.text = ride.target
    
    return view
    
    override fun getItem(position: Int) : Any{
    return rideList[position]
}

	override fun getItemId(position : Int) : Long{
    Not Yet
    }
    
    override fun getCount() : Int {
    
    return rideList.size
    }
    

다음과 같이 Adapter가 구성된다. 이 후에 레이아웃에 포함된 childrenView들을 findViewById()를 통해 필요한 뷰에 데이터를 set해준다. 여기서 ListView의 동작과정에서의 문제점이 발견된다. 그 문제는 findViewById()이다.

findViewById()


Layout를 inflate할 때 뷰가 하나 밖에 없다면 해당 View가 맞는지에 대해 한번 만 확인을 하면된다. 하지만 View가 Group을 이루고 있다면 (ViewGroup) 경우에는 비교하여 맞지 않다면 그 아래의 childrenView들을 탐색하기 시작하는데 비교를 할때마다 findViewById()를 호출하는 것이다. 이것이 동작과정의 문제점이다. Layout이 깊어질수 록 viewGroup이 많을 수록 비교해야 하는 수는 많아지게 되고 똑같은 동작을 반복하다보니 해당 기능을 수행하는 비용이 커지게 된다.

장단점

장점이라면

  • 간단하게 리스트를 만드는 부분에 있고
  • List아이템들을 addview로 넣어주지 않고 편하게 Adapter를 통해 데이터를 집어 넣을 수 있다.

단점은
-viewHolder을 강제하지않기 때문에 스크롤 과정에서 발생하는 Memory Inflate가 발생하고 그러다 보니 동작의 부드러움이 덜 할 수 있다.

  • 수직 스크롤에 적합하도록 디자인이 되어있다.

RecyclerView

RecyclerView는 위의 ListView의 메모리 인플레이트 부분의 단점을 보완을 한 뷰 그룹의 한 종류이다. 기본적인 동작의 방식은 ListView와 다름이 없다. 가장 큰 차이는 형식을 인플레이트 할때 그 개수이다. ListView의 경우 눈에 보이는 개수 만큼 인플레이트를 한 후 스크롤을 통해 다음 아이템들을 메모리에 인플레이트한다. 반면 RecyclerView의 경우는 처음 눈에 보이는 개수 만큼만 형식을 메모리 인플레이트한다. 그 후 스크롤을 통해 다음 형식이 올라와야한다면 반대로 스크롤을 통해 가려지는 형식도 있을 것이다. 그 형식을 다시 불러와 데이터만 다음 아이템의 데이터로 바꿔주는 것이다. 이렇게 하게 되면 계속 형식을 메모리에서 불러오는게 아니라 아이템의 데이터만 불러오는 것이기 때문에 Memory leak의 발생을 예방하고 부드러운 스크롤 이모션을 제공한다.

위에 ListView에서는 getView를 이용하여 데이터에 접근을 하는데 RecyclerView는 ViewHolder을 통해 객체를 재사용한다.

일단 Data와 View를 연결해주는 Adapter를 만들어 준다

  1. Adapter
class ModelRecyclerAdapter<M : Model, VM : BaseViewModel>(
    private var modelList: List<Model>,
    private val viewModel: VM,
    private val resourcesProvider: ResoucesProvider,
    private val adapterListener: AdapterListener
) : ListAdapter<Model, ModelViewHolder<M>>(Model.DIFF_CALLBACK) {

    override fun getItemViewType(position: Int): Int = modelList[position].type.ordinal

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModelViewHolder<M>{
        return  ViewHolderMapper.map(parent, CellType.values()[viewType], viewModel, resourcesProvider)
    }

    @Suppress("UNCHECKED_CAST")
    override fun onBindViewHolder(holder: ModelViewHolder<M>, position: Int) {
        holder.bindData(modelList[position] as M)
        holder.bindViews(modelList[position] as M, adapterListener)

    }

ViewBinding을 사용해놔서 다른 예시들과 다를 수 있기 때문에 약간의 설명을 하자면 ModelList에서 데이터를 가지고 오고 그 데이터 getItemViewType로 가지고 온 것이다. 그리고 onCreateViewHolder로 Holder를 만들어 onBindViewHolder로 통해 데이터를 전달해준다.

장단점

장점

  • 강제적으로 ViewHolder의 사용을 요구하기 때문에 재사용성이 높고 가로 세로 상관없이 구현이 가능하다.
  • Adapter 클래스를 직접 구현하기 때문에 View Custome작업에 대한 유연성이 ListView보다 더욱 쉽고 편하다.

단점

  • 개별 터치 이벤트를 관리 하지만 클릭 처리 기능이 내장되어 있지 않다.
profile
Android 개발 잘하고 싶어요!!!

0개의 댓글