본문 바로가기

Android

어댑터뷰

Adapter View

1) 정의

  • 여러 개의 항목을 다양한 형식으로 나열하고 선택할 수 있는 기능 제공하는 뷰
    • 리스트 뷰 (List View) : 항목을 수직으로 나열
    • 그리드 뷰 (Gird View) : 항목을 격자 형태로 나열
  • 표시할 항목 데이터를 직접 관리 X, 어댑터라는 객체로부터 공급 받음

2) Adapter

  • 데이터 관리, 데이터 원본과 어댑터 뷰 사이의 중계 역할
  • 데이터 원본이 어댑터에 설정 + 어댑터 뷰에는 어댑터 설정

  • getCount() : 현재 어댑터가 관리하는 데이터 항목의 총 개수
  • getView() : 화면에 실제로 표시할 항목 뷰
  • getItem() : 특정 위치의 항목을 선택했을 때, 선택된 항목
  • getItemId() : 특정 위치의 항목을 선택했을 때. 항목 Id

3) Adapter 종류

  • BaseAdapter : 어댑터 클래스의 공통 구현 (기본)
  • ArrayAdapter : 객체 배열이나 리소스에 정의된 배열로부터 데이터 공급 받음
  • CursorAdapter : 데이터베이스로부터 데이터 공급 받음
  • SimpleAdatper : 데이터를 Map(키, 값) 리스트로 관리

1. ListView

: 어댑터 뷰 대표 위젯, 복수 개의 항목 수직으로 표시

1) 리스트 뷰 생성 방법

메인 화면 레이아웃에 ListView 위젯 정의

- res/layout/activity_main.xml

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

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

 

어댑터 객체 설정

- 데이터 원본이 배열인 경우 ArrayAdapter 객체 사용

- ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects)

 

  • context : 현재 context
  • object : 어댑터로 공급될 데이터 (단순 배열)
  • resource

 

 

- MainActivity : ArrayAdapter 객체 생성

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val items = arrayOf<String?>("item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6",  "item7", "item8")

        //어댑터 준비 (배열 객체 이용, simple_list_item_1 리소스 사용
        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)

   }
}

 

ListView 객체에 어댑터 연결

 

- MainActivity : ArrayAdapter 객체 연결

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val items = arrayOf<String?>("item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6",  "item7", "item8")

        //어댑터 준비 (배열 객체 이용, simple_list_item_1 리소스 사용
        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)

        // 어댑터를 ListView 객체에 연결
        binding.listView.adapter = adapter

    }
}

2. GridView

: 2차원 스크롤 가능한 그리드에 항목 표시

1) 그리드 뷰 생성 방법

메인 화면 레이아웃에 GridView 위젯 정의

 

- res/layout/activity_main.xml

 

  • android:columnWidth : 그리드 항목 하나의 폭
  • android:numColumns=“auto_fit” : 열의 폭과 화면 폭을 바탕으로 자동 계
  • android:verticalSpacing : 항목 간의 간격 설정
  • android:stretchMode=“columnWidth”: 열 내부의 여백을 폭에 맞게 채움
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="100dp"
    android:numColumns="auto_fit"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="10dp"
    android:stretchMode="columnWidth"
    android:gravity="center"
    />

 

ArrayAdapter 객체 생성 + 연결

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val items = arrayOf<String?>("item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6",  "item7", "item8")

        //어댑터 준비 (배열 객체 이용, simple_list_item_1 리소스 사용
        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)

        // 어댑터를 GridView 객체에 연결
        binding.gridView.adapter = adapter

    }
}

 

3. Custom View

: 어댑터 뷰의 항목에 이미지나 문자열을 포함하는 임의의 뷰 넣음

1) 커스텀 뷰 생성 방법

커스텀 항목 xml 레이아웃 정의

 

- item.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="wrap_content"
  android:orientation="horizontal"
  >
  <ImageView
      android:id="@+id/iconItem"
      android:layout_width="@dimen/icon_size"
      android:layout_height="@dimen/icon_size"
      android:scaleType="centerCrop"
      android:padding="@dimen/icon_padding"
      android:layout_gravity="center_vertical"
      android:layout_weight="1"
      android:src="@drawable/sample_0"
      />
  <LinearLayout
      android:orientation="vertical"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_weight="2">
      <TextView
          android:id="@+id/textItem1"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="@color/colorAccent"
          android:textSize="@dimen/list_item_text_size1"
          android:padding="@dimen/list_item_padding"
          android:hint="Name"
          />
      <TextView
          android:id="@+id/textItem2"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="@color/colorPrimary"
          android:textSize="@dimen/list_item_text_size2"
          android:padding="@dimen/list_item_padding"
          android:hint="Age"
          />
  </LinearLayout>

