이번 포스팅에서는 Kotlin 기초를 마무리 한 상태에서 직접 앱을 개발해보며 하나씩 내용 확인을 하도록 하겠습니다.
개발할 앱은 여러 단위 프로젝트를 통해서 만들어 볼 예정입니다.
여러 단위 프로젝트로 포스팅이 모두 완료가 된다면 실제 서비스를 만들어가는 과정을 포스팅하며 정리해보도록 하겠습니다.
우선 이번 포스팅에서는 응급진료기록을 가져오는 부분을 할 건데 세 차례에 걸쳐서 핵심 개념들을 나눠 설명하도록 하겠습니다.
1. style 구성을 두어서 xml 내용을 공통적으로 처리한다.
xml을 구성하다보면 공통된 style을 쓸 때가 있습니다. 이 부분을 동일하게 구성하도록 셋팅하는 방법입니다.
이렇게 리소스 파일을 만드는데 values 폴더안에서 style.xml을 만듭니다.
style.xml에서는 공통적으로 사용하는 속성에 대해서 셋팅하면 되는데, 이번 단위 프로젝트에서는 Texview를 공통적으로 쓸 것이고
textColor, textSize 등 공통적으로 속성을 쓸 것이기 때문에 해당 내용들을 공통 모듈로 만들어보겠습니다.
[style.xml]
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Title" parent="Widget.AppCompat.TextView">
<item name="android:textColor">@color/brown</item>
<item name="android:textSize">24sp</item>
<item name="android:layout_marginTop">36dp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Value" parent="Title">
<item name="android:textColor">@color/black</item>
<item name="android:textSize">20sp</item>
<item name="android:textStyle">bold</item>
<item name="android:gravity">end</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
<item name="android:layout_width">0dp</item>
</style>
</resources>
리소스 태그 내 첫 번째 스타일은 TextView를 부모로 갖는 스타일이며 TextView 속성의 textColor, textSize, layout_marginTop, textStyle 이 네 가지에 대해서 정의했습니다.
따라서 다음 style name이 Title인 style로 쓰게되면 다음 내용들이 적용되는 것입니다.
아래 style name이 Value인 style은 parent로 Title을 상속 받았기 때문에 Title인 Style을 그대로 사용할 수 있고
더불어 Value 내에 셋팅한 item들로 적용되어 사용할 수 있습니다.
[activity_main.xml]
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/txtName"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:text="이름"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/name"
style="@style/Value"
android:layout_height="wrap_content"
android:layout_marginEnd="50dp"
android:text="함성호"
app:layout_constraintBaseline_toBaselineOf="@+id/txtName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtBirthDate"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="생년월일"
app:layout_constraintStart_toStartOf="@+id/txtName"
app:layout_constraintTop_toBottomOf="@+id/txtName" />
<TextView
android:id="@+id/birthDate"
style="@style/Value"
android:layout_height="wrap_content"
android:text="1992.07.04"
app:layout_constraintBaseline_toBaselineOf="@+id/txtBirthDate"
app:layout_constraintEnd_toEndOf="@+id/name"
app:layout_constraintStart_toStartOf="@id/guideline" />
<TextView
android:id="@+id/txtBloodType"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="혈액형"
app:layout_constraintStart_toStartOf="@+id/txtName"
app:layout_constraintTop_toBottomOf="@+id/txtBirthDate" />
<TextView
android:id="@+id/bloodType"
style="@style/Value"
android:layout_height="wrap_content"
android:text="O형"
app:layout_constraintBaseline_toBaselineOf="@id/txtBloodType"
app:layout_constraintEnd_toEndOf="@+id/name"
app:layout_constraintStart_toStartOf="@id/guideline" />
<TextView
android:id="@+id/txtPhoneBook"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="연락처"
app:layout_constraintStart_toStartOf="@+id/txtName"
app:layout_constraintTop_toBottomOf="@+id/txtBloodType" />
<TextView
android:id="@+id/phoneBook"
style="@style/Value"
android:layout_height="wrap_content"
android:text="010-4618-2721"
app:layout_constraintBaseline_toBaselineOf="@+id/txtPhoneBook"
app:layout_constraintEnd_toEndOf="@+id/name"
app:layout_constraintStart_toStartOf="@id/guideline" />
<TextView
android:id="@+id/txtEtc"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="주의사항"
app:layout_constraintStart_toStartOf="@+id/txtName"
app:layout_constraintTop_toBottomOf="@+id/txtPhoneBook" />
<TextView
android:id="@+id/etc"
style="@style/Value"
android:layout_height="wrap_content"
android:text="주의사항 값"
app:layout_constraintBaseline_toBaselineOf="@+id/txtEtc"
app:layout_constraintEnd_toEndOf="@+id/name"
app:layout_constraintStart_toStartOf="@id/guideline" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.4" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:layout_marginBottom="50dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@android:drawable/ic_menu_edit" />
</androidx.constraintlayout.widget.ConstraintLayout>
다음처럼 셋팅하게 되면 style="@style/Title" 또는 style="@style/Value" 로 셋팅하기 때문에 앞서 설정한 내용들이 공통적으로
적용되어 보여지게 됩니다.
이렇듯, xml에서 쓰는 공통적인 내용은 res/values에서 공통 모듈로 셋팅할 수 있습니다.
다음은 리소스 아이디를 통해 찾아서 하는 것이 아니라 ViewBinding을 통해 객체를 수신하는 방법을 설명하겠습니다.
우선 build.gradle(app)에 android { } 블록안에다가 viewBinding { enable = true } 를 적고 동기화를 진행합니다.
이렇게 진행하면 findViewById를 통해 아이디를 찾던 것을 ViewBinding을 통해 간단히 찾을 수 있으며,
코드가 간소화되고 효율적이며 ID값을 일일히 기억하지 않아도 자동적으로 찾을 수 있습니다.
또한 ViewBinding은 내부적으로 LayoutInflater를 가지고 있기 때문에 inflate만 진행하면 자동으로 xml에
바인딩 될 수 있고, 바인딩 객체를 만들 때에는 xml의 이름에 따라서 카멜문자로 셋팅됩니다.
[MainActivity.kt]
package com.example.chapter4
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.chapter4.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MainActivity"
}
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
binding.floatingActionButton.setOnClickListener {
var intent: Intent = Intent(this, InputActivity::class.java).apply {
putExtra("IntentMessage","응급의료정보")
}
Log.d(TAG, intent.getStringExtra("IntentMessage").toString())
startActivity(intent)
}
}
}
다음 코드에서 확인이 가능하듯, ActivityMainBinding은 activity_main.xml을 토대로 바인딩하기에
이름이 이렇게 되는 것이고, 내부적으로 LayoutInflater가 존재하므로 inflate가 진행되게 됩니다.
또한 setContentView에는 binding에 root를 넣어주면 됩니다.
내부에 설정한 위젯 접근은 binding.객체ID 로 접근하면 되는데, binding.floatingActionButton 처럼 진행하면 됩니다.
다음 플로팅 버튼을 선택했을경우, 다른 액티비티로 Intent를 통해 이동하는 것을 하고자 하므로 다음처럼 정의했습니다.
binding.floatingActionButton.setOnClickListener {
var intent: Intent = Intent(this, InputActivity::class.java).apply {
putExtra("IntentMessage","응급의료정보")
}
Log.d(TAG, intent.getStringExtra("IntentMessage").toString()
startActivity(intent)
}
Intent 내 첫번째 인자는 현재 액티비티이고 다음 인자는 이동할 액티비티 입니다.
::class.java로 접근하는 것이 조금 생소할 수 있으니 익혀두면 좋겠습니다.
더불어 apply는 확장함수로 객체 자체에 값을 셋팅하는 것이기 때문에 Intent에다가 putExtra로 메시지를 정의해 보냅니다.
다음 내용을 받는 InputActivity에서도 값이 String이니까 intent.getStringExtra로 보낸 메시지를 확인할 수 있습니다.
[InputActivity.kt]
package com.example.chapter4
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.chapter4.databinding.InputActivityBinding
class InputActivity : AppCompatActivity() {
companion object {
const val TAG = "InputActivity"
}
private lateinit var binding: InputActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = InputActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d(TAG, intent.getStringExtra("IntentMessage").toString())
}
}
이 결과는 아래와 같습니다.
플로팅 버튼을 선택했을 경우 찍히는 로그로 확인한 결과입니다.
Intent에는 명시적/암시적이 있고 그 내용도 다양하며 사용처가 많으므로 추후에 다시 정리해보도록 하겠습니다.
복습 차원에서 Intent 관련 Document도 참조하겠습니다. 감사합니다.
https://developer.android.com/reference/android/content/Intent
'Android' 카테고리의 다른 글
+/- 만 구현된 간단한 계산기앱(Decimal Format, Flow, StringBuilder) (0) | 2024.07.23 |
---|---|
Layer / DatePickerDialog / Spinner / SharedPreference (6) | 2024.07.22 |
카운트를 측정하는 앱 - 화면전환 (0) | 2024.07.19 |
Kotiln 기초 3편 (3) | 2024.07.16 |
Kotlin 기초 2편 (0) | 2024.07.11 |