[Android AdapterView] : ListView, RecyclerView
Android/Android UI

[Android AdapterView] : ListView, RecyclerView

728x90
반응형

Adapter View

  • 개발자는 화면의 다양한 View 들을 배치해 화면을 구성하게 된다.
  • 대부분의 뷰들은 배치를 하면 기본적으로 정해진 속성에 따라 모양이 구성된다. 
  • 하지만 일부 View 들은 스스로 결정할 수 없는 부분이 있어 개발자가 반드시 데이터를 설정해야만 구성이 가능하다. 이렇게 개발자가 반드시 설정해야 화면을 구성할 수 있는 View 들을 가르켜 Adapter View라고 부른다.

Adapter Class

  • Adapter View 들은 View 구성하기 위해서 개발자가 다양한 데이터를 설정해줘야 한다.
  • 이러한 데이터를 관리하는 Class 를 Adapter Class라고 부른다.
  • Adapter Class는 사용 목적이나 적용할 View 에 따라 다양하게 제공되고 있으며 원한다면 직접 생성해서 사용할 수도 있다.
  • 주로 다양한 항목을 제공하는 View를 구성할 때 사용한다.

 

주요 AdapterView 종류

ListView (ArrayAdapter)

CustomListView (ArrayAdapter)

CustomListView (SimpleAdapter)

CustomAdapter (BaseAdapter)

 

Spinner (ArrayAdapter)

GrideView (ArrayAdapter)

ViewPager

AutoCompleteTextView (ArrayAdapter)

Single/Multi ChoiceListView (ArrayAdapter)

RecyclerView (RecyclerView.Adapter)

 

 

 


ArrayAdapter

  • ArrayAdapter를 이용해 Adapter 객체를 만들 때 개발자가 작성한 layout 파일을 지정할 수 있다.
  • 만약 기본 ListView 를 사용한다면 안드로이드에서 제공해주는 android.R.layout.simple_list_item_1 폼을 사용한다. 
  • 두 번째 매개 변수로 layout 파일을, 세 번째 layout 파일 내에서 문자열을 설정한 View의 아이디를 설정한다.

ListView를 위한 ArrayAdapter (android.R.layout.simple_list_item_1 안드로이드 제공)

val mAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, adapterViewDatas)

CustomListView를 위한 ArrayAdapter (row.xml 사용자 지정)

val mAdapter = ArrayAdapter(this, R.layout.row, R.id.textView, data)

ListView (ArrayAdapter)

  • 가장 대표적이고 가장 많이 사용하는 Adapter View 이다.
  • 지금은 RecyclerView 사용을 추천하고 있지만 AdapterView 개념을 공부하기에 적당하다.
  • RecyclerView가 내부적으로 관리의 효율성을 가지고 있지만 지금도 ListView를 많이 사용하고 있다.
  • RecyclerView를 사용할 정도가 아니라면 ListView를 사용해도 된다.

ListView 의 주요 프로퍼티

  • adapter : AdapterView를 구성하기 위해 사용하는 adapter를 관리한다.

ListView 의 주요 이벤트

  • ItemClick : 항목을 터치하면 발생된다.
AdapterView.OnItemClickListener{

        // onItemClick (이벤트가 발생한 항목을 가지고 있는 AdapterView, 이벤트가 발생한 항목View, 이벤트가 발생한 항목의 index)
        override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
            when(parent?.id){
                R.id.listView -> {
                    textViewTitle.text = "${adapterViewDatas[position]} 보러가기 !! "
                }
            }
        }
    }

 

전체 소스코드

MainActivity.kt

class MainActivity : AppCompatActivity() {
    val adapterViewDatas = arrayOf("ListView", "CustomListView",
            "ArrayAdapter", "SimpleAdapter", "CustomAdapter",
            "Spinner", "GrideView", "ViewPager",
            "AutoCompleteTextView", "Single/Multi ChoiceListView",
            "RecyclerView")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ArrayAdapter(Context, 항목 하나를 구성하기 위해 사용할 layout 파일)
        val adapter1 = ArrayAdapter(this, android.R.layout.simple_list_item_1, adapterViewDatas)
        listView.adapter = adapter1
        listView.setOnItemClickListener(listener1)

    }

    val listener1 = object : AdapterView.OnItemClickListener{

        // onItemClick (이벤트가 발생한 항목을 가지고 있는 AdapterView, 이벤트가 발생한 항목View, 이벤트가 발생한 항목의 index)
        override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
            when(parent?.id){
                R.id.listView -> {

                    textViewTitle.text = "${adapterViewDatas[position]} 보러가기 !! "
                }
            }
        }
    }
}