</LinearLayout>

 

 

- res/values/dimens.xml : item.xml의 속성 값을 정의

<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="list_item_text_size1">20dp</dimen>
<dimen name="list_item_text_size2">16dp</dimen>
<dimen name="list_item_padding">4dp</dimen>
<dimen name="icon_size">60dp</dimen>
<dimen name="icon_padding">8dp</dimen>
</resources>

 

항목 관련 데이터클래스 정의

data class MyItem(val aIcon:Int, val aName:String, val aAge:String) {}

 

어댑터 클래스 정의

 

- 앞서 정의한 MyItem 타입의 객체들을 ArrayList로 관리하는 BaseAdapter 사용

package com.android.customitemview

class MyAdapter(val mContext: Context, val mItems: MutableList<MyItem>) : BaseAdapter() {

    // MyAdapter 클래스가 관리하는 항목의 총 개수를 반환
    override fun getCount(): Int {
        return mItems.size
    }

    // MyAdapter 클래스가 관리하는 항목의 중에서 position 위치의 항목을 반환
    override fun getItem(position: Int): Any {
        return mItems[position]
    }

    // 항목 id를 항목의 위치로 간주함
    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    // position 위치의 항목에 해당되는 항목뷰를 반환하는 것이 목적임
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

        var convertView = convertView
        if (convertView == null) convertView = LayoutInflater.from(parent?.context).inflate(R.layout.item, parent, false)

        val item : MyItem = mItems[position]

        // convertView 변수로 참조되는 항목 뷰 객체내에 포함된 객체를 id를 통해 얻어옴
        val iconImageView = convertView?.findViewById<View>(R.id.iconItem) as ImageView
        val tv_name = convertView.findViewById<View>(R.id.textItem1) as TextView
        val tv_age = convertView.findViewById<View>(R.id.textItem2) as TextView

        // 어댑터가 관리하는 항목 데이터 중에서 position 위치의 항목의 객체를 헤딩 힝목에 설정
        iconImageView.setImageResource(item.aIcon)
        tv_name.text = item.aName
        tv_age.text = item.aAge

        return convertView
    }
}

 

메인 화면 xml 정의

 

- activity_main.xml

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

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

 

어댑터 객체 생성 + 연결

 

- 데이터 ArrayList 객체 준비

- MyAdapter 객체 생성 + 연결

package com.android.customitemview

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val dataList = mutableListOf<MyItem>()
        dataList.add(MyItem(R.drawable.sample_0, "Bella", "1"))
        dataList.add(MyItem(R.drawable.sample_1, "Charlie", "2"))
        dataList.add(MyItem(R.drawable.sample_2, "Daisy", "1.5"))
        dataList.add(MyItem(R.drawable.sample_3, "Duke", "1"))
        dataList.add(MyItem(R.drawable.sample_4, "Max", "2"))
        dataList.add(MyItem(R.drawable.sample_5, "Happy", "4"))
        dataList.add(MyItem(R.drawable.sample_6, "Luna", "3"))
        dataList.add(MyItem(R.drawable.sample_7, "Bob", "2"))

        // 어댑터 생성 및 연결
        binding.listView.adapter = MyAdapter(this, dataList)

        // 항목 클릭 이벤트 처리
        binding.listView.setOnItemClickListener{ parent, view, position, id ->
            val name: String = (binding.listView.adapter.getItem(position) as MyItem).aName
            Toast.makeText(this," $name 선택!", Toast.LENGTH_SHORT).show()
        }
    }
}

 

3. Recycler View

: 한정적인 화면에 많은 데이터를 넣을 수 있는 View (View 사용해 재활용)

1) ListView와 RecyclerView

  • ListView
    • 스크롤 할 때마다 위에 아이템 삭제, 맨 아래 아이템 생성
    • 아이템이 100개면 100개 삭제, 생성 → 성능 별로 좋지 않음
  • RecyclerVIew
    • 스크롤 할 때, 위에 있던 아이템 아래로 이동해 재사용
    • 아이템이 100개여도 10개 정도의 View만 만들고 10개를 재활용해 사용

