[Android] viewBinding과 AdapterView 정리

Minjun Kim·2023년 8월 22일
0

Android

목록 보기
18/47
post-thumbnail

📝 스파르타코딩클럽의 '내일배움캠프' 지급 강좌를 정리한 글 입니다.


❓ viewBinding 이란?

findViewById 를 대체한다.

📚 viewBinding 사용 방법

1. build.gradle 파일의 android { } 부분에 요소 선언

android{
	...
    
    // Android 3.6 ~ 4.0
    viewBinding{
    	enabled = true
    }
    
    // Android 4.0 ~
    buildFeatures{
    	viewBinding = true
    }
}

2. 뷰, 레이아웃에 아이디 부여


3. kt 파일에 코드 작성

class MainActivity : AppCompatActivity() {
	private lateinit var binding: ActivityMainBinding

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

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

		binding.btnBtn.setOnClickListener {
			binding.txtTitle.text = "바인딩이 잘 되었네요~~"
		}
	}
}

📚 이미지 GridView 사용 방법

1. GridView 위젯 정의

<?xml version="1.0" encoding="utf-8"?>
<GridView 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:id="@+id/gridView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="100dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp"
    tools:context=".MainActivity" />
  • android:columnWidth=“100dp” : 그리드 항목 하나의 폭을 100dp로 설정

  • android:numColumns=“auto_fit” : 열의 폭과 화면 폭을 바탕으로 자동 계산

  • android:verticalSpacing : 항목 간의 간격 설정

  • android:stretchMode=“columnWidth” : 열 내부의 여백을 폭에 맞게 채움

2. KT 파일에 코드 작성

텍스트가 아닌 이미지를 사용할 경우 ImageAdapterBaseAdapter 로부터 파생하여 정의한다.

먼저 ImageAdapter 를 정의한다. BaseAdapter 의 4개의 메소드를 재정의 해주자.

class ImageAdapter : BaseAdapter() {

	// 항목의 총 개수를 반환하기 위해 mThumbIds 배열의 크기를 반환
	override fun getCount(): Int {
		return mThumbIds.size
	}

	// 특정 위치의 항목을 반환하기 위해 mThumbIds 배열의 지정된 위치의 항목을 반환
	override fun getItem(p0: Int): Any {
		return mThumbIds[p0]
	}

	// 특정 위치의 항목 아이디를 반환, 배열의 순서를 항목의 아이디로 간주함
	override fun getItemId(p0: Int): Long {
		return p0.toLong()
	}

	override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
		val imageView: ImageView
		if (p1 == null) {
			imageView = ImageView(p2!!.context)
			imageView.layoutParams = AbsListView.LayoutParams(200, 200)
			imageView.scaleType = ImageView.ScaleType.CENTER_CROP
			imageView.setPadding(8, 8, 8, 8)
		} else {
			imageView = p1 as ImageView
		}

		imageView.setImageResource(mThumbIds.get(p0))
		return imageView
	}

	private val mThumbIds = arrayOf<Int>(
		R.drawable.sample_2, R.drawable.sample_3,
		R.drawable.sample_4, R.drawable.sample_5,
		R.drawable.sample_6, R.drawable.sample_7,
		R.drawable.sample_0, R.drawable.sample_1,
		R.drawable.sample_2, R.drawable.sample_3,
		R.drawable.sample_4, R.drawable.sample_5,
		R.drawable.sample_6, R.drawable.sample_7,
		R.drawable.sample_0, R.drawable.sample_1,
		R.drawable.sample_2, R.drawable.sample_3,
		R.drawable.sample_4, R.drawable.sample_5,
		R.drawable.sample_6, R.drawable.sample_7
	)
}
  • getView()는 getView 메소드는 첫번째 파라미터로 주어진 위치의 항목 뷰를 반환하는 것이므로, mThumbIds 배열의 position 위치에 있는 이미지 리소스를 ImageView의 이미지로 설정하고, 이 설정된 ImageView 객체를 그리드 뷰의 항목뷰로 반환한다.
    • getView() 메소드의 두번째 파라미터인 convertView는 이전에 생성된 항목뷰 (여기서는 ImageView)를 의미한다. 만약 해당 위치의 항목뷰가 처음 만들어지는 경우라면, 새로운 이미지뷰 객체를 만들고 크기와 스케일타입, 패팅을 설정한다. 만약 이전에 이미 만들어진 것이라면, 이를 재사용한다.
    • 이미지 뷰의 scaleType은 원본 이미지를 이미지 뷰에 맞게 확대 및 축소시킬 때, 어떻게 처리할 지를 지정하는 것인데, 여기서 CENTER_CROP은 종횡비를 유지하여 스케일링하며 뷰의 크기 이상으로 채우게 됨을 의미한다. 따라서 이미지 일부가 잘릴 수 있다.

GridView 객체에 ImageAdapter 객체를 던져준다.

class MainActivity : AppCompatActivity() {
	private lateinit var binding: ActivityMainBinding

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

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

		// ImageAdapter 객체를 생성하고 GridView 객체에 연결
		binding.gridView.adapter = ImageAdapter()
	}
}

📌 GridView의 클릭 이벤트 처리

class MainActivity : AppCompatActivity() {
	private lateinit var binding: ActivityMainBinding

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

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

		// ImageAdapter 객체를 생성하고 GridView 객체에 연결
		binding.gridView.adapter = ImageAdapter()