activity_main.xml

<?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"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textViewTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Title"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

 

Custom ListView (ArrayAdapter)

  • 기본으로 제공되는 layout 파일 대신해 개발자가 직접 작성한 layout을 이용해 ListView를 구성할 수 있다.
  • ListView 뿐만 아니라 모든 AdapterView는 동일한 방법으로 설정이 가능하다.
  • 여기서는 문자열 하나를 설정하는 방법을 살펴본다.

CustomListViewActivity.kt

class CustomListViewActivity : AppCompatActivity() {
    val titleData = arrayOf("문자열1", "문자열2", "문자열3", "문자열4", "문자열5")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_list_view)

        val adapter1 = ArrayAdapter(this, R.layout.row_custom_list, R.id.rowTextView, titleData)
        customList.adapter = adapter1

        customList.setOnItemClickListener { parent, view, position, id ->
            textView.text = "${titleData[position]}를 터치하였습니다"
        }

    }
}

 

activity_custom_list_view.xml

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="12dp"
        app:layout_constraintBottom_toTopOf="@+id/rowTextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/rowTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

row_custom_list.xml ( 직접 작성한 layout!!! )

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".CustomListViewActivity">
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="CustomListView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <ListView
        android:id="@+id/customList"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

 

 

SimpleAdapter

  • 개발자가 ListView 의 항목을 자유롭게 디자인 했을 때 사용하는 Adapter Class이다.
  • ListView의 항목 내에 배치된 View들을 직접 지정하면서 데이터를 설정할 수 있다.
val mAdapter = SimpleAdapter(this, dataList, R.layout.row, keys, ids)

Custom ListView (SimpleAdapter)

 

CustomListView2Activity.kt

class CustomListView2Activity : AppCompatActivity() {

    val imgRes = arrayOf(R.drawable.food_icon_barbecue, R.drawable.food_icon_beer,
        R.drawable.food_icon_bobatea, R.drawable.food_icon_burger, R.drawable.food_icon_coffee,
        R.drawable.food_icon_cupcake, R.drawable.food_icon_donut, R.drawable.food_icon_noodle,
        R.drawable.food_icon_pizza, R.drawable.food_icon_steak)

    val dataKR = arrayOf("바베큐", "맥주", "버블티", "버거", "커피",
        "컵케이크", "도넛", "국수", "피자", "스테이크")

    val dataEN = arrayOf("barbecue", "beer", "bobatea", "burger", "coffee",
        "cupcake", "donut", "noodle", "pizza", "steak")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_list_view2)

        // simple adapter에 셋팅할 데이터를 가지고 있을 ArrayList
        val dataList = ArrayList<HashMap<String, Any>>()

        for(i in imgRes.indices){
            // 항목 하나를 구성하기 위해 필요한 데이터를 담고 있는 HashMap
            val map = HashMap<String, Any>()
            map["img"] = imgRes[i]
            map["data_kr"] = dataKR[i]
            map["data_en"] = dataEN[i]
            dataList.add(map)
        }

        // HashMap에 데이터를 저장했을 때 사용했던 이름 배열
        val keys = arrayOf("img", "data_kr", "data_en")

        // 데이터를 셋팅할 View의 id 배열
        val ids = intArrayOf(R.id.rowImageView, R.id.rowKRTextView, R.id.rowENTextView)


        val adapter1 = SimpleAdapter(this, dataList, R.layout.row_custom_list2, keys, ids)
        listView.adapter = adapter1


        listView.setOnItemClickListener { parent, view, position, id ->
            textView.text = "${dataKR[position]}항목을 터치하였습니다"
        }

    }
}

 

activity_custom_list_view2.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".CustomListView2Activity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="SimpleAdapter를 사용한 \nCustomListView입니다."
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

 

row_custom_list2.xml

 

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.3"/>

    <ImageView
        android:id="@+id/rowImageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline_vertical"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/rowKRTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/rowENTextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/guideline_vertical"
         />

    <TextView
        android:id="@+id/rowENTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintTop_toBottomOf="@+id/rowKRTextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/guideline_vertical"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

 

BaseAdapter

BaseAdapter

  • BaseAdapter를 상속받아 다음 메서드를 구현하면 된다.
  • getCount : AdapterView를 통해 보여줄 항목의 개수를 반환한다.
  • getView : AdapterView를 통해 보여줄 항목의 View를 반환한다.

 