2) Adapter, ViewHolder

  • Adapter : 데이터 테이블을 목록 형태로 보여주기 위해 사용
  • ViewHolder : 화면에 표시될 데이터나 아이템 저장하는 역할 (재활용하기 위한 View)

3) Recycler View 생성 방법

커스텀 항목 xml 레이아웃 정의

 

- item.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="wrap_content"
  android:orientation="horizontal"
  >
  <ImageView
      android:id="@+id/iconItem"
      android:layout_width="@dimen/icon_size"
      android:layout_height="@dimen/icon_size"
      android:scaleType="centerCrop"
      android:padding="@dimen/icon_padding"
      android:layout_gravity="center_vertical"
      android:layout_weight="1"
      android:src="@drawable/sample_0"
      />
  <LinearLayout
      android:orientation="vertical"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_weight="2">
      <TextView
          android:id="@+id/textItem1"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="@color/colorAccent"
          android:textSize="@dimen/list_item_text_size1"
          android:padding="@dimen/list_item_padding"
          android:hint="Name"
          />
      <TextView
          android:id="@+id/textItem2"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="@color/colorPrimary"
          android:textSize="@dimen/list_item_text_size2"
          android:padding="@dimen/list_item_padding"
          android:hint="Age"
          />
  </LinearLayout>

</LinearLayout>

 

 

- res/values/dimens.xml : item.xml의 속성 값을 정의

<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="list_item_text_size1">20dp</dimen>
<dimen name="list_item_text_size2">16dp</dimen>
<dimen name="list_item_padding">4dp</dimen>
<dimen name="icon_size">60dp</dimen>
<dimen name="icon_padding">8dp</dimen>
</resources>

 

메인화면 레이아웃 생성

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
       />
</LinearLayout>

 

어댑터 클래스 정의

 

- RecyclerView Adapter 사용

class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {

    //외부에서 함수 정의하기 위해 인터페이스 사용
    interface ItemClick {
        fun onClick(view : View, position : Int)
    }

    //인터페이스를 구현한 객체 참조 : 실제로 아이템이 클릭되었을 때 실행될 콜백 함수 구현
    var itemClick : ItemClick? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.itemView.setOnClickListener {  //클릭이벤트추가부분
            itemClick?.onClick(it, position)
        }
        holder.iconImageView.setImageResource(mItems[position].aIcon)
        holder.name.text = mItems[position].aName
        holder.age.text = mItems[position].aAge
    }

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

    override fun getItemCount(): Int {
        return mItems.size
    }

    inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
        val iconImageView = binding.iconItem
        val name = binding.textItem1
        val age = binding.textItem2
    }
}

 

MainActivity

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        val dataList = mutableListOf<MyItem>()
        dataList.add(MyItem(R.drawable.sample_0, "Bella", "1"))
        dataList.add(MyItem(R.drawable.sample_1, "Charlie", "2"))
        dataList.add(MyItem(R.drawable.sample_2, "Daisy", "1.5"))
        dataList.add(MyItem(R.drawable.sample_3, "Duke", "1"))
        dataList.add(MyItem(R.drawable.sample_4, "Max", "2"))
        dataList.add(MyItem(R.drawable.sample_5, "Happy", "4"))
        dataList.add(MyItem(R.drawable.sample_6, "Luna", "3"))
        dataList.add(MyItem(R.drawable.sample_7, "Bob", "2"))

        binding.recyclerView.adapter = MyAdapter(dataList)

        val adapter = MyAdapter(dataList)
        binding.recyclerView.adapter = adapter
        binding.recyclerView.layoutManager = LinearLayoutManager(this)

        //인터페이스 재정의
        adapter.itemClick = object : MyAdapter.ItemClick {
            override fun onClick(view: View, position: Int) {
                val name: String = dataList[position].aName
                Toast.makeText(this@MainActivity," $name 선택!", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

'Android' 카테고리의 다른 글

ROOM  (1) 2023.09.14
Parcelize  (0) 2023.08.30
Dialog  (0) 2023.08.25
Fragment  (0) 2023.08.24
뷰 바인딩  (0) 2023.08.24