๐ SeSAC์ 'JetPack๊ณผ Kotlin์ ํ์ฉํ Android App ๊ฐ๋ฐ' ๊ฐ์ข๋ฅผ ์ ๋ฆฌํ ๊ธ ์ ๋๋ค.
ํญ๋ชฉ์ ๋์ดํ๊ณ ๊ทธ ์ค ํ๋๋ฅผ ์ ์ ์๊ฒ ์ ํ ๋ฐ๊ณ ์ ํ๋ ๋ทฐ
๋ํ์ ์ผ๋ก ListView, AutoCompleteTextView, Spinner ๋ฑ
Adapter์ ์ํด ํญ๋ชฉ์ด ๋ง๋ค์ด์ง๋ ๋ทฐ
โ ์ฆ, AdapterView๋ง์ผ๋ก๋ ํ๋ฉด์ ํ์๋์ง ์๋๋ค.
์ต์์ ๋ทฐ์ ์๋ธํด๋์ค์ด๋ค.
Adapter
๋ก ํญ๋ชฉ์ ๊ตฌ์ฑํด์ผ ํ๋ค.
AdaterView
๋ ํญ๋ชฉ์ ์ถ๋ ฅํ๊ณ Adapter
๋ AdaqterView์ ํญ๋ชฉ์ ๊ตฌ์ฑํ๋ค.
๋ฐ์ดํฐ
๋ ์ด์์ XML ํ์ผ
์ ๋ณดAdapter๋ ์ธํฐํ์ด์ค ๋ช ์ด๋ฉฐ ์ง์ ์ฌ์ฉํ์ง๋ ์๋๋ค. ๋๋ฌธ์ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ฅผ '์ด๋ํฐ'๋ผ๊ณ ๋ถ๋ฅธ๋ค.
BaseAdapter๋ถํฐ๊ฐ ํด๋์ค์ด๊ณ ArrayAdapter, SimpleAdapter, CursorAdapter ์ ๋๊ฐ ํ๋ซํผ์์ ์ ๊ณต๋๊ณ ์๋ค.
ํญ๋ชฉ์
๋ฌธ์์ด
๋ฐ์ดํฐ๋ฅผ ์์๋๋ก ํ๋์ฉ ๋์ดํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ
์์ ๊ฐ์ ํ๋ฉด์ ๊ตฌ์ฑํ๋ค๊ณ ํ์. ํญ๋ชฉ์ด ๋์ด๋์ด ์์ผ๋ ListView๋ฅผ ์ฌ์ฉ๋ ๊ฒ์ด๋ค.
์ผ๋จ ๋ฆฌ์คํธ๋ทฐ์ ๋ค์ด๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํด์ฃผ์.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="location">
<item>๊ฐ์๋</item>
<item>๊ฒฝ๊ธฐ๋</item>
<item>๊ฒฝ์๋จ๋</item>
<item>๊ฒฝ์๋ถ๋</item>
<item>๊ด์ฃผ๊ด์ญ์</item>
</string-array>
</resources>
val datas = resources.getStringArray(R.array.location)
values ํ์์ arrays.xml ํ์ผ์ ์์ฑํ์ฌ <string-array>
๋ก ๋ฐ์ดํฐ๋ฅผ ์ค๋นํด ์ฃผ์๋ค.
๊ทธ๋ฆฌ๊ณ getStringArray
์ ์ฌ์ฉํ์ฌ ๋ฐฐ์ด ํ์
์ผ๋ก ๊ฐ์ ๊ฐ์ ธ์ค์.
์ด์ ์ฐ๋ฆฌ๋ ์ด๋ค Adapter๋ฅผ ์ฌ์ฉํ๋๋ง ๊ณ ๋ฏผํ๋ฉด ๋๋ค.
๋ฌธ์์ด์ด ํ๋์ฉ ๋์ด๋๋ ํญ๋ชฉ ๊ตฌ์ฑ์ด๋ ArrayAdapter
๋ฅผ ์ฌ์ฉํด์ฃผ์.
val adapter = ArrayAdapter(
this,
android.R.layout.simple_list_item_1,
datas
)
listView.adapter = adapter
์ด๋ํฐ์๊ฒ ๋ ์ด์์ XML ํ์ผ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๊ณ , ์ด๋ฅผ ๋ฆฌ์คํธ๋ทฐ.์ด๋ํฐ์๊ฒ ์ฃผ์ ํ๋ค.
์ด ๋, ํญ๋ชฉ์ ๊ตฌ์ฑํ๋ ๋ ์ด์์์ ์ฐ๋ฆฌ๊ฐ ์ง์ ์ปค์คํ ํด๋ ๋์ง๋ง, ํ๋ซํผ์์ ์ ๊ณตํ๋ ๋ ์ด์์์ ์ฌ์ฉํ ์๋ ์๋ค.
ArrayAdapter | ๊ธฐ๋ฅ |
---|---|
simple_list_item_1 | ํญ๋ชฉ์ ๋ฌธ์์ด ๋ฐ์ดํฐ ํ๋ |
simple_list_item_2 | ํญ๋ชฉ์ ๋ฌธ์์ด ๋ฐ์ดํฐ 2๊ฐ ์์๋ ๋์ด |
simple_list_item_multiple_choice | ๋ฌธ์์ด๊ณผ ์ค๋ฅธ์ชฝ ์ฒดํฌ๋ฐ์ค ์ ๊ณต |
simple_list_item_single_choice | ๋ฌธ์์ด๊ณผ ์ค๋ฅธ์ชฝ ๋ผ๋์ค ๋ฒํผ ์ ๊ณต |
ํญ๋ชฉ์ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ ๊ฐ ์์๋๋ก ํ๋์ฉ ๋์ดํ๋ ๊ฒฝ์ฐ ์ด์ฉ
ํ๋ซํผ์์ ์ ๊ณตํ๋ ์ด๋ํฐ
ํ๋์ ํญ๋ชฉ์ ์ฌ๋ฌ ๊ฐ์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ๋ฅผ ArrayList ํ์
์ Map ๊ฐ์ฒด
๋ก ์ฃผ์ด์ผ ํ๋ค.
โ ์ฆ, ์ ์ฒด๊ฐ List ํ์
์ ๊ฐ ํญ๋ชฉ์ด Map ํ์
์ธ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํด์ผ ํ๋ค.
val datas: ArrayList<HashMap<String?, String?>> = ArrayList()
val adapter = SimpleAdapter(
this,
datas,
android.R.layout.simple_list_item_2,
arrayOf("name", "content"),
inArrayOf(android.R.id.text1, android.R.id.text2)
)
listView.adapter = adapter
arrayOf("name", "content")
๋ก Map ๊ฐ์ฒด์์ ํค๊ฐ์ผ๋ก ๋ฝ์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ๋ค.
ํค๊ฐ์ 2๊ฐ ์ฃผ์์ผ๋ ํ๋์ ํญ๋ชฉ์ 2๊ฐ์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๋ค. ํค๊ฐ์ด 3๊ฐ๋ฉด 3๊ฐ์ ๋ฐ์ดํฐ๊ฐ ๋ฝํ๋ค.
inArrayOf(android.R.id.text1, android.R.id.text2)
๋ก ๋ฐ์ดํฐ๊ฐ ํ์๋๋ ๋ทฐ๋ฅผ ์ง์ ํ๋ค.
"name" ์ value๋ text1 ๋ทฐ์, "content"์ value๋ text2 ๋ทฐ์ ์ถ๋ ฅ๋๋ค.
์์ ๊ฐ์ ํ๋ฉด์ ๊ตฌ์ฑํ๋ค๊ณ ํ์.
์ฐ์ ArrayList ํ Map ๊ฐ์ฒด
๋ฅผ ์ค๋นํ์.
val datas: ArrayList<HashMap<String, String>> = ArrayList()
var map: HashMap<String, String> = HashMap()
map["name"] = "LG ํธ์์ค"
map["content"] = "์์ธ, ์ ์ค ์ผ๊ตฌ์ฅ"
datas.add(map)
map = HashMap()
map["name"] = "๋์ฐ ๋ฒ ์ด์ค"
map["content"] = "์์ธ, ์ ์ค ์ผ๊ตฌ์ฅ"
datas.add(map)
map = HashMap()
map["name"] = "KT ์์ฆ"
map["content"] = "์์, KT ์์ฆ ํํฌ"
datas.add(map)
๊ทธ๋ฆฌ๊ณ SimpleAdapter
๋ฅผ ์์ฑํ๋ค.
val adapter = SimpleAdapter(
this,
datas,
android.R.layout.simple_list_item_2,
arrayOf("name", "content"),
intArrayOf(android.R.id.text1, android.R.id.text2)
)
listView.adapter = adapter
adapter.notifyDataSetChanged()
listView.setOnItemClickListener { adapterView, view, i, l ->
//.........
}
์ด๋ฒคํธ ์์ค์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ฆฌ์ค๋๋ก ์ฐ๊ฒฐํ ๊ตฌ์กฐ
์ธ ๋ฒ์งธ ๋งค๊ฐ๋ณ์ i๊ฐ ์ ์ ๊ฐ ์ ํํ ํญ๋ชฉ์ ์ธ๋ฑ์ค ๊ฐ์ ๋ฐ๋๋ค.
์๋๋ ํญ๋ชฉ์ ์ถ๊ฐํ๊ณ ์ ๊ฑฐํ๋ ์์ค ์ฝ๋ ์์ ์ด๋ค.
package com.kotdev99.android.c33
class MainActivity : AppCompatActivity() {
val todos = mutableListOf<String>()
lateinit var adapter: ArrayAdapter<String>
lateinit var listView: ListView
lateinit var editText: EditText
lateinit var button: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listView = findViewById(R.id.main_list)
editText = findViewById(R.id.edit)
button = findViewById(R.id.button)
adapter = ArrayAdapter(
this,
android.R.layout.simple_list_item_1,
todos
)
listView.adapter = adapter
listView.setOnItemClickListener { adapterView, view, i, l ->
AlertDialog.Builder(this)
.setTitle("remove todo")
.setPositiveButton("OK") { dialog, which ->
todos.removeAt(i)
adapter.notifyDataSetChanged()
}
.setNegativeButton("Cancel", null)
.create()
.show()
}
button.setOnClickListener {
todos.add(editText.text.toString())
editText.text.clear()
adapter.notifyDataSetChanged()
}
}
}
๊ฐ๋ฐ์ ์๊ณ ๋ฆฌ์ฆ ๋๋ก ํญ๋ชฉ์ ๋ฐ์ดํฐ๊ฐ ์ค์ ๋์ด์ผ ํ๋ ๊ฒฝ์ฐ
๊ฐ๋ฐ์ ์๊ณ ๋ฆฌ์ฆ ๋๋ก ํญ๋ชฉ๋ณ View์ ์ด๋ฒคํธ๋ฅผ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ
๊ฐ๋ฐ์ ์๊ณ ๋ฆฌ์ฆ ๋๋ก ํญ๋ชฉ๋ณ Layout์ ๋ค๋ฅด๊ฒ ์ ์ฉ์์ผ์ผ ํ๋ ๊ฒฝ์ฐ
BaseAdapter ํน์ ArrayAdapter, SimpleAdapter ๋ฅผ ์์๋ฐ์ ์์ฑํ๋ค.
class DriveAdapter(val cxt: Context, val resId: Int, val datas: MutableList<DriveVO>) :
ArrayAdapter<DriveVO>(cxt, resId) {
//...........
}
override fun getCount(): Int {
return datas.size
}
ํญ๋ชฉ์ ๊ฐ์๋ฅผ ๋ฆฌํด ํ๋ค.
๋ง์ฝ size๋ฅผ 0์ผ๋ก ์ฃผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์กด์ฌํด๋ ํญ๋ชฉ์ด ํ์๋์ง ์๋๋ค.
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
//...........
}
ํ๋์ ํญ๋ชฉ์ ๊ตฌ์ฑํ๊ธฐ ์ํด์ ์ฝ ๋๋ ํจ์
๋๋ฌธ์ ํ๋์ ํญ๋ชฉ์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋์ดํ ๊ฑด์ง, ํญ๋ชฉ์ ์ด๋ฒคํธ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฑด์ง, ํญ๋ชฉ์ ๋ ์ด์์์ ์ด๋ป๊ฒ ์กฐ์ ํ ๊ฑด์ง ์์ฑํด ์ฃผ์ด์ผ ํ๋ค.
์ฝ ๋๋ ํญ๋ชฉ์ ์ธ๋ฑ์ค ๊ฐ์ ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์ position์ผ๋ก ์ ๋ฌ๋๋ค.
๋ ๋ฒ์งธ ๋งค๊ฐ๋ณ์ convertView๋ ์ฌ์ฉํ ๋ทฐ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ค.
์์ ๊ฐ์ ํ๋ฉด์ ๊ตฌ์ฑํด๋ณด์!
์ฐ์ ํญ๋ชฉ ๋์ด๋๋ ListView ๋ฅผ ์ ์ธํ์.
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/custom_listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
res/layout ํ์์ ํญ๋ชฉ์ ๋ ์ด์์์ ์ ์ํ XML ํ์ผ์ ์์ฑํ์.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/custom_item_type_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/custom_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16sp"
android:layout_toRightOf="@id/custom_item_type_image" />
<TextView
android:id="@+id/custom_item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/custom_item_title"
android:layout_alignLeft="@id/custom_item_title"
android:layout_marginLeft="16sp" />
<ImageView
android:id="@+id/custom_item_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/ic_menu" />
</RelativeLayout>
๋ฐ์ดํฐ ํด๋์ค DriveVO๋ฅผ ์์ฑํ๋ค.
package com.kotdev99.android.c34
data class DriveVO(
var title: String,
val date: String,
val type: String
)
๊ฐ๊ฐ์ ํญ๋ชฉ์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๋ทฐ๋ฅผ Holder์ ์ค๋นํ์.
class DriveHolder(root: View) {
var typeImageView: ImageView
var titleView: TextView
var dateView: TextView
var menuImageView: ImageView
init {
typeImageView = root.findViewById(R.id.custom_item_type_image)
titleView = root.findViewById(R.id.custom_item_title)
dateView = root.findViewById(R.id.custom_item_date)
menuImageView = root.findViewById(R.id.custom_item_menu)
}
}
ํญ๋ชฉ์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๋ทฐ๋ฅผ ์ ์ธํ๊ณ , ๊ทธ ๋ทฐ๋ฅผ ํ๋ํ๋ ์ญํ ์
์ด๋ํฐ ์ชฝ์์ ๋ฐ๋ก ๋ทฐ๋ฅผ ์ค๋นํ ํ์ ์์ด ํด๋น ํ๋ ๋ด์ ์๋ ๋ทฐ๋ฅผ ์ด์ฉํ๋ค.
์ผ๋ฐ์ ์ผ๋ก 'ํ๋ ํด๋์ค' ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ด์ ๊ฐ์ฅ ์ค์ํ ์ด๋ํฐ ํด๋์ค๋ฅผ ์์ฑํ๋ค.
package com.kotdev99.android.c34
/* ์์ ๋ฐ์ ArrayAdapter์ ๋์ผํ๊ฒ ๋งค๊ฐ๋ณ์๋ฅผ ์ ์ธ
resId๋ ํญ๋ชฉ์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๋ ์ด์์ XML ํ์ผ, datas๋ ํญ๋ชฉ์ ๊ตฌ์ฑํ๊ธฐ ์ํ ๋ฐ์ดํฐ */
class DriveAdapter(cxt: Context, val resId: Int, val datas: MutableList<DriveVO>) :
ArrayAdapter<DriveVO>(cxt, resId) {
override fun getCount(): Int {
return datas.size
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var convertView = convertView
if (convertView == null) { // ์ฌ์ฉํ ๋ทฐ ๊ฐ์ฒด๊ฐ null ์ด๋ฉด ๋ทฐ ๊ฐ์ฒด ์์ฑ
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
as LayoutInflater
convertView = inflater.inflate(resId, null)
/* LayoutInflater
๊ฐ๋ฐ์ ์ฝ๋ ์์ ์๊ฐ XML ํ์ผ๋ก ์ ์๋์ด ์๋ ๋ ์ด์์ ๋ฆฌ์์ค๋ฅผ
์ด๊ธฐํ์์ผ์ ๋ทฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์์
์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค. */
val holder = DriveHolder(convertView)
convertView!!.tag = holder // ๋ทฐ ๊ฐ์ฒด์ ํ๊ทธ๋ฅผ ๋ถ์ฌ ์ด ํ์๋ ์ฌ์ฉ ๊ฐ๋ฅ
}
val holder = convertView.tag as DriveHolder
val typeImageView = holder.typeImageView
val titleView = holder.titleView
val dateView = holder.dateView
val menuImageView = holder.menuImageView
// ๋ฐ์ดํฐ ํด๋์ค์์ ํ๊บผ๋ฒ์ 3๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ป๋ ์ฝ๋
val (type, title, date) = datas[position]
titleView.text = title
dateView.text = date
if (type == "doc") {
typeImageView.setImageDrawable(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_type_doc, null
)
)
} else if (type == "img") {
typeImageView.setImageDrawable(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_type_image, null
)
)
} else if (type == "file") {
typeImageView.setImageDrawable(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.ic_type_file, null
)
)
}
menuImageView.setOnClickListener {
Toast.makeText(context, "$title menu click", Toast.LENGTH_SHORT)
.show()
}
return convertView
}
}
๋ง์ง๋ง์ผ๋ก ๋ฉ์ธ ์กํฐ๋น์์ AdapterView์ Adapter๋ฅผ ์ฐ๊ฒฐํ๋ค.
package com.kotdev99.android.c34
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// ์๋ณธ ๋ฐ์ดํฐ ์ค๋น
val mutableList = mutableListOf<DriveVO>()
mutableList.add(DriveVO("์๋๋ก์ด๋", "2์ 6์ผ", "doc"))
mutableList.add(DriveVO("db.zip", "2์ 6์ผ", "file"))
mutableList.add(DriveVO("์ด๋ฏธ์ง", "2์ 6์ผ", "img"))
val listView = findViewById<ListView>(R.id.custom_listView)
val adapter = DriveAdapter(this, R.layout.custom_item, mutableList)
listView.adapter = adapter
}
}
๊ฒฐ๊ณผ