Custom Adapter View(BaseAdapter)

  • BaseAdapter를 상속받은 클래스를 직접 만들면 AdapterView를 커스터마이징 할 수 있다.
  • AdapterView의 항목을 자유롭게 디자인해서 사용할 때는 SimpleAdapter 만으로도 충분하다.
  • 하지만 AdapterView 자체를 커스터마이징하여 특별한 기능을 부여하고 싶을 때는 Adapter 클래스를 구현하면된다.

CustomAdapterViewActivity.kt

class CustomAdapterViewActivity : AppCompatActivity() {

    val data1 = arrayOf("데이터1", "데이터2", "데이터3", "데이터4", "데이터5")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_adapter_view)

        customAdapter.adapter = adapter1

    }
    /**
    * "BaseAdapter"클래스 상속받아서 adapter1를 구현함
    **/
    val adapter1 = object : BaseAdapter(){
        // 항목의 개수를 반환한다.
        override fun getCount(): Int {
            return data1.size
        }
        override fun getItem(position: Int): Any? {
            return null
        }
        override fun getItemId(position: Int): Long {
            return 0
        }

        // 항목 하나를 구성하기 위해 호출되는 메서드
        override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

            // 재사용 가능한 View를 변수에 담는다.
            var rowView = convertView

            if(rowView == null){ 
                rowView = layoutInflater.inflate(R.layout.row_custom_adapter, null) 
            }
            
            // 원하는 기능 구현
            rowView?.run{
                rowTextView.text = data1[position]
                
                rowButton1.tag = position
                rowButton2.tag = position

                rowButton1.setOnClickListener { textView.text = "첫 번째 버튼을 눌렀습니다 : ${it.tag}" }
                rowButton2.setOnClickListener { textView.text = "두 번째 버튼을 눌렀습니다 : ${it.tag}" }
            }

            return rowView!!
        }
    }
}

 

activity_custom_adapter_view.xml

<?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"
    android:orientation="vertical"
    tools:context=".CustomAdapterViewActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <ListView
        android:id="@+id/customAdapter"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

 

row_custom_adapter.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/rowTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <Button
        android:id="@+id/rowButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="12dp"
        android:text="기능 1" />

    <Button
        android:id="@+id/rowButton2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="12dp"
        android:text="기능 2" />
</LinearLayout>

 

 

 


Adapter 종류 정리!!!!!!!

문자열 등 1개의 배열(Array)로 만들고싶을때는 ArrayAdapter로 구현하고,

항목을 자유롭게(문자열을 포함한 항목 1개 이상 Map) 하고싶을때는 SimpleAdapter로 구현하고, 

데이터 뿐 아니라 기능을 자유롭게 하고싶을때는 BasdAdapter를 상속받아서 CustomAdapter를 직접 구현한다. 

 

원하는 기능 Adapter 종류 xml 파일의 위젯
문자열 1개등의 Array 데이터 ArrayAdapter ListView
여러 항목을 나타내는 Map 데이터 SimpleAdapter ListView
기능 자유롭게 구현하고 싶을 때  BaseAdapter 상속받아 직접 구현 ListView

 


Spinner

  • Spinner는 선택할 항목의 메뉴를 숨기고 있다가 필요할 때 화면에 표시하는 Adapter View다.
  • 사용자에게 항목을 주고 선택 하게 할 수 있는 AdapterView
  • 작은 스마트폰 화면을 효율적으로 사용할 수 있다는 장점을 가지고 있다.

 

Spinner의 주요 속성

  • spinnerMode : 나타나는 메뉴 항목의 타입을 설정한다.

Spinner의 주요 프로퍼티

  • selectedItemPosition : 현재 선택되어 있는 항목이 인덱스(0 부터 시작)를 관리한다.
  • adapter : View 구성을 위한 Adapter를 관리한다.
  • onItemSelectedListener : 항목을 선택했을 때 사용할 리스너를 설정한다.

Spinner의 주요 이벤트

  • ItemSelected : 사용자가 항목을 선택했을 경우. 이 이벤트의 리스너는 프로퍼티로 설정한다.

 

 

GridView

ListView와 거의 동일하며 항목을 그리드 형태로 보여 줄 수 있는 View 이다.

 

GridView의 주요 속성

  • numColumns : 그리드로 구성할 칸의 개수. auto_fit 으로 설정하면 디바이스에 맞게 자동으로 구성된다.

 

