๋ฐ๊ฒฝ์ก์ E.B
Android <5> Layouts & Tip(ํ) ๊ณ์ฐ๊ธฐ๐ข ๋ณธ๋ฌธ
๐ฝ๋ฏธ๋ฆฌ ์๊ณ ๋ค์ด๊ฐ๊ธฐ
Android ์ฑ์ UI๋ ๊ตฌ์ฑ์์(์์ ฏ)์ ํฌํจ UI ๊ณ์ธต ๊ตฌ์กฐ์ ์ด๋ฌํ ๊ตฌ์ฑ์์์ ํ๋ฉด ๋ ์ด์์์ผ๋ก ๋น๋๋จ.
*UI ์์์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ
๊ฐ UI ์์๋ XML ํ์ผ์ XML ์์๋ก ํํ + ๊ฐ ์์๋ ํ๊ทธ๋ก ์์ํด์ ํ๊ทธ๋ก ๋๋จ.
*์์ ํ๋์ ๋ถ๋ถ์ ์๋์ ๊ฐ์ด ์ ์ ์๋ ์๋ค! (ํ๊ทธ์ ์ฐจ์ด)
<TextView
android:text="Hello World!" />
์์ : TextView ์๋ Button์ ํ์ ์์๋ก ์ถ๊ฐํ๋ ๊ฒฝ์ฐ
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:text="Hello World!" />
<Button
android:text="Calculate" />
</androidx.constraintlayout.widget.ConstraintLayout>
ConstraintLayout ํ๊ทธ์ ๊ฒฝ์ฐ ์์ ๊ฐ์ด ๊ธธ๊ฒ ํ์๋๋ ์ด์
: ConstraintLayout์ด ํต์ฌ Android ํ๋ซํผ ์ธ์๋ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ฝ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํฌํจ๋ Android Jetpack์ ์ผ๋ถ์ด๊ธฐ ๋๋ฌธ
์ค๋์ ๊ณผ์ : ๐ขํ ๊ณ์ฐ๊ธฐ ์ฑ ๋ง๋ค๊ธฐ ๐ข
๐ข ์๋น์ค ๋น์ฉ ํ ์คํธ ์ ๋ ฅ๋ ์ถ๊ฐํ๊ธฐ
: EditText ์์ = ์ฌ์ฉ์๊ฐ ์ฑ์์ ํ ์คํธ๋ฅผ ์ ๋ ฅํ๊ฑฐ๋ ์์ ํ ์ ์๋ ์์
<!-- ์ฌ์ฉ์๊ฐ ์ฑ์์ ํ
์คํธ๋ฅผ ์
๋ ฅ/์์ ํ ์ ์๋ ์์+์ ์ฝ์กฐ๊ฑด-->
<EditText
android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
๐ค์ ์ฝ์กฐ๊ฑด์ ์ด๋ฆ
: layout_constraint<Source>_to<Target>Of ํ์
Source - ํ์ฌ ๋ทฐ, Target - ํ์ฌ ๋ทฐ๊ฐ ์ ํ๋๋ ํ๊ฒ ๋ทฐ(์์ ์ปจํ ์ด๋ ๋๋ ๋ค๋ฅธ ๋ทฐ)์ ๊ฐ์ฅ์๋ฆฌ
๐ค๋ฆฌ์์ค ID
: XML ํ์ผ์ ์๋ก์ด ๋ทฐ ID๋ @+id ์ ๋์ฌ๋ก ์ ์ํด์ผ ํ๊ณ ์ด ์ ๋์ฌ๋ Android ์คํ๋์ค์ ์ด ID๋ฅผ ์ ๋ฆฌ์์ค ID๋ก ์ถ๊ฐํ๋ผ๊ณ ์ง์.
-> ์ฑ์ฝ๋์์ ๋ฆฌ์์คID๋ฅผ ์ฐธ์กฐํ ๋๋ R.<type>.<name> ์ฌ์ฉ (ex. R.id.button)
*์์ฑ ์์ ํ๊ธฐ
<EditText
android:id="@+id/cost_of_service"
android:layout_height="wrap_content"
android:layout_width="160dp" <- ๊ณ ์ ๋๋น๋ก ๋ณ๊ฒฝ
android:inputType="numberDecimal" <- ์ซ์๋ง ์
๋ ฅ๋ฐ๊ธฐ
android:hint="Cost of Service" <--์ฌ์ฉ์์๊ฒ ์
๋ ฅ๋์ ๋ํ ํํธ ์ฃผ๊ธฐ
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
โ ๏ธ๊ฐ๋ถ ์๋ฌ ๋ฐ์๊ณผ ํด๊ฒฐ ..
โ ๏ธ An issue was found when checking AAR metadata:
1. Dependency 'androidx.activity:activity:1.8.0' requires libraries and applications that depend on it to compile against version 34 or later of the Android APIs. :app is currently compiled against android-33. Recommended action: Update this project to use a newer compileSdk of at least 34, for example 34.
Note that updating a library or application's compileSdk (which allows newer APIs to be used) can be done separately from updating targetSdk (which opts the app in to new runtime behavior) and minSdk (which determines which devices the app can be installed on).
-> ์คํํ๋๋ ์๋ฐ ์๋ฌ ๋ฐ์ !
: ์ฐพ์๋ณด๋ androidx.activity:activity ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ต์ ๋ฒ์ ์ด ์๋๋ก์ด๋ API ๋ ๋ฒจ 34 ์ด์์ ํ์๋ก ํ๋๋ฐ, ํ์ฌ ํ๋ก์ ํธ๊ฐ ์ฌ์ฉ ์ค์ธ ์๋๋ก์ด๋ API ๋ ๋ฒจ์ด 33์ด์ด์ ๋ฐ์ํ ๋ฌธ์ ๋ผ๊ณ ํจ
app > Gradle Scripts > build.gradle.kt(:app) ํ์ผ์์
compileSdk = 33
์ผ๋ก ๋ผ์๋ ๋ถ๋ถ์ 34๋ก ๋ณ๊ฒฝ ํ, sync now ๋ฒํผ์ ๋๋ฅด๊ณ ์ฌ์คํํ๋ฉด ํด๊ฒฐ๋จ!
๐ข ์๋น์ค์ ๋ํ ์ง๋ฌธ ์ถ๊ฐํ๊ธฐ
<TextView
android:id="@+id/service_question"
android:layout_width="wrap_content" <- Textview๊ฐ ๋ด๋ถ์ ํ
์คํธ ์ฝํ
์ธ ๋งํผ๋ง ํฌ๋ฉด ๋๊ธฐ ๋๋ฌธ!
android:layout_height="wrap_content"
android:text="์๋น์ค๋ ์ด๋ ์
จ๋์?"
<!-- ์ ์ฝ์กฐ๊ฑด ์ถ๊ฐํ๊ธฐ -->
app:layout_constraintStart_toStartOf="parent" <- ์์ ๊ฐ์ฅ์๋ฆฌ๋ฅผ ์์ ์์์ ์์ ๊ฐ์ฅ์๋ฆฌ๋ก
app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>
<- ์๋จ ๊ฐ์ฅ์๋ฆฌ๋ฅผ ์๋น์ค๋น์ฉview์ ๊ฐ์ฅ์๋ฆฌ๋ก ์ ํ
๐ข ํ ์ต์ ์ถ๊ฐํ๊ธฐ - ์ ์ ํ ์์ ์ฌ์ฉํ๊ธฐ
: ์ต์ 3๊ฐ์ง ์ถ๊ฐํ๊ธฐ - ํ๋ฃกํจ(20%), ์ข์(18%), ๊ด์ฐฎ์(15%)
--๊ตฌ์กฐ--
RadioGroup : ์์ ๋ทฐ
| -- RadioButton : ํ์ ๋ทฐ
<RadioGroup
android:id="@+id/tip_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/service_question"> <-์๋น์ค ์ง๋ฌธ ์๋ ๋ฐฐ์น
</RadioGroup>
<RadioButton
android:id="@+id/option_twenty_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="์ต๊ณ ! (20%)" />
<RadioButton
android:id="@+id/option_eighteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="์ข์์ (18%)" />
<RadioButton
android:id="@+id/option_fifteen_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="๊ด์ฐฎ์์ด์ (15%)" />
๐ค๊ธฐ๋ณธ ์ ํ ์ถ๊ฐํ๊ธฐ
* RadioGroup์๋ ์ฒ์์ ์ ํํด์ผ ํ๋ ๋ฒํผ์ ์ง์ ํ ์ ์๋ ์์ฑ์ด ์์
= checkButton : ์ ํํ๊ธธ ๋ฐ๋ผ๋ ๋ผ๋์ค ๋ฒํผ์ ๋ฆฌ์์ค ID๋ก ๊ฐ์ ์ค์
android:id="@+id/tip_options"
android:checkedButton="@id/option_twenty_percent"
๊ฒฐ๊ณผ๋ฌผ! ์ ๊ท์ฝ๊ตฐ
๐ข ํ์ ๋ฐ์ฌ๋ฆผํ๊ธฐ ์ํ Switch ์์ ฏ ์ถ๊ฐํ๊ธฐ
<Switch
android:id="@+id/round_up_switch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:checked="true" <-- ๊ธฐ๋ณธ๊ฐ์ true๋ก ์ค์
android:text="ํ์ ๋ฐ์ฌ๋ฆผํ ๊น์?"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tip_options"
app:layout_constraintTop_toBottomOf="@id/tip_options" />
๐ข ๊ณ์ฐ ๋ฒํผ ์ถ๊ฐํ๊ธฐ
<Button
android:id="@+id/calculate_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="๊ณ์ฐํ๊ธฐ"
app:layout_constraintTop_toBottomOf="@id/round_up_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
๐ค View์ ๋๋น๋ฅผ ํฌํจ๋๋ ConstraintLayout์ ๋๋น์ ๊ฐ๊ฒ ํ๋ ค๋ฉด ์์๊ณผ ๋์ ์์ ์์์ ์์๊ณผ ๋์ผ๋ก ์ ํํ๊ณ ๋๋น๋ฅผ 0dp๋ก ์ค์ ํฉ๋๋ค!
๐ขํ ๊ฒฐ๊ณผ ์ถ๊ฐํ๊ธฐ
<TextView
android:id="@+id/tip_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/calculate_button"
android:text="Tip Amount" />
ํ ๊ณ์ฐํ๋ ๊ธฐ๋ฅ ๊ตฌํํ๊ธฐ
๐งโ๏ธ๋ทฐ ๊ฒฐํฉ ์ฌ์ฉ ์ค์
: ํ์ ๊ณ์ฐํ๋ ค๋ฉด ์ฝ๋๊ฐ ๋ชจ๋ UI ์์์ ์์ธ์คํ์ฌ ์ฌ์ฉ์์ ์ ๋ ฅ์ ์ฝ์ด์ผ ํจ
-> ๋ทฐ์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฐํํ๋ ์์ ์คํ (findViewById()๋ณด๋ค ๊ฐ๋จํ ๊ธฐ๋ฅ)
build.gradle ํ์ผ์ android ์น์ ์ ๋ด์ฉ ์ถ๊ฐ
buildFeatures {
viewBinding = true
}
๐งโ๏ธ๊ฒฐํฉ ๊ฐ์ฒด ์ด๊ธฐํ
: ์ฑ์ ๊ฐ View๋ง๋ค findViewById()๋ฅผ ํธ์ถํ๋๋์ , View Binding์ผ๋ก ๊ฒฐํฉ ๊ฐ์ฒด๋ฅผ ํ ๋ฒ ๋ง๋ค๊ณ ์ด๊ธฐํ !
MainActivity.kt ์ฝ๋ ์์ ํ๊ธฐ -> *ActivityMainBinding import๋ฌธ๋ ์ถ๊ฐํด์ผ ํจ!
package com.myfirstandroidapp.realtiptime
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.myfirstandroidapp.realtiptime.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding // ๊ฒฐํฉ ๊ฐ์ฒด์ ์ต์์ ๋ณ์ ์ ์ธ
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) // activity_main.xml ๋ ์ด์์์์
Views์ ์์ธ์คํ๋ ๋ฐ ์ฌ์ฉํ binding ๊ฐ์ฒด ์ด๊ธฐํ
setContentView(binding.root) // ํ๋์ ์ฝํ
์ธ ๋ทฐ ์์ฑ. ์ฑ์ ๋ทฐ ๊ณ์ธต ๊ตฌ์กฐ ๋ฃจํธ๋ก ์ง์
}
}
๐ค์ง๋ ๋ฒ์ ์ฃผ์ฌ์ ๊ตด๋ฆฌ๋ ์ฑ์ ๋ง๋ค ๋๋ View์ ๋ํ ์ฐธ์กฐ๊ฐ ํ์ํ ๋ findViewById()๋ฅผ ๋งค๋ฒ ํธ์ถํด์ ์ฌ์ฉํ๋๋ฐ,
์ด์ ๊ทธ๋ด ํ์ ์์ด ์ง์ view ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ง ํ๋ฉด ๋๋ค๋ ์๊ธฐ ๊ฐ๋ค !!!!!!!!!!!!!
*why?
: binding ๊ฐ์ฒด๋ ID๊ฐ ์๋ ์ฑ์ ๋ชจ๋ View๋ฅผ ์ํ ์ฐธ์กฐ๋ฅผ ์๋์ผ๋ก ์ ์ํ๊ธฐ ๋๋ฌธ์! View๋ฅผ ์ํ ์ฐธ์กฐ๋ฅผ ์ ์งํ ๋ณ์ ์์ฑ์ด ๋ถํ์ํ๊ธฐ ๋๋ฌธ!
package com.myfirstandroidapp.realtiptime
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.myfirstandroidapp.realtiptime.databinding.ActivityMainBinding
import java.text.NumberFormat
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.calculateButton.setOnClickListener{ calculateTip() }
}
fun calculateTip() {
//์๋น์ค ๋น์ฉ์ ํ
์คํธ๋ถ๋ถ์ ๊ฐ์ ธ์ค๊ณ ,์ญ์ง์๋ก ๋ณํ
// toDouble()์ String์์ ํธ์ถ๋์ด์ผ ํจ : text -> String -> Double
val stringInTextField = binding.costOfService.text.toString()
val cost = stringInTextField.toDouble()
//ํ ๋น์จ ๊ฐ์ ธ์ค๊ธฐ
val selectedId = binding.tipOptions.checkedRadioButtonId
val tipPercentage = when (selectedId) {
R.id.option_twenty_percent -> 0.20
R.id.option_eighteen_percent -> 0.18
else -> 0.15
} // when๋ฌธ์๋ ๋ฌด์กฐ๊ฑด else๊ฐ ํ์ํจ!
var tip = tipPercentage * cost
// ์ฌ์ฉ์์ switch ์ต์
์ ํ์ ๋ฐ๋ผ ๊ฐ์ด ๋ฐ์ฌ๋ฆผ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋ ์ ์์
//-> val์ด ์๋๋ผ var
val roundUp = binding.roundUpSwitch.isChecked
//switch๊ฐ ์ฌ์ฉ ์ค์ ์ํ์ธ์ง ํ์ธ
if (roundUp) { //roundUp์ด ์ฐธ์ธ ๊ฒฝ์ฐ
tip = kotlin.math.ceil(tip) //tip๋ณ์์ tip์ ์ํ ํ ๋น
}
//์ซ์ ํ์ ์ง์ ํด๋์ค ์ฌ์ฉ
val formattedTip = NumberFormat.getCurrencyInstance().format(tip)
}
}
๐งโ๏ธํ ํ์
*app>res>values>strings.xml ํ์ผ์ ์ถ๊ฐ
<string name="app_name">Real Tip Time: %s</string>
</resources>
* %s๋ ํ์์ด ์ง์ ๋ ํตํ๊ฐ ์ฝ์ ๋๋ ์์น์ด๋ค!
*calculateTip() ์์ ๋ฐํ๋๋ ๊ฐ์ ํ ๊ฒฐ๊ณผ TextView์ text ์์ฑ์ ํ ๋นํ๊ธฐ
binding.tipResult.text = getString(R.string.tip_amount, formattedTip)
*app > res > layout > activity_main.xml ํ์ผ์ android:text๋ฅผ tools:text -- ๋ก ์์
โ ๏ธ์ ๋ ๋ฌด์จ ์ด์ํ ์ค๋ฅ๋ฌ๋๋ฐ ๋ณด๋๊น? ์ด ํ์ผ์์ string/app_name ๋ฆฌ์์ค๋ฅผ ์ฐพ์ ์ ์๋์ฅ..
=> AndroidManifest.xml ํ์ผ์์ android:label ์, ์์ string.xml์์ ๋ฐ๊พผ ์ด๋ฆ์ธ tip_amount๋ฅผ ์ ๋๋ก ์ ์ด์ค์ผ ํจ!
๊ฒฐ๊ณผ๋ฌผ!!!!!!!!!! - ์๋ ์ ๊ฐ์๊ธฐ ํ๋ฉด ์บก์ณ๊ฐ ์๋๋์ง .. ใ ๋ชจ๋ฅด๊ฒ ์์
์๋ฌดํผ ์ ์๋ํ๋ค๋ >>..!
'GDSC > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Android <4> Build your first Android app: [Dice Roller๐ฒ] (1) | 2023.11.17 |
---|---|
Android <3> Classes and objects (1) | 2023.11.03 |
Android <2> More About Kotlin Functions (1) | 2023.10.14 |
Android <2> Kotlin Functions - (Almost) Everything has a value! (0) | 2023.10.07 |
Android <1> Kotlin Basic - Benefits of Kotlin (0) | 2023.09.18 |