		// 항목 클릭 이벤트 처리
		binding.gridView.setOnItemClickListener { parent, view, position, id ->
			Toast.makeText(
				this@MainActivity, "" + (position + 1) + "번째 선택", Toast.LENGTH_SHORT
			).show()
		}
	}
}
  • parent : 클릭 이벤트가 발생한 AdapterView

  • view : 실제 클릭 된 AdapterView 안의 View

  • position : Adapter 내에서 클릭 된 항목/뷰의 위치

  • id : 클릭 된 항목의 id


📚 CustomView 사용 방법

  1. 레이아웃을 정의한 XML 파일 생성

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

  3. Adapter 클래스 정의

  4. 액티비티 XML 파일에 ListView 위젯 정의

  5. Adapter를 생성하고 AdapterView 객체에 연결

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:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:padding="@dimen/icon_padding"
        android:scaleType="centerCrop"
        android:src="@drawable/img" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textItem1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Name"
            android:padding="@dimen/list_item_padding"
            android:textColor="@color/black"
            android:textSize="@dimen/list_item_text_size1" />

        <TextView
            android:id="@+id/textItem2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Age"
            android:padding="@dimen/list_item_padding"
            android:textColor="@color/black"
            android:textSize="@dimen/list_item_text_size2" />
    </LinearLayout>

</LinearLayout>

  • dimens.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>

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

  • MyItem 데이터 클래스 생성
package com.example.test

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

💬 참고

❓ 데이터 클래스(Data class)는 무엇인가?

코틀린의 데이터 클래스(Data Class)는 데이터를 다루는데 최적화된 클래스로
equals(), hashCode(), toString(), copy(), componentN() 5가지 유용한 함수들을 내부적으로 자동으로 생성

3. Adapter 클래스 정의

  • MyAdapter 클래스 생성
package com.example.test

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 위치의 항목에 해당되는 항목뷰를 반환하는 것이 목적임
    // position : 몇 번째 데이터를 사용할 건지
    // convertView : 어떤 레이아웃 xml 파일을 사용할 건지
    // parent : 리스트의 몇 번째 항목인지
	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
	}
}

4. 액티비티 XML 파일에 ListView 위젯 정의

  • activity_main.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="vertical">

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

5. Adapter를 생성하고 AdapterView 객체에 연결

package com.example.test

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.img, "Bella", "1"))
		dataList.add(MyItem(R.drawable.img2, "Charlie", "2"))
		dataList.add(MyItem(R.drawable.img3, "Daisy", "1.5"))
		dataList.add(MyItem(R.drawable.img4, "Duke", "1"))
		dataList.add(MyItem(R.drawable.img5, "Max", "2"))
		dataList.add(MyItem(R.drawable.img6, "Happy", "4"))
		dataList.add(MyItem(R.drawable.img7, "Luna", "3"))
		dataList.add(MyItem(R.drawable.img8, "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()
		}
	}
}
  • MyItem 타입의 데이터를 List로 형태로 dataList 에 저장

  • MyAdapter 에 context와 데이터 원본 전달

  • 변수 name에 어댑터의 position번 째의 aName 값을 대입 -> Toast 출력


📚 RecyclerView 사용 방법

RecyclerView는 ViewHolder 를 추가적으로 사용한다.

❓ ViewHolder 란?

화면에 표시될 데이터나 아이템을 저장하는 역할
RecyclerView의 개념을 적용하기 위해서는 스크롤 하여 위로 올라간 View를 재활용 해야 한다.
따라서 재활용할 View를 기억하고 있어야 하고, ViewHolder가 그 역할을 한다.


1. RecyclerView 를 준비

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
    	android:id="@+id/view_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

2. 항목 레이아웃을 정의

  • item_recycler.xml
    커스텀뷰의 item.xml 파일을 이름만 변경했다.

3. 어댑터 클래스를 정의

  • MyAdapter.kt
package com.example.test

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 = ItemRecycleBinding.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: ItemRecycleBinding) : RecyclerView.ViewHolder(binding.root) {
		val iconImageView = binding.iconItem
		val name = binding.textItem1
		val age = binding.textItem2
	}
}
  • inner class Holder 에서 재활용할 Holder 생성

  • override fun onCreateViewHolder 에서 해당 Holder를 반환

  • override fun onBindViewHolder 에서 항목의 데이터 삽입

❗ CustomView와 차이점

Holder 를 만들어 줘야 한다.
해당 Holder를 onCreateViewHolder 에서 리턴한다.
getView 대신 onBindViewHolder 에서 항목의 데이터를 삽입한다.


4. 어댑터뷰와 어탭터를 연결

package com.example.test

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.img, "Bella", "1"))
		dataList.add(MyItem(R.drawable.img2, "Charlie", "2"))
		dataList.add(MyItem(R.drawable.img3, "Daisy", "1.5"))
		dataList.add(MyItem(R.drawable.img4, "Duke", "1"))
		dataList.add(MyItem(R.drawable.img5, "Max", "2"))
		dataList.add(MyItem(R.drawable.img6, "Happy", "4"))
		dataList.add(MyItem(R.drawable.img7, "Luna", "3"))
		dataList.add(MyItem(R.drawable.img8, "Bob", "2"))

		val adapter = MyAdapter(dataList)
		binding.viewRecycler.adapter = adapter
		binding.viewRecycler.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()
			}
		}
	}
}
profile
응애 나 아기 뉴비

0개의 댓글