GridView의 주요 이벤트

  • ItemClick : 사용자가 항목을 선택했을 경우.

 

ViewPager(PagerAdapte)

  • ViewPager는 좌우로 스와이프 할 때 View가 전환된는 AdapterView 이다.
  • 화면이 바뀌는 것이 아닌 화면의 크기만한 View들을 생성하여 View들을 전환하는 개념이다.
  • 현재 ViewPager를 업그레이드한 ViewPager2를 제공하고 있다.
  • ViewPager, ViewPager2 모두 정상 작동하며 ViewPager는 View를 전환할 때, ViewPager2는 프래그먼트라는 것을 전환할 때 사용한다.

PagerAdapter

ViewPager는 PagerAdapter를 구현하여 사용한다. 

PagerAdapter메서드

  • getCount : ViewPager로 보여줄 View의 전체 개수
  • instantiateItem : ViewPager로 보여줄 View 객체를 생성한다.
  • isViewFromObject : instantiateItem 에서 만든 객체를 사용할 것인지의 여부를 결정한다.
  • destroyItem : ViewPager에서 View가 사라질 때 제거하는 작업을 한다.

 

ViewPager의 주요 이벤트
PageChange : ViewPager에서 View가 전환될 때 반응하는 리스너

 

 

AutoCompleteTextView

EditText에 자동완성 기능을 추가한 View 이다.
사용자가 문자열을 입력하면 설정한 문자열 항목을 통해 자동완성 리스트를 제공한다.

 

 

 

MultiAutoCompleteTextView

 

 

Single/Multi ChoiceListView

SingleChoiceListView

 

 

 

Multi ChoiceListView

 

 

 

RecyclerView

  • Android 5.0 때 추가된 View 이다.
  • ListView와 GridView의 구현이 비슷한 부분이 많이 이를 통합한 View이다.
  • RecyclerView는 ListView와 GridView를 합쳐 구현할 수 있도록 제공되는 View이다.
  • RecyclerView는 Adapter를 직접 구현해 줘야 하며 이를 통해 항목을 자유롭게 구성할 수 있다.
  • RecyclerView는 반드시 항목들을 어떠한 형태로 보여줄 것인가를 설정해야 한다. ()

ViewHolder 만들기

  • ViewHolder 클래스는 항목 하나를 구성하는 View 들의 주소 값을 가지고 있는 클래스이다.
  • 이 클래스는 RecyclerView의 Adapter 클래스 내부에 구현하여 준다.
inner class ViewHolderClass(itemView : View) : RecyclerView.ViewHolder(itemView) {
    val rowImageView = itemView.rowImageView
    val rowTextView = itemView.rowTextView

}

RecyclerView의 Adapter 클래스

  • RecyclerView는 RecyclerView.Adapter 클래스를 상속받은 클래스를 작성하여 Adapter를 구성해야 한다.
  • onCreateVeiwHolder : RecyclerView는 항목 하나를 구성하기 위해 ViewHolder를 사용한다. ViewHolder는 항목 내부를 구성하는 View 객체들을 관리하는 객체로 ViewHolder를 생성하고자 할 때 이 메서드를 호출한다.
  • onBindViewHolder : 항목을 구성하기 위해 호출한다. 여기에서 ViewHolder가 가지고 있는 View들을 구성해준다.
  • getItemCount : RecyclerView의 항목의 개수를 반환한다.

 

RecyclerView의 항목 배치

  • RecyclerView는 ListView와 GridView를 통합한 View이다. 따라서 ListView 처럼 보여줄 것인지 GridView 처럼 보여줄 것인지 결정해야 한다.
  • LinearLayoutManager : ListView 처럼 항목을 보여준다.
  • GridLayoutManager : GridView 처럼 항목을 보여준다.
  • StaggerGridLayoutManager : GridView 처럼 보여주지만 완전한 그리드가 아닌 각 항목의 크기에 따라 유동적으로 조절된다.

recycler_view.layoutManager = LinearLayoutManager(this)

 

 

 

 

 

recycler_view.layoutManager = GridLayoutManager(this, 2)

 

 

recycler_view.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)

 

 

 

recycler_view.layoutManager = StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL)

 

 

 

728x90
반응형

'Android > Android UI' 카테고리의 다른 글

[Appbar] - Toolbar  (0) 2021.12.31
View를 만드는 가장 기본적인 방법, LayoutInflater  (0) 2021.12.20
android custom ui  (0) 2021.08.24
[View]: Widget, layout  (0) 2021.04.17