📝 스파르타코딩클럽의 '내일배움캠프' 지급 강좌를 정리한 글 입니다.
findViewById
를 대체한다.
build.gradle
파일의 android { }
부분에 요소 선언android{
...
// Android 3.6 ~ 4.0
viewBinding{
enabled = true
}
// Android 4.0 ~
buildFeatures{
viewBinding = true
}
}
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 = "바인딩이 잘 되었네요~~"
}
}
}
<?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” : 열 내부의 여백을 폭에 맞게 채움
KT
파일에 코드 작성텍스트가 아닌 이미지를 사용할 경우 ImageAdapter
를 BaseAdapter
로부터 파생하여 정의한다.
먼저 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
)
}
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()
}
}
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
레이아웃을 정의한 XML 파일 생성
항목 관련 데이터 클래스 정의
Adapter 클래스 정의
액티비티 XML 파일에 ListView 위젯 정의
Adapter를 생성하고 AdapterView 객체에 연결
<?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>
<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>
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가지 유용한 함수들을 내부적으로 자동으로 생성
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
}
}
<?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>
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는 ViewHolder
를 추가적으로 사용한다.
❓ ViewHolder 란?
화면에 표시될 데이터나 아이템을 저장하는 역할
RecyclerView의 개념을 적용하기 위해서는 스크롤 하여 위로 올라간 View를 재활용 해야 한다.
따라서 재활용할 View를 기억하고 있어야 하고, ViewHolder가 그 역할을 한다.
<?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>
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
에서 항목의 데이터를 삽입한다.
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()
}
}
}
}