๋ฐํ์ : ์ก์ง์ฐ
๋ฐํ์ผ์ : 2018-10-24()
๋ฐํ์ฃผ์ : Kotlin Anko
- ์ปค๋์ ์ฝํ๋ฆฐ(Anko ์ฑํฐ) -๊นํํธ ์ง์-
- [Anko Coroutines] https://github.com/Kotlin/anko/wiki/Anko-Coroutines
- [Anko SQLite] https://github.com/Kotlin/anko/wiki/Anko-SQLite
- Kotlin์ผ๋ก ์์ฑ๋ DSL ( Domain-specific-Language) ์ ๋๋ค.
- ๊ธฐ์กด์ Android์์๋ View๋ฅผ ๋์์ธ ํ ๋ XML Layout์ ํตํด์ ํํํ์์ต๋๋ค.
- Anko์ ๊ณต์์ ์ผ๋ก ๋ฐํ๋ ์๋ฃ์๋ Android ๊ฐ๋ฐ์ ์ข๋ ์ฝ๊ณ ๋น ๋ฅด๊ฒ ๊ฐ๋ฐํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ผ๊ณ ์๊ฐ๋์ด ์์ต๋๋ค.
- ๊ธฐ์กด์ XML ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด Java์ฝ๋๋ก ๋ณํํ๋ ์์ ์ ํ๊ฒ ๋๋ฉด์ CPU๋ ๋ฐฐํฐ๋ฆฌ ์๋ชจ๋ฅผ ํ๊ณ ์ฝ๋์ ์ฌํ์ฉ์ด ๋ถํธํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
- Anko๋ ์ด๋ฐ ๋จ์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๊ฐ๋ ์ฑ์ ๋์ด๊ณ XML ํ์ฑ์ ๋ฐ๋ฅธ ์ค๋ฒํค๋๋ฅผ ์์จ ์ ์์ต๋๋ค.
- Anko Commons
- Anko Loayouts
- Anko SQLite
- Anko Coroutines
- ์ฐ์ Anko ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Gradle์ถ๊ฐ ์์ ์ด ํ์ํฉ๋๋ค. 2018 - 05 - 17 ์์๋ '0.10.5'๋ฒ์ ๊น์ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
dependencies {
implementation "org.jetbrains.anko:anko:$anko_version"
}
- Anko Commons์ ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑํ ๋ ์ผ๋ฐ์ ์ผ๋ก ์์ฃผ ๊ตฌํํ๋ ๊ธฐ๋ฅ์ ๊ฐํธํ๊ฒ ์ถ๊ฐํ ์ ์๋ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
- Anko Commons์ ์ฌ์ฉํ์ฌ๋ฉด ์ด๋ฅผ ์ฌ์ฉํ ๋ชจ๋์ ๋น๋์คํฌ๋ฆฝํธ์ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
// build.gradle
android {
...
}
dependencies {
// Anko Commons ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
implementation "org.jetbrains.anko:anko-commons:0.10.2"
...
}
- ์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ํ์์ ๋ฐ๋ผ anko-appcompat-v7-commons ํน์ anko=support-v4-commons์ ๋น๋์คํฌ๋ฆฝํธ ๋ด ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
// build.gradle
android {
...
}
dependencies {
// Anko Commons ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
implementation "org.jetbrains.anko:anko-commons:0.10.2"
// appcompat-v7์ฉ Anko Commons ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
implementation "org.jetbrains.anko:anko-appcompat-v7-commons:0.10.2"
// support-v4์ฉ Anko Commons ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
implementation "org.jetbrains.anko:anko-support-v4-commons:0.10.2"
}
- toast() ๋ฐ toastLong() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ํ ์คํธ ๋ฉ์์ง๋ฅผ ๊ฐํธํ๊ฒ ํ์ํ ์ ์์ต๋๋ค.
- ํ ์คํธ๋ฅผ ํ์ํ๋ ค๋ฉด Context ํด๋์ค์ ์ธ์คํด์ค๊ฐ ํ์ํ๋ฏ๋ก, ์ด ํด๋์ค ํน์ ์ด๋ฅผ ์์ํ๋ ํด๋์ค(์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ)๋ด๋ถ์์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
// ๋ค์ ์ฝ๋์ ๋์ผํ ์ญํ ์ ํฉ๋๋ค.
// Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_SHORT).show()
toast("Hello, Kotlin!")
// ๋ค์ ์ฝ๋์ ๋์ผํ ์ญํ ์ ํฉ๋๋ค.
// Toast.makeText(Context, R.string.hello, Toast.LENGTH_SHORT).show()
toast(R.string.hello)
// ๋ค์ ์ฝ๋์ ๋์ผํ ์ญํ ์ ํฉ๋๋ค.
// Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_LONG).show()
longToast("Hello, Kotlin!)
- alert() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด AlertDialog๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
- ํ ์คํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก Context ํด๋์ค ํน์ ์ด๋ฅผ ์์ํ๋ ํด๋์ค(์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ) ๋ด๋ถ์์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
// ๋ค์ด์ผ๋ก๊ทธ์ ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ์ ์ง์ ํฉ๋๋ค.
alert(title = "Message", message = "Let's learn Kotlin!") {
// AlertDialog.Builder.setPositiveButton()์ ๋์ํฉ๋๋ค.
positiveButton("Yes") {
// ๋ฒํผ์ ํด๋ฆญํ์ ๋ ์ํํ ๋์์ ๊ตฌํํฉ๋๋ค.
toast("Yay!")
}
// AlertDialog.Builder.setNegativeButton()์ ๋์ํฉ๋๋ค.
negativeButton("No") {
// ๋ฒํผ์ ํด๋ฆญํ์ ๋ ์ํํ ๋์์ ๊ตฌํํฉ๋๋ค.
longToast("No way...")
}
}.show()
- ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ๋ค์ด์ผ๋ก๊ทธ๊ฐ ์๋ ์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ ๋ค์ด์ผ๋ก๊ทธ(android.support.v7.app.AlertDialog)๋ฅผ ์์ฑํ๋ ค๋ฉด,
- anko-appcompat-v7-commons์ ์์กด์ฑ์ ์ถ๊ฐํ ํ ๋ค์๊ณผ ๊ฐ์ด Appcompat์ ํจ๊ป ์ธ์์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
// import ๋ฌธ์ด ์ถ๊ฐ๋ฉ๋๋ค.
import org.jetbrains.anko.appcompat.v7.Appcompat
// Appcompat์ ์ธ์์ ์ถ๊ฐํฉ๋๋ค.
alert(Appcompat, title = "Message", message = "Let's learn Kotlin!") {
...
}.show()
- ์ฌ๋ฌ ํญ๋ชฉ ์ค ํ๋๋ฅผ ์ ํํ๋๋ก ํ ๋ ์ฌ์ฉํ๋ ๋ฆฌ์คํธ ๋ค์ด์ผ๋ก๊ทธ๋ selector() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ ์ ์์ต๋๋ค.
// ๋ค์ด์ผ๋ก๊ทธ์ ํ์ํ ๋ชฉ๋ก์ ์์ฑํฉ๋๋ค.
val cities = listOf("Seoul", "Tokyo", "Mountain View", "Singapore")
// ๋ฆฌ์คํธ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์์ฑํ๊ณ ํ์ํฉ๋๋ค.
selector(title = "Select City", items = cities) { dig, selection ->
// ํญ๋ชฉ์ ์ ํํ์ ๋ ์ํํ ๋์์ ๊ตฌํํฉ๋๋ค.
toast("You selected ${cities[selection]}!")
}
- ์์ ์ ์งํ ์ํ๋ฅผ ํ์ํ ๋ ์ฌ์ฉํ๋ ํ๋ก๊ทธ๋ ์ค ๋ค์ด์ผ๋ก๊ทธ๋progressDialog()์ indeterminateProgressDialog() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ ์ ์์ต๋๋ค.
- progressDialog() ํจ์๋ ํ์ผ ๋ค์ด๋ก๋ ์ํ์ ๊ฐ์ด ์งํ๋ฅ ์ ํ์ํด์ผ ํ๋ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉํฉ๋๋ค.
- indeterminateProgressDialog() ํจ์๋ ์งํ๋ฅ ์ ํ์ํ์ง ์๋ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉํฉ๋๋ค.
// ์งํ๋ฅ ์ ํ์ํ๋ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์์ฑํฉ๋๋ค.
val pd = progressDialog(title = "File Download", message = "Downloading...")
// ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ํ์ํฉ๋๋ค.
pd.show()
// ์งํ๋ฅ ์ 50์ผ๋ก ์กฐ์ ํฉ๋๋ค.
pd.progress = 50
// ์งํ๋ฅ ์ ํ์ํ์ง ์๋ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์์ฑํ๊ณ ํ์ํฉ๋๋ค.
indenterminateProgressDialog(message = "Please wait...").show()
- ์ธํ ํธ๋ ์ปดํฌ๋ํธ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋์๋ ์ฌ์ฉํ์ง๋ง ์ฃผ๋ก ์กํฐ๋นํฐ๋ ์๋น์ค๋ฅผ ์คํํ๋ ์ฉ๋๋ก ์ฌ์ฉํฉ๋๋ค.
- ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์คํํ๊ธฐ ์ํด ์ธํ ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ด ์ธํ ํธ๋ ๋์ ์ปดํฌ๋ํธ์ ๋ํ ์ ๋ณด์ ๊ธฐํ ๋ถ๊ฐ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.
// DetailActivity ์กํฐ๋นํฐ๋ฅผ ๋์ ์ปดํฌ๋ํธ๋ก ์ง์ ํ๋ ์ธํ
ํธ
val intent = Intent(this, DetailActivity::class.java)
// DetailActivity๋ฅผ ์คํํฉ๋๋ค.
startActivity(intent)
- ์ด ์ธํ ํธ์ ๋ถ๊ฐ ์ ๋ณด๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ํ๋๊ทธ๋ฅผ ์ค์ ํ๋ ๊ฒฝ์ฐ ์ธํ ํธ๋ฅผ ์์ฑํ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
val intent = Intent(this, DetailActivity::class.java)
// ์ธํ
ํธ์ ๋ถ๊ฐ์ ๋ณด๋ฅผ ์ถ๊ฐํฉ๋๋ค.
intent.putExtra("id", 150L)
intent.putExtra("title", "Awesome item")
// ์ธํ
ํธ์ ํ๋๊ทธ๋ฅผ ์์ฑํฉ๋๋ค.
intent.setFlag(Intent.FLAG_ACTIVITY_NO_HISTORY)
- intentFor() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์ฌ ๊ฐ์ํ ํํ๋ก ๋์ผํ ์ญํ ์ ํ๋ ์ธํ ํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
val intent = intentFor<DetailActivity>(
// ๋ถ๊ฐ ์ ๋ณด๋ฅผ Pair ํํ๋ก ์ถ๊ฐํฉ๋๋ค.
"id" to 150L, "title" to "Awesome item")
// ์ธํ
ํธ ํ๋๊ทธ๋ฅผ ์ค์ ํฉ๋๋ค.
.noHistory()
- ์ธํ ํธ์ ํ๋๊ทธ๋ฅผ ์ง์ ํ์ง ์๋๋ค๋ฉด, startActivity() ํจ์๋ startService() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ธํ ํธ ์์ฑ๊ณผ ์ปดํฌ๋ํธ ํธ์ถ์ ๋์์ ์ํํ ์ ์์ต๋๋ค.
- ์ด๋ค ํจ์๋ ๋ชจ๋ Context ํด๋์ค๋ฅผ ํ์๋ก ํ๋ฏ๋ก, ์ด ํด๋์ค ํน์ ์ด๋ฅผ ์์ํ๋ ํด๋์ค(์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ) ๋ด๋ถ์์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
// ๋ถ๊ฐ์ ๋ณด ์์ด DetailActivity๋ฅผ ์คํํฉ๋๋ค.
startActivity<DetailActivity>()
// ๋ถ๊ฐ์ ๋ณด๋ฅผ ํฌํจํ์ฌ DetailActivity๋ฅผ ์คํํฉ๋๋ค.
startActivity<DetailActivity>("id" to 150L, "title" to "Awesome item")
// ๋ถ๊ฐ์ ๋ณด ์์ด DataSyncService๋ฅผ ์คํํฉ๋๋ค.
startService<DataSyncService>()
// ๋ถ๊ฐ์ ๋ณด๋ฅผ ํฌํจํ์ฌ DataSyncService๋ฅผ ์คํํฉ๋๋ค.
startService<DataSyncService>("id" to 1000L)
- ์ด ์ธ์๋, ์์ฃผ ์ฌ์ฉํ๋ ํน์ ์์ ์ ๋ฐ๋ก ์ํํ ์ ์๋ ํจ์๋ค์ ์ ๊ณตํฉ๋๋ค.
// ์ ํ๋ฅผ ๊ฑฐ๋ ์ธํ
ํธ๋ฅผ ์คํํฉ๋๋ค.
makeCall(number = "01012345678")
// ๋ฌธ์๋ฉ์์ง๋ฅผ ๋ฐ์กํ๋ ์ธํ
ํธ๋ฅผ ์คํํฉ๋๋ค.
sendSMS(number = "01012345678", text = "Hello, Kotlin!")
// ์น ํ์ด์ง๋ฅผ ์ฌ๋ ์ธํ
ํธ๋ฅผ ์คํํฉ๋๋ค.
browse(url = "https://google.com")
// ์ด๋ฉ์ผ์ ๋ฐ์กํ๋ ์ธํ
ํธ๋ฅผ ์คํํฉ๋๋ค.
email(email = "[email protected]", subject = "Hello, Taeho Kim", text = "How are you?")
- ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ก๊ทธ๋ฉ์์ง๋ฅผ ๊ธฐ๋กํ๋ ค๋ฉด android.util.Log ํด๋์ค์์ ์ ๊ณตํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
- ํ์ง๋ง ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๋ ํจ์๋ฅผ ํธ์ถํ ๋๋ง๋ค ๋งค๋ฒ ํ๊ทธ๋ฅผ ํจ๊ป ์ ๋ ฅํด์ผ ํ๋ฏ๋ก ๋ค์ ๋ถํธํฉ๋๋ค.
- Anko ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ AnkoLogger๋ฅผ ์ฌ์ฉํ๋ฉด ํจ์ฌ ํธ๋ฆฌํ๊ฒ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๊ธฐ๋กํ ์ ์์ต๋๋ค.
- AnkoLogger์์๋ ๋ค์๊ณผ ๊ฐ์ด android.util.Log ํด๋์ค์ ๋ก๊ทธ ๊ธฐ๋ก ๋ฉ์๋์ ๋์ํ๋ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
android.util.Log | AnkoLogger |
---|---|
v() | verbose() |
d() | debug() |
i() | info() |
w() | warn() |
e() | error() |
wtf() | wtf() |
- AnkoLogger๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์ด๋ฅผ ์ฌ์ฉํ ํด๋์ค์์ AnkoLogger ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ฉด ๋ฉ๋๋ค.
- AnkoLogger ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์กํฐ๋นํฐ์์์ ์ฌ์ฉ ์๋ ๋ค์ ์ฝ๋์์ ํ์ธํ ์ ์์ต๋๋ค.
- ์ถ๋ ฅํ ๋ฉ์์ง์ ํ์ ์ผ๋ก String๋ง ํ์ฉํ๋ android.util.Log ํด๋์ค์ ๋ฌ๋ฆฌ ๋ชจ๋ ํ์ ์ ํ์ฉํ๋ ๋ชจ์ต์ ํ์ธํ ์ ์์ต๋๋ค.
// AnkoLogger ์ธํฐํจ์ด์ค๋ฅผ ๊ตฌํํฉ๋๋ค.
class MainActivity: AppCompatActivity(), AnkoLogger {
fun doSomething() {
// Log.INFO ๋ ๋ฒจ๋ก ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
info("doSomething() called")
}
fun doSomethingWithParameter(number: Int) {
// Log.DEBUG ๋ ๋ฒจ๋ก ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
// String ํ์
์ด ์๋ ์ธ์๋ ํด๋น ์ธ์์ toString() ํจ์ ๋ณํ๊ฐ์ ๊ธฐ๋กํฉ๋๋ค.
debug(number)
}
...
}
- AnkoLogger์์ ์ ๊ณตํ๋ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ๊ธฐ๋กํ๋ ๊ฒฝ์ฐ, ๋ก๊ทธ ํ๊ทธ๋ก ํด๋น ํจ์๊ฐ ํธ์ถ๋๋ ํด๋์ค์ ์ด๋ฆ์ ์ฌ์ฉํฉ๋๋ค.
- ๋ฐ๋ผ์ ์์ ์์ ์์๋ "MainActivity"๋ฅผ ๋ก๊ทธ ํ๊ทธ๋ก ์ฌ์ฉํฉ๋๋ค.
- ๋ก๊ทธ ํ๊ทธ๋ฅผ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด loggerTag ํ๋กํผํฐ๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๋ฉด ๋ฉ๋๋ค.
class MainActivity: AppCompatActivity(), AnkoLogger {
// ์ด ํด๋์ค ๋ด์์ ์ถ๋ ฅ๋๋ ๋ก๊ทธ ํ๊ทธ๋ฅผ "Main"์ผ๋ก ์ง์ ํฉ๋๋ค.
override val loggerTag: String
get() = "Main"
...
}
- ์๋๋ก์ด๋๋ ๋ค์ํ ๊ธฐ๊ธฐ๋ฅผ ์ง์ํ๊ธฐ ์ํด ํฝ์ (px) ๋จ์ ๋์ dip(ํน์ dp; device independent pixels)๋ sp(scale independent pixels)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- dp๋ sp ๋จ์๋ ๊ฐ ๋จ๋ง๊ธฐ์ ํ๋ฉด ํฌ๊ธฐ๋ ๋ฐ๋์ ๋ฐ๋ผ ํ๋ฉด์ ํ์๋๋ ํฌ๊ธฐ๋ฅผ ์ผ์ ๋น์จ๋ก ์กฐ์ ํ๋ฏ๋ก, ๋ค์ํ ํ๋ฉด ํฌ๊ธฐ๋ ๋ฐ๋๋ฅผ ๊ฐ์ง ๋จ๋ง๊ธฐ์ ๋์ํ๋ UI๋ฅผ ์์ฑํ ๋ ์ ์ฉํฉ๋๋ค.
- ์ปค์คํ ๋ทฐ ๋ด๋ทฐ์ ๊ฐ์ด ๋ทฐ์ ํ์๋๋ ์์์ ํฌ๊ธฐ๋ฅผ ํฝ์ ๋จ์๋ก ๋ค๋ฃจ๋ ๊ฒฝ์ฐ dp๋ sp๋จ์๋ฅผ ํฝ์ ๋จ์๋ก ๋ณํํ๊ธฐ ์ํด ๋ณต์กํ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ํฉ๋๋ค.
class MainActivity : AppCompatActivity() {
fun doSomething() {
// 100dp๋ฅผ ํฝ์
๋จ์๋ก ๋ณํํฉ๋๋ค.
val dpInPixel = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics)
// 16sp๋ฅผ ํฝ์
๋จ์๋ก ๋ณํํฉ๋๋ค.
val spInPixel = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics)
}
...
}
- Anko์์ ์ ๊ณตํ๋ dip() ๋ฐ sp() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฌํ ๋จ์๋ฅผ ๋งค์ฐ ๊ฐ๋จํ ๋ณํํ ์ ์์ต๋๋ค.
- ๋จ์๋ฅผ ๋ณํํ๊ธฐ ์ํด ๋จ๋ง๊ธฐ์ ํ๋ฉด ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ DisplayMetrics ๊ฐ์ฒด๊ฐ ํ์ํ๋ฏ๋ก, ์ด ํจ์๋ค์ ๋จ๋ง๊ธฐ ํ๋ฉด ์ ๋ณด์ ์ ๊ทผํ ์ ์๋ ํด๋์ค์ธ Context๋ฅผ ์์ํ ํด๋์ค ํน์ ์ปค์คํ ๋ทฐ ํด๋์ค ๋ด์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- dip() ํจ์ ๋ฐ sp() ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ํํํ ๋ชจ์ต์ ๋๋ค.
- TypedValue.applyDimension() ๋ฉ์๋๋ Float ํ ์ธ์๋ง ์ง์ํ์ง๋ง, dip() ๋ฐ sp() ํจ์๋ Int ํ ์ธ์๋ ์ง์ํฉ๋๋ค.
// 100dp๋ฅผ ํฝ์
๋จ์๋ก ๋ณํํฉ๋๋ค.
val dpInPixel = dip(100)
// 16sp๋ฅผ ํฝ์
๋จ์๋ก ๋ณํํฉ๋๋ค.
val spInPixel = sp(16)
- ๋ฐ๋๋ก, ํฝ์ ๋จ์๋ฅผ dp๋ sp ๋จ์๋ก ๋ณํํ๋ ํจ์๋ ์ ๊ณตํฉ๋๋ค. ๊ฐ๊ฐ px2dip(), px2sp() ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
// 300px๋ฅผ dp ๋จ์๋ก ๋ณํํฉ๋๋ค.
val pxInDip = px2dip(300)
// 80px๋ฅผ sp ๋จ์๋ก ๋ณํํฉ๋๋ค.
val pxInSp = px2sp(80)
- ์ฌ๋ฌ ๋จ๋ง๊ธฐ ํ๊ฒฝ์ ์ง์ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์, ๋จ๋ง๊ธฐ ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฅธ ํํ์ UI๋ฅผ ๋ณด์ฌ์ฃผ๋๋ก ๊ตฌํํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
- ์ด๋ฌํ ๊ฒฝ์ฐ, configuration() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ๋จ๋ง๊ธฐ ํ๊ฒฝ์ผ ๋๋ง ์คํํ ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
๋งค๊ฐ๋ณ์ ์ด๋ฆ | ๋จ๋ง๊ธฐ ํ๊ฒฝ ์ข ๋ฅ |
---|---|
density | ํ๋ฉด ๋ฐ๋ |
language | ์์คํ ์ธ์ด |
long | ํ๋ฉด ๊ธธ์ด |
nightMode | ์ผ๊ฐ๋ชจ๋ ์ฌ๋ถ |
orientation | ํ๋ฉด ๋ฐฉํฅ |
rightToLeft | RTL(Right-to-Left)๋ ์ด์์ ์ฌ๋ถ |
screenSize | ํ๋ฉด ํฌ๊ธฐ |
smallestWidth | ํ๋ฉด์ ๊ฐ์ฅ ์์ ๋ณ์ ๊ธธ์ด |
uiMode | UI ๋ชจ๋(์ผ๋ฐ, TV, ์ฐจ๋, ์๊ณ, VR ๋ฑ) |
- configuration() ๋ํ ๋จ๋ง๊ธฐ ํ๊ฒฝ์ ์ ๊ทผํด์ผ ํ๋ฏ๋ก ์ด ์ ๋ณด์ ์ ๊ทผํ ์ ์๋ Context ํด๋์ค ํน์ ์ด๋ฅผ ์์ํ ํด๋์ค(์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ)์์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
class MainActivity : AppCompatActivity() {
fun doSomething() {
configuration(orientation = Orientation.PORTRAIT) {
// ๋จ๋ง๊ธฐ๊ฐ ์ธ๋ก ๋ฐฉ๋์ผ ๋ ์ํํ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
...
}
configuration(orientation = Orientation.LANDSCAPE, language = "ko") {
// ๋จ๋ง๊ธฐ๊ฐ ๊ฐ๋ก ๋ฐฉํฅ์ด๋ฉด์ ์์คํ
์ธ์ด๊ฐ ํ๊ตญ์ด๋ก ์ค์ ๋์ด ์์ ๋
// ์ํํ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
...
}
}
...
}
- ๋จ์ํ ๋จ๋ง๊ธฐ์ OS ๋ฒ์ ์ ๋ฐ๋ผ ๋ถ๊ธฐ๋ฅผ ์ํํ๋ ๊ฒฝ์ฐ doFromSdk()์ doIfSdk()๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
doFromSdk(Build.VERSION_CODES.0) {
// ์๋๋ก์ด๋ 8.0 ์ด์ ๊ธฐ๊ธฐ์์ ์ํํ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
...
}
doIfSdk(Build.VERSION_CODES.N) {
// ์๋๋ก์ด๋ 7.0 ๊ธฐ๊ธฐ์์๋ง ์ํํ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
...
}
- ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑํ ๋, ๋๋ถ๋ถ XML ๋ ์ด์์์ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํฉ๋๋ค.
- ์์ค ์ฝ๋(Java ํน์ Kotlin)๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ๋ ๊ฒ๋ ๊ฐ๋ฅํ์ง๋ง XML ๋ ์ด์์์ ๋นํด ๋ณต์กํ๊ณ ๊น๋ค๋ผ๋ก์ ๋๋ค์์ ์ฌ๋๋ค์ด ์ ํธํ์ง ์์ต๋๋ค.
- ํ์ง๋ง XML๋ก ์์ฑ๋ ๋ ์ด์์์ ์ฌ์ฉํ๋ ค๋ฉด ์ด ํ์ผ์ ์ ์๋ ๋ทฐ๋ฅผ ํ์ฑํ๋ ์์ ์ ๋จผ์ ์ํํด์ผ ํฉ๋๋ค.
- ๋๋ฌธ์ ์์ค ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ ๊ฒฝ์ฐ์ ๋นํด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ด ์ ํ๋๊ณ , ํ์ฑ ๊ณผ์ ์์ ์์์ด ๋ ํ์ํ ๋งํผ ๋ฐฐํฐ๋ฆฌ๋ ๋ ๋ง์ด ์๋ชจํฉ๋๋ค.
- Anko Layouts๋ ์์ค ์ฝ๋๋ก ํ๋ฉด์ ๊ตฌ์ฑํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ํจ์๋ค์ ์ ๊ณตํ๋ฉฐ, ์๋ฃฐ ์ฌ์ฉํ๋ฉด XML ๋ ์ด์์์ ์์ฑํ๋ ๊ฒ์ฒ๋ผ ํธ๋ฆฌํ๊ฒ ์์ค์ฝ๋๋ก๋ ํ๋ฉด์ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
- Anko Layouts์ ์ฌ์ฉํ๋ ค๋ฉด ์ด๋ฅผ ์ฌ์ฉํ ๋ชจ๋์ ๋น๋์คํฌ๋ฆฝํธ์ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ๋๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ minSdkVersion์ ๋ฐ๋ผ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฌ๋ผ์ง๋๋ค.
- ์ ํ๋ฆฌ์ผ์ด์ ์ minSdkVersion์ ๋์ํ๋ Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
minSdkVersion | Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ |
---|---|
15์ด์ 19๋ฏธ๋ง | anko-sdk15 |
19์ด์ 21๋ฏธ๋ง | anko-sdk19 |
21์ด์ 23๋ฏธ๋ง | anko-sdk21 |
23์ด์ 25๋ฏธ๋ง | anko-sdk23 |
25์ด์ | anko-sdk25 |
android {
defaultConfig {
// minSdkVersion์ด 15๋ก ์ค์ ๋์ด ์์ต๋๋ค.
minSdkVersion 15
targetSdkVersion 27
...
}
...
}
dependencies {
// minSdkVersion์ ๋ง์ถ์ด Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
compile "org.jetbrains.anko:anko-sdk15:0.10.2"
}
- ์ํํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋ ๋ทฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ฌ์ฉํ๋ ๋ทฐ๊ฐ ํฌํจ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋์ํ๋ Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
- ๊ฐ ์ํํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋์ํ๋ Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ |
---|---|
appcompat-v7 | anko-appcompat-v7 |
cardview | anko-cardview-v7 |
design | anko-design |
gridlayout | anko-gridlayout-v7 |
recyclerview-v7 | anko-recyclerview-v7 |
support-v4 | anko-support-v4 |
- ๋ค์์ ์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ด์ ๋์ํ๋ Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์กด์ฑ์ผ๋ก ์ถ๊ฐํ ์์ ๋๋ค.
android {
...
}
dependencies {
// appcompat-v7 ์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
implementation "com.android.support:appcompat-v7:27.0.1"
// appcompat-v7์ฉ Anko Layouts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
implementation "org.jetbrains.anko:anko-appcompat-v7:0.10.2"
}
- Anko Layouts์ ์ฌ์ฉํ๋ฉด ์์ค ์ฝ๋์์ ํ๋ฉด์ DSL(Domain Specific Language) ํํ๋ก ์ ์ํ ์ ์์ต๋๋ค.
- ๋ค์์ DSL์ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ๋ ๊ฐ๋จํ ์๋ฅผ ๋ณด์ฌ์ค๋๋ค. XML ๋ ์ด์์์ผ๋ก ์ ์ํ ๋๋ณด๋ค ๋ ๊ฐ๋จํ๊ฒ ํ๋ฉด์ ๊ตฌ์ฑํ ์ ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
verticalLayout {
padding = dip(12)
textView("Enter Login Credentials")
editText {
hint = "E-mail"
}
editText {
hint = "Password"
}
button("Submit")
}
- ์์ ์ฝ๋์์ ์ฌ์ฉํ verticalLayout(), textView(), editText(), button()์ Anko Layout์์ ์ ๊ณตํ๋ ํจ์๋ก, ๋ทฐ ํน์ ๋ค๋ฅธ ๋ทฐ๋ฅผ ํฌํจํ ์ ์๋ ๋ ์ด์์์ ์์ฑํ๋ ์ญํ ์ ํฉ๋๋ค.
- ๋ค์์ ์ฌ๊ธฐ์์ ์ ๊ณตํ๋ ํจ์ ์ค ์์ฃผ ์ฌ์ฉํ๋ ํจ์ ๋ช ๊ฐ์ ๋ชฉ๋ก์ ๋๋ค.
ํจ์ | ์์ฑํ๋ ๋ทฐ | ๋น๊ณ |
---|---|---|
button() | android.widget.Button | |
checkBox() | android.widget.CheckBox | |
editText() | android-widget.EditText | |
frameLayout() | android-widget.FrameLayout | |
imageView() | android-widget-ImageView | |
linearLayout() | android.widget.LinearLayout | |
radioButton() | android.widget.RadioButton | |
relativeLayout() | android-widget.RelativeLayout | |
switch() | android-widget.Switch | ์ํฌํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ ๋ทฐ๋ switchCompat() ์ฌ์ฉ |
verticalLayout() | android-widget-LinearLayout | orientation ๊ฐ์ผ๋ก LinearLayout.VERTICAL์ ๊ฐ๋ LinearLayout |
webView() | android-webkit.WebView |
- XML ๋ ์ด์์ ํ์ผ์ XML๋ก ๊ตฌ์ฑํ ๋ ์ด์์์ ์ ์ฅํ๋ฏ์ด, DSL๋ก ๊ตฌ์ฑํ ๋ทฐ๋ AnkoComponent ํด๋์ค๋ฅผ ์ปจํ ์ด๋๋ก ์ฌ์ฉํฉ๋๋ค.
- AnkoComponent์๋ ์ ์๋์ด ์๋ ํ๋ฉด์ ํ์ํ ๋์ ์ปดํฌ๋ํธ์ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.
- ๋ค์์ MainActivity ์กํฐ๋นํฐ์ ํ์ํ ๋ทฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ง๋ AnkoComponent์ ์ฝ๋ ์์๋ฅผ ๋ณด์ฌ์ค๋๋ค.
class MainActivityUI : AnkoComponent<MainActivity> {
override fun createView(ui: AnkoContext<MainActivity>) = ui.apply {
vertivalLayout {
// LinearLayout์ padding์ 12dp๋ก ์ค์ ํฉ๋๋ค.
padding = dip(12)
// TextView๋ฅผ ์ถ๊ฐํฉ๋๋ค.
textView("Enter Login Credentials")
// EditText๋ฅผ ์ถ๊ฐํ๊ณ , ํํธ ๋ฌธ์์ด์ ์ค์ ํฉ๋๋ค.
editText {
hint = "E-mail"
}
editText {
hint = "password"
}
// ๋ฒํผ์ ์ถ๊ฐํฉ๋๋ค.
button("Submit")
}
}.view
}
- ์ถ๊ฐ๋ก, ์กํฐ๋นํฐ์์๋ AnkoComponent ์์ด ์ง์ ์กํฐ๋นํฐ ๋ด์์ DSL์ ์ฌ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
- ๋ค์์ ์์ ์ฝ๋์ ๋์ผํ ๋ ์ด์์์ AnkoComponent ์์ด ๊ตฌ์ฑํ๋ ์์ ๋๋ค. ์ด ๋ฐฉ์์ผ๋ก ํ๋ฉด์ ๊ตฌ์ฑํ๋ ๊ฒฝ์ฐ setContentView()๋ฅผ ํธ์ถํ์ง ์์๋ ๋ฉ๋๋ค.
class MainActivity: AppcompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView()๊ฐ ์์ด๋ ๋ฉ๋๋ค.
verticalLayout {
padding = dip(12)
textView("Enter Login Credentials")
editText {
hint = "E-mail"
}
editText {
hint = "Password"
}
button("Submit")
}
}
}
- ํ๋๊ทธ๋จผํธ์์ Anko Layouts์ ์ฌ์ฉํ๋ ค๋ฉด ํ๋๊ทธ๋จผํธ๋ฅผ ์ํ AnkoComponent๋ฅผ ๋ง๋ค๊ณ , onCreateView()์์ createView()๋ฅผ ์ง์ ํธ์ถํ์ฌ ํ๋๊ทธ๋จผํธ์ ํ๋ฉด์ผ๋ก ์ฌ์ฉํ ๋ทฐ๋ฅผ ๋ฐํํ๋ฉด ๋ฉ๋๋ค.
- createView()๋ฅผ ์ง์ ํธ์ถํ๋ ค๋ฉด AnkoContext ๊ฐ์ฒด๋ฅผ ์ง์ ๋ง๋ค์ด ์ธ์๋ก ์ ๋ฌํ๋ฉด ๋ฉ๋๋ค.
class MainFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?{
// AnkoComponent.createView() ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ทฐ๋ฅผ ๋ฐํํฉ๋๋ค.
return MainFragmentUI().createView(AnkoContext.create(context, this))
}
}
// ํ๋๊ทธ๋จผํธ๋ฅผ ์ํ AnkoComponent๋ฅผ ๋ง๋ญ๋๋ค.
class MainFragmentUI : AnkoComponent<MainFragment> {
override fun createView(ui: AnkoContext<MainFragment>) = ui.apply {
verticalLayout {
textView("Enter Login Credentials")
editText {
hint = "E-mail"
}
editText {
hint = "Password"
}
button("Submit")
}
}.view
}
-
Anko Support Plugin์ Anko์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ IDE ํ๋ฌ๊ทธ์ธ์ ๋๋ค.
-
ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๋ ค๋ฉด ์ฝํ๋ฆฐ IDE ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๋ ๊ณผ์ ๊ณผ ๋์ผํ๊ฒ ์งํํ๋ฉด ๋๋ฉฐ, ํ๋ฌ๊ทธ์ธ ๊ฒ์ ๋ค์ด์ผ๋ก๊ทธ์์ ๋ค์๊ณผ ๊ฐ์ด 'Anko Support'๋ฅผ ์ ํํ์ฌ ์ค์นํ๋ฉด ๋ฉ๋๋ค.
-
Anko Support Plugin์์๋ AnkoComponent๋ก ์์ฑํ ํ๋ฉด์ด ์ด๋ป๊ฒ ํ์๋๋์ง ๋ฏธ๋ฆฌ ํ์ธํ ์ ์๋ ๋ ์ด์์ ํ๋ฆฌ๋ทฐ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
-
๋ ์ด์์ ํ๋ฆฌ๋ทฐ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด, ๋จผ์ ํ๋ฆฌ๋ทฐ ๊ธฐ๋ฅ์ผ๋ก ํ์ธํ๊ณ ์ถ์ AnkoComponent๊ฐ ๊ตฌํ๋์ด ์๋ ํ์ผ์ ์ฐ ํ AnkoComponent์ ๊ตฌํ๋ถ ๋ด๋ถ ์๋ฌด๊ณณ์ ์ปค์๋ฅผ ๋ก๋๋ค.
-
๊ทธ ๋ค์, [View > Tools Windows > Anko Layout Preview]๋ฅผ ์ ํํ์ฌ ๋ ์ด์ด์ ํ๋ฆฌ๋ทฐ ์ฐฝ์ ๋์๋๋ค.
-
๋ ์ด์์ ํ๋ฆฌ๋ทฐ ์ฐฝ์ XML ๋ ์ด์์ ํ๋ฆฌ๋ทฐ ์ฐฝ๊ณผ ๊ฑฐ์ ์ ์ฌํ ํํ๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค.
-
์์์ ์ ํํ AnkoComponent์ ๋ ์ด์์ ํ๋ฆฌ๋ทฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ฉฐ, ํ๋ฉด์ด ํ์๋์ง ์๊ฑฐ๋ ๋ฐ๋ ๋ด์ฉ์ด ๋ฐ์๋์ง ์์๋ค๋ฉด ํ๋ก์ ํธ๋ฅผ ๋ค์ ๋น๋ํ๋ฉด ๋ฉ๋๋ค.
- Android ์ปค์๋ฅผ ์ฌ์ฉํ์ฌ SQLite ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํ์ฑํ๋ ๊ฒ์ ํ๋ญ๋๋ค.
- ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ ํ์ ๊ตฌ๋ฌธ ๋ถ์ํ๊ธฐ ์ํด ๋ง์ ์์ฉ๊ตฌ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ด๋ฅผ ์ด๊ฑฐ ๋ ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ์ ์ ํ๊ฒ ๋ซ์ผ๋ ค๋ฉด ์ ์์๋ try..finally ๋ธ๋ก์ผ๋ก ๋ฌถ์ด์ผํฉ๋๋ค.
- Anko๋ SQLite databases์ ํจ๊ป ๊ฐ๋จํ๊ฒ ์๋ํ ์ ์๋๋ก ๋ง์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํฉ๋๋ค.
- build.gradle์ dependency์ anko-sqlite๋ฅผ ์ถ๊ฐํฉ๋๋ค.
dependencies {
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}
- SQLiteOpenHelper๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ผ๋ก getReadableDatabase () ๋๋ getWritableDatabase ()๋ฅผ ํธ์ถํฉ๋๋ค.
- ๊ฒฐ๊ณผ๋ ์ค์ ๋ก ํ๋ก๋์ ์ฝ๋์์ ๋์ผํ์ง๋ง ์์ ๋ SQLiteDatabase์์ close () ๋ฉ์๋๋ฅผ ํธ์ถํด์ผํฉ๋๋ค.
- ๋ํ ์ด๋๊ฐ์ ๋์ฐ๋ฏธ ํด๋์ค๋ฅผ ์บ์ํด์ผํ๋ฉฐ ์ฌ๋ฌ ์ค๋ ๋์์์ด ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋์ ์ก์ธ์ค๋ฅผ ์ธ์ํ๊ณ ์์ด์ผํฉ๋๋ค.
- ์ด ๋ชจ๋ ๊ฒ์ ๊ฝค ํ๋ญ๋๋ค. ๊ทธ๋์ ์๋๋ก์ด๋ ๊ฐ๋ฐ์๋ ๋ํดํธ SQLite API์ ์ด์คํ์ง ์๊ณ ๋์ ORM๊ณผ ๊ฐ์ ๊ฐ ๋น์ผ ๋ํผ๋ฅผ ์ ํธํฉ๋๋ค.
- Anko๋ ๊ธฐ๋ณธ ํด๋์ค๋ฅผ ์๋ฒฝํ๊ฒ ๋์ฒดํ๋ ManagedSQLiteOpenHelper ํด๋์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
class MyDatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) {
companion object {
private var instance: MyDatabaseOpenHelper? = null
@Synchronized
fun getInstance(ctx: Context): MyDatabaseOpenHelper {
if (instance == null) {
instance = MyDatabaseOpenHelper(ctx.getApplicationContext())
}
return instance!!
}
}
override fun onCreate(db: SQLiteDatabase) {
// Here you create tables
db.createTable("Customer", true,
"id" to INTEGER + PRIMARY_KEY + UNIQUE,
"name" to TEXT,
"photo" to BLOB)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
// Here you can upgrade tables, as usual
db.dropTable("User", true)
}
}
// Access property for Context
val Context.database: MyDatabaseOpenHelper
get() = MyDatabaseOpenHelper.getInstance(getApplicationContext())
- try ๋ธ๋ก์ ์ฝ๋๋ฅผ ํฌํจํ๋ ๋์ ์ด์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ์ ์์ต๋๋ค.
database.use {
// 'this' is a SQLiteDatabase instance
}
-
{} ์์ ๋ชจ๋ ์ฝ๋๋ฅผ ์คํ ํ ํ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์์ ํ ๋ซํ๋๋ค.
-
๋น๋๊ธฐ ํธ์ถ ์์ :
class SomeActivity : Activity() {
private fun loadAsync() {
async(UI) {
val result = bg {
database.use { ... }
}
loadComplete(result)
}
}
}
- ์๋ ์ธ๊ธ๋ ๋ฉ์๋๋ค๊ณผ ๋ชจ๋ ๋ฉ์๋๋ฅผ์ SQLiteException์ throw ํ ์ ์์ต๋๋ค.
- Anko๊ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๋ ๊ฒ์ฒ๋ผ ๊ฐ์ฅํ๋ ๊ฒ์ ๋ฌด๋ฆฌ์ผ ์ ์์ผ๋ฏ๋ก ์ง์ ์ฒ๋ฆฌํด์ผํฉ๋๋ค.
- Anko์ ํจ๊ป๋ผ๋ฉด ์ฝ๊ฒ ์๋ก์ด ํ ์ด๋ธ์ ๋ง๋ค๊ณ ์ญ์ ํ ์ ์์ต๋๋ค. ๊ตฌ๋ฌธ์ ๊ฐ๋จํฉ๋๋ค.
database.use {
createTable("Customer", true,
"id" to INTEGER + PRIMARY_KEY + UNIQUE,
"name" to TEXT,
"photo" to BLOB)
}
- SQLite์์๋ NULL, INTEGER, REAL, TEXT, BLOB ์ด 5๊ฐ์ ๋ฉ์ธ ํ์ ๋ค์ด ์์ต๋๋ค.
- ํ์ง๋ง ๊ฐ ์ด์๋ PRIMATY KEY ๋๋ UNIQUE์ ๊ฐ์ ์์ ์๊ฐ ์์ ์ ์์ต๋๋ค. ์ด๋ฌํ ์์ ์๋ ๊ธฐ๋ณธ ์ ํ ์ด๋ฆ์ "์ถ๊ฐ"์ ํจ๊ป ์ถ๊ฐ ํ ์ ์์ต๋๋ค.
- ํ ์ด๋ธ์ ์ง์ธ ๋์๋ dropTable ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
dropTable("User", true)
- ์ผ๋ฐ์ ์ผ๋ก ํ ์ด๋ธ์ ํ์ ์ฝ์ ํ๋ ค๋ฉด ContentValues โโ์ธ์คํด์ค๊ฐ ํ์ํฉ๋๋ค. ๋ค์์ ๊ทธ ์์ ๋๋ค.
val values = ContentValues()
values.put("id", 5)
values.put("name", "John Smith")
values.put("email", "[email protected]")
db.insert("User", null, values)
- Anko๋ insert() ํจ์์ ์ธ์๋ก ๊ฐ์ ์ง์ ์ ๋ฌํ์ฌ ์ด๋ฌํ ์ฝ๋์ ๊ฑฐ์น๋์ ์ ๊ฑฐ ํ ์ ์์ต๋๋ค.
// Where db is an SQLiteDatabase
// eg: val db = database.writeableDatabase
db.insert("User",
"id" to 42,
"name" to "John",
"email" to "[email protected]"
)
- database.use๋ฅผ ์ฌ์ฉํ๋ฉด
database.use {
insert("User",
"id" to 42,
"name" to "John",
"email" to "[email protected]"
)
}
- Anko๋ ํธ๋ฆฌํ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ ๊ณตํฉ๋๋ค. db.select (tableName, vararg columns)๋ฅผ ์ฌ์ฉํ์ฌ ๋ง๋ค ์ ์์ต๋๋ค. db๋ SQLiteDatabase์ ์ธ์คํด์ค์ ๋๋ค.
ํจ์ | ๊ธฐ๋ฅ |
---|---|
column(String) |
๊ฒ์์ด ์ ํํ๋ ์ด ์ถ๊ฐํ๊ธฐ |
distinct(Boolean) |
๊ณ ์ ํ ์ฟผ๋ฆฌ |
whereArgs(String) |
์์ ๋ฌธ์์ด where ์ฟผ๋ฆฌ๋ฅผ ์ง์ ํ๊ธฐ |
whereArgs(String, args) (์ค์) |
์ธ์๋ฅผ ์ฌ์ฉํ์ฌwhere ์ฟผ๋ฆฌ๋ฅผ ์ง์ ํ๊ธฐ |
whereSimple(String, args) |
? '๋งํฌ ์ธ์๋ฅผ ๊ฐ์ง where` ์ฟผ๋ฆฌ๋ฅผ ์ง์ ํ๊ธฐ |
orderBy(String, [ASC/DESC]) |
์ด ์ด์ ์์ |
groupBy(String) |
์ด ์ด์ ๊ทธ๋ฃนํํ๊ธฐ |
limit(count: Int) |
์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ํ ์ ์ ํํ๊ธฐ |
limit(offset: Int, count: Int) |
์คํ์ ์ด์๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ํ ์ ์ ํํ๊ธฐ |
having(String) |
์์ 'having'ํํ์ ์ง์ ํ๊ธฐ |
having(String, args) (์ค์) |
์ธ์๋ก `having' ํํ์์ ์ง์ ํ๊ธฐ |
- (์ค์) ๋ก ํ์๋ ํจ์๋ ํน๋ณํ ๋ฐฉ๋ฒ์ผ๋ก ์ธ์๋ฅผ ๊ตฌ๋ฌธ ๋ถ์ํฉ๋๋ค. ์ด๋ค ์์๋ก๋ ๊ฐ์ ์ ๊ณตํ๊ณ ์ํํ escaping์ ์ง์ํฉ๋๋ค.
db.select("User", "name")
.whereArgs("(_id > {userId}) and (name = {userName})",
"userName" to "John",
"userId" to 42)
- ์ฌ๊ธฐ์์ {userId} ๋ถ๋ถ์ 42, {userName}์ 'John'์ผ๋ก ๋ฐ๋๋๋ค. ํ์์ด ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐ (Int, Float ๋ฑ) ๋๋ Boolean ๊ฐ์ด๋ฉด escaped๋ฉ๋๋ค. ๋ค๋ฅธ ํ์์ ๊ฒฝ์ฐ toString() ํํ์ด ์ฌ์ฉ๋ฉ๋๋ค.
- whereSimple ํจ์๋ String ์๋ฃํ์ ํ์ฉํฉ๋๋ค. ์ด๊ฒ์ SQLiteDatavase์ query() ์ ๊ฐ์ ์ผ์ ํฉ๋๋ค. (์ง๋ฌธ ํ์ ?๋ ์๋ฃํ์ ์ค์ ๊ฐ๊ณผ ๋์ฒด๋ฉ๋๋ค.)
- ์ด ์ฟผ๋ฆฌ๋ฅผ ์ด๋ป๊ฒ ์คํํ ๊น์? exec() ํจ์๋ฅผ ์๋๋ค. Cursor. () -> T ํ์์ ํ์ฅ ๊ธฐ๋ฅ์ ํ์ฉํฉ๋๋ค. ์์ ํ ํ์ฅ ๊ธฐ๋ฅ์ ์์ํ ๋ค์ Cursor๋ฅผ ๋ซ์ ์ค์ค๋ก ์ํํ ํ์๊ฐ ์๋๋กํฉ๋๋ค.
db.select("User", "email").exec {
// Doing some stuff with emails
}
- ๊ทธ๋์ ์ฐ๋ฆฌ๋ Cursor๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ๊ทธ๊ฒ์ ์ ๊ท ํด๋์ค๋ก ์ด๋ป๊ฒ ํ์ฑํ ์ ์์๊น์?
- Anko๋ parseSingle, parseOpt ๋ฐ parseList ํจ์๋ฅผ ์ ๊ณตํ์ฌ ํจ์ฌ ์ฝ๊ฒ ์ฒ๋ฆฌ ํ ์ โโ์์ต๋๋ค.
Method | Description |
---|---|
parseSingle(rowParser): T | Parse exactly one row |
parseOpt(rowParser): T? | Parse zero or one row |
parseList(rowParser): List | Parse zero or more rows |
- ์์ ๋ Cursor๊ฐ ๋ ์ด์์ ํ์ ํฌํจํ๋ฉด parseSingle () ๋ฐ parseOpt ()๊ฐ ์์ธ๋ฅผ throwํฉ๋๋ค.
- rowParser ๋ ๋ฌด์์ผ๊น์? ๊ฐ ํจ์๋ RowParser์ MapRowParser์ธ ๋ ๊ฐ์ง ์ ํ์ ํ์๋ฅผ ์ง์ํฉ๋๋ค.
interface RowParser<T> {
fun parseRow(columns: Array<Any>): T
}
interface MapRowParser<T> {
fun parseRow(columns: Map<String, Any>): T
}
-
๋งค์ฐ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ค๋ฉด RowParser๋ฅผ ์ฌ์ฉํฉ๋๋ค (ํ์ง๋ง ๊ฐ ์ด์ ์ธ๋ฑ์ค๋ฅผ ์์์ผํฉ๋๋ค).
-
parseRow๋ Any์ ํ์ ์ ๋ฐ์๋ค์ ๋๋ค (Any ํ์ Long, Double, String ๋๋ ByteArray ์ด์ธ์ ๊ฒ์ผ ์ ์์).
-
๋ฐ๋ฉด์ MapRowParser๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ์ด๋ฆ์ ์ฌ์ฉํ์ฌ ํ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
-
Anko๋ ์ด๋ฏธ ๊ฐ๋จํ ๋จ์ผ ์ด ํ์ ๋ํ ํ์๋ฅผ ๋ณด์ ํ๊ณ ์์ต๋๋ค.
- ShortParser
- IntParser
- LongParser
- FloatParser
- DoubleParser
- StringParser
- BlobParser
-
๋ํ ํด๋์ค ์์ฑ์์์ ํ ํ์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ํด๋์ค๊ฐ ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
class Person(val firstName: String, val lastName: String, val age: Int)
- ํ์๋ ๊ฐ๋จํด์ง๋๋ค.
val rowParser = classParser<Person>()
- ํ์ฌ๋ก์๋ ๊ธฐ๋ณธ ์์ฑ์์ ์ ํ์ ๋งค๊ฐ ๋ณ์๊ฐ์๋ ๊ฒฝ์ฐ Anko๋ ์ด๋ฌํ ํ์ ์์ฑ์ ์ง์ํ์ง ์์ต๋๋ค.
- ๋ํ ์์ฑ์๋ Java Reflection์ ์ฌ์ฉํ์ฌ ํธ์ถ๋๋ฏ๋ก ์ปค๋ค๋ ๋ฐ์ดํฐ ์ธํธ์ ๊ฒฝ์ฐ ์ฌ์ฉ์ ์ ์ RowParser๋ฅผ ์์ฑํ๋ ๊ฒ์ด ๋ ํฉ๋ฆฌ์ ์ ๋๋ค.
- Anko db.select () ๋น๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ parseSingle, parseOpt ๋๋ parseList๋ฅผ ์ง์ ํธ์ถํ๊ณ ์ ์ ํ ํ์๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค.
- ์๋ฅผ ๋ค์ด, ์ด (Int, String, String)์ ๋ํด ์ ํ์๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค. ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
class MyRowParser : RowParser<Triple<Int, String, String>> {
override fun parseRow(columns: Array<Any>): Triple<Int, String, String> {
return Triple(columns[0] as Int, columns[1] as String, columns[2] as String)
}
}
- ์, ์ด์ ์ฝ๋์ 3๊ฐ์ง ๋ช ์์ ์บ์คํธ๊ฐ ์์ต๋๋ค. rowParser ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ ๊ฑฐํด๋ณด๊ฒ ์ต๋๋ค.
val parser = rowParser { id: Int, name: String, email: String ->
Triple(id, name, email)
}
- ์ด๊ฒ ๋ค ์ ๋๋ค. rowParser๋ ๋ชจ๋ ์บ์คํธ๋ฅผ ์์ฑํ๊ณ ์ํ๋๋๋ก ๋๋ค ๋งค๊ฐ ๋ณ์์ ์ด๋ฆ์ ์ง์ ํ ์ ์์ต๋๋ค.
- Anko๋ SQLite Cursor์ ๊ธฐ๋ฅ์ ์ผ๋ก ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
- cursor.asSequence () ๋๋ cursor.asMapSequence () ํ์ฅ ํจ์๋ฅผ ํธ์ถํ์ฌ ์ผ๋ จ์ ํ์ ๊ฐ์ ธ์ต๋๋ค. ์ปค์๋ฅผ ๋ซ๋ ๊ฒ์ ์์ง ๋ง์ธ์ :)
- ์ฌ์ฉ์ ์ค ํ ๋ช ์๊ฒ ์๋ก์ด ์ด๋ฆ์ ์ค๋๋ค.
update("User", "name" to "Alice")
.where("_id = {userId}", "userId" to 42)
.exec()
- ๋ํ ์ ํต์ ์ธ ๋ฐฉ์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์ ๊ณตํ๋ ค๋ ๊ฒฝ์ฐ update์๋ whereSimple () ๋ฉ์๋๊ฐ ์์ต๋๋ค.
update("User", "name" to "Alice")
.`whereSimple`("_id = ?", 42)
.exec()
- ํ์ ์ญ์ ํด ๋ด ์๋ค (delete ๋ฉ์๋์๋ whereSimple () ๋ฉ์๋๊ฐ ์์ผ๋ฉฐ, ๋์ ์ธ์์ ์ง์ ์ฟผ๋ฆฌ๋ฅผ ์ ๊ณตํฉ๋๋ค).
val numRowsDeleted = delete("User", "_id = {userID}", "userID" to 37)
- transaction ()์ด๋ผ๋ ํน๋ณํ ํจ์๊ฐ ์๋๋ฐ, ์ฌ๋ฌ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ์ฐ์ ํ๋์ SQLite ํธ๋์ญ์ ์ผ๋ก ๋ฌถ์ ์ ์์ต๋๋ค.
transaction {
// Your transaction code
}
- {} ๋ธ๋ก ๋ด์ ์์ธ๊ฐ ๋ฐ์ํ์ง ์์ผ๋ฉด ํธ๋์ญ์ ์ ์ฑ๊ณต์ผ๋ก ํ์๋ฉ๋๋ค.
- ์ด๋ค ์ด์ ๋ก ํธ๋์ญ์ ์ ์ค๋จํ๋ ค๋ฉด TransactionAbortException์ throw ํ์ธ์. ์ด ๊ฒฝ์ฐ์๋์ด ์์ธ๋ฅผ ์ง์ ์ฒ๋ฆฌ ํ ํ์๊ฐ ์์ต๋๋ค.
- build.gradle์ dependency์ anko-coroutines๋ฅผ ์ถ๊ฐํฉ๋๋ค.
dependencies {
implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
}
- ๋น๋๊ธฐ API๊ฐ ์ทจ์๋ฅผ ์ง์ํ์ง ์์ผ๋ฉด ์ฝ๋ฃจํด์ด ๋ฌด๊ธฐํ ์ ์ง ๋ ์ ์์ต๋๋ค.
- ์ฝ๋ฃจํด์ ์บก์ฒ ๋ ๊ฐ์ฒด์ ๋ํ ๊ฐ๋ ฅํ ์ฐธ์กฐ๋ฅผ ๋ณด์ ํ๋ฏ๋ก Activity ๋๋ Fragment ์ธ์คํด์ค์ ์ธ์คํด์ค๋ฅผ ์บก์ฒํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ์ง์ ์บก์ฒ ๋์ asReference ()๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
suspend fun getData(): Data { ... }
class MyActivity : Activity() {
fun loadAndShowData() {
// Ref<T> uses the WeakReference under the hood
val ref: Ref<MyActivity> = this.asReference()
async(UI) {
val data = getData()
// Use ref() instead of this@MyActivity
ref().showData(data)
}
}
fun showData(data: Data) { ... }
}
- bg ()๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์ฝ๋๋ฅผ ์ฝ๊ฒ ์คํํ ์ ์์ต๋๋ค.
fun getData(): Data { ... }
fun showData(data: Data) { ... }
async(UI) {
val data: Deferred<Data> = bg {
// Runs in background
getData()
}
// This code is executed on the UI thread
showData(data.await())
}