Part1_Ch04_05 응급의료 정보 UI 그리기 (3)
목표
이번 소단원에서는, 이전 소단원 에서 구현하지 못한
- Spinner ( A / B / AB / O ) 를 통해 선택된 혈액형 표시하기
- Calendar 연결
- CheckBox 를 통해 주의사항의 Visibility 관리
이렇게 위 세 가지 UI 를 구현해보도록 할 것이다.
1) Spinner 에 adapter 연결
1-1) Spinner 와 adapter
Spinner 는
삼각형 버튼을 누르면, 리스트로 데이터가 나오고
그 데이터를 선택하면, 선택한 값을 노출시키는 역할이다.
개발하면서 정말 자주 마주하는 문제 중 하나가
반복을 정말 싫어한다는 것이다.
만약 Spinner를 xml 로 그려야한다면
xml 코드로 리스트 모양을 하나하나 그려야한다.
이는 있을 수 없는 일이다.
리스트형 데이터를 UI로 연결해주는 역할을 해주는 것이 바로 adapter 이다.
adapter 를 통해서 bloodTypeSpinner 에
A, B, AB, O 이 4가지를 리스트로 넣어주고,
그 중 하나를 선택했을 때
Spinner 에게 얘를 선택했어-라는 것을 알려주는 코드를 작성해볼 것이다.
1-2) InputActivity.kt
InputActivity.kt
class InputActivity : AppCompatActivity() {
private lateinit var binding: ActivityInputBinding
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
binding = ActivityInputBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.bloodTypeSpinner.adapter = ArrayAdapter.createFromResource(
/* context = */
this,
)
}
ViewBinding 을 한 후
bloodTypeSpinner 에 adapter 를 연결하려고 한다.
우리는 ArrayAdapter 의 createFromResource 를 연결할 것인데,
이의 인자를 살펴보면
context
textArrayResId
textViewResID
이렇게 3가지를 인자로 한다.
두 번째 인자인 textArrayResId 를 위해
arrays.xml 리소스 파일을 생성해보자.
1-2) arrays.xml 리소스 파일
New > Android Resource File > name: arrays
리소스 파일을 추가해보자.
RadioButton 에서 <item>을 통해 요소를 넣어주었던 것 처럼
string-array 에서도 <item>을 통해 요소를 넣어준다.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="blood_types">
<item>A</item>
<item>B</item>
<item>O</item>
<item>AB</item>
</string-array>
</resources>
1-3) InputActivity.kt 에서 adapter 연결 완료
다시 InputActivity.kt 의
ArrayAdaptor 의 createFromResource 의 인자를 채우러 가보자.
textViewResID 는 이미 안드로이드에서 만들어서 제공하는
간단한 리스트인 simple_list_item_1 을 사용하자.
binding.bloodTypeSpinner.adapter = ArrayAdapter.createFromResource(
this,
R.array.blood_types,
android.R.layout.simple_list_item_1
)
simple_list_item_1 를 살펴보기 위해
글자 위에 커서를 올리고, command 를 누른 채로 클릭 을 해보자.
살펴보니 단순한 TextView 이다.
여기에서 우리가 선택한 text 를 넣을 것이다.
여기까지 완료하면 아래와 같이
Spinner 에 요소가 들어가있는 것을 확인할 수 있다.
1-4) Spinner 공간 확보
현재 Spinner 에서 혈액형을 선택했을 때
옆에 혈액형이 표시되지 않는다.
따라서 Spinner 공간을 확보하기 위해
activity_input.xml 파일을 수정해보자.
- 1-4-1) RadioGroup 에서gravity 를 지우고
- End_toStartOf 를 지정하지 않고 지운다.
- layout_width 를 wrap_content 로 변경하고
<!-- Rh 타입 선택하는 RadioGroup 과 RadioButton -->
<RadioGroup
android:id="@+id/bloodTypeRadioGroup"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/bloodtypeTextView"
app:layout_constraintStart_toStartOf="@+id/guideLine"
app:layout_constraintTop_toTopOf="@id/bloodtypeTextView">
- 1-4-2) Spinner 에서
Spinner 의 공간을 최대한 활용하기 위해서
layout_width 를 0dp 로 변경하고
왼쪽 constraint 를 최대한 왼쪽으로 맞추자.
<!-- 실제 혈액형 선택하는 Spinner -->
<Spinner
android:id="@+id/bloodTypeSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@id/bloodtypeTextView"
app:layout_constraintEnd_toEndOf="@id/nameEditText"
app:layout_constraintStart_toEndOf="@+id/bloodTypeRadioGroup"
app:layout_constraintTop_toTopOf="@id/bloodtypeTextView" />
결과
Spinner 에서 혈액형 선택 시, 오른쪽에 표시된다.
2) Calendar 연결
2-1) layer 로 두 요소 묶기
날짜 또는 버튼 이미지를 선택했을 때
캘린더를 띄워볼 것이다.
날짜나 버튼이미지 어느 부분이든 클릭해도 되도록
이 두 개를 묶는 layer 를 만들어보자.
Palette 에서 command ( 또는 ctrl ) 을 누른 채로
두 요소를 클릭하여 선택하자.
선택된 상태에서 마우스 우클릭 > Add helpers > Layer 로 층을 추가한다.
activity_input.xml
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/birthdateLayer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="birthdateValueTextView,birthdateImageView"
tools:ignore="MissingConstraints" />
2-2) birthdateLayer 에 ClickListener 추가
InputActivity.kt
binding.birthdateLayer.setOnClickListener{
val listener = OnDateSetListener { _, year, month, dayOfMonth ->
binding.birthdateTextView.text = "$year-$month-$dayOfMonth"
}
DatePickerDialog(
this,
listener,
2000,
1,
1
).show()
}
content : this
listener : 사용자가 선택한 날짜를 받아주는 역할
초기날짜 설정 : 2000년 1월 1일
DatePickerDialog 에서 show() 를 하지 않으면
DatePickerDialog 를 만들어놓기만 한 것이고 보이지 않는다.
실행해보면,
내가 선택한 날짜는 1998-2-6 인데
보이는 날짜는 1998-1-6 이다.
안드로이드에서 제공하는 캘린더에 나오는 달(month)은 하나를 더 추가해주어야 날짜가 정확히 나온다.
따라서 아래 코드를 변경하자.
변경 전: binding.birthdateTextView.text = "$year-$month-$dayOfMonth"
변경 후: binding.birthdateTextView.text = "$year-${month.inc()}-$dayOfMonth"
3) CheckBox 를 통해 주의사항의 Visibility 관리
// 체크박스
binding.warningCheckBox.setOnCheckedChangeListener{ _, isChecked ->
binding.warningEditText.isVisible = isChecked
}
체크박스 선택이 되면, 주의사항이 보이고
체크박스가 선택 되지 않으면, 주의사항이 안 보인다.
( 주의사항 노출 글자 길이는 다음 단원에서 수정할 것이다. )
다만, 초반에는 선택 여부에 따라서, 노출 여부가 적용되지 않는다.
값이 변경 되었을 때만, 화면에 노출여부를 선택하게 했기 때문이다.
기본적으로 선택되지 않은 것이 기본값이다.
처음에 선택이 되었는지 여부에 따라
노출여부를 한 번 더 설정해주어야 한다.
// 체크박스
binding.warningCheckBox.setOnCheckedChangeListener{ _, isChecked ->
binding.warningEditText.isVisible = isChecked
}
// 초기 선택여부
binding.warningEditText.isVisible = binding.warningCheckBox.isChecked
추가 구현할 기능
현재까지는 선택한 정보가 이전 화면에 보이지 않는다.
이 문제를 해결하기 위해서
사용자가 입력한 값을 intent 를 통해서 데이터를 보낼 수는 있다.
그러나 intent 는
Activity 와 Activity 사이에 데이터를 주고받는 역할밖에 하지 못한다.
어딘가에 데이터를 저장하지 않았기 때문에
앱을 껐다가 켜면, 데이터가 사라진다.
다음 단원에서는 입력한 정보를 저장하고 데이터를 불러오는 것을 할 것이다.
전체코드
InputActivity.kt
package com.part1.chapter4
import android.app.DatePickerDialog
import android.app.DatePickerDialog.OnDateSetListener
import android.os.Bundl
import android.os.PersistableBundle
import android.util.Log
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.part1.chapter4.databinding.ActivityInputBinding
class InputActivity : AppCompatActivity() {
private lateinit var binding: ActivityInputBinding
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
binding = ActivityInputBinding.inflate(layoutInflater)
setContentView(binding.root)
// 스피너
binding.bloodTypeSpinner.adapter = ArrayAdapter.createFromResource(
this,
R.array.blood_types,
android.R.layout.simple_list_item_1
)
// 캘린더
binding.birthdateLayer.setOnClickListener{
val listener = OnDateSetListener { _, year, month, dayOfMonth ->
binding.birthdateTextView.text = "$year-${month.inc()}-$dayOfMonth"
}
DatePickerDialog(
this,
listener,
2000,
1,
1
).show()
}
// 체크박스
binding.warningCheckBox.setOnCheckedChangeListener{ _, isChecked ->
binding.warningEditText.isVisible = isChecked
}
// 초기 선택여부
binding.warningEditText.isVisible = binding.warningCheckBox.isChecked
}
}
activity_input.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 이름 -->
<TextView
android:id="@+id/nameTextView"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:text="이름"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/nameEditText"
style="@style/Value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="36dp"
android:inputType="text"
app:layout_constraintBaseline_toBaselineOf="@+id/nameTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/guideLine" />
<!-- 생년월일 -->
<TextView
android:id="@+id/birthdateTextView"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="생년월일"
app:layout_constraintStart_toStartOf="@id/nameTextView"
app:layout_constraintTop_toBottomOf="@+id/nameTextView" />
<TextView
android:id="@+id/birthdateValueTextView"
style="@style/Value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingEnd="8dp"
android:text="0000-00-00"
app:layout_constraintBaseline_toBaselineOf="@id/birthdateTextView"
app:layout_constraintEnd_toStartOf="@id/birthdateImageView"
app:layout_constraintStart_toStartOf="@id/guideLine" />
<!-- 달력 이미지 -->
<ImageButton
android:id="@+id/birthdateImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_edit_calendar_24"
app:layout_constraintBottom_toBottomOf="@id/birthdateTextView"
app:layout_constraintEnd_toEndOf="@id/nameEditText"
app:layout_constraintTop_toTopOf="@id/birthdateTextView" />
<!-- 혈액형 -->
<TextView
android:id="@+id/bloodtypeTextView"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="혈액형"
app:layout_constraintStart_toStartOf="@id/birthdateTextView"
app:layout_constraintTop_toBottomOf="@+id/birthdateTextView" />
<!-- Rh 타입 선택하는 RadioGroup 과 RadioButton -->
<RadioGroup
android:id="@+id/bloodTypeRadioGroup"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/bloodtypeTextView"
app:layout_constraintStart_toStartOf="@+id/guideLine"
app:layout_constraintTop_toTopOf="@id/bloodtypeTextView">
<RadioButton
android:id="@+id/bloodTypePlus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rh+" />
<RadioButton
android:id="@+id/bloodTypeMinus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rh-" />
</RadioGroup>
<!-- 실제 혈액형 선택하는 Spinner -->
<Spinner
android:id="@+id/bloodTypeSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@id/bloodtypeTextView"
app:layout_constraintEnd_toEndOf="@id/nameEditText"
app:layout_constraintStart_toEndOf="@+id/bloodTypeRadioGroup"
app:layout_constraintTop_toTopOf="@id/bloodtypeTextView" />
<!-- 비상연락처 -->
<TextView
android:id="@+id/emergencyContactTextView"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="비상 연락처"
app:layout_constraintStart_toStartOf="@id/nameTextView"
app:layout_constraintTop_toBottomOf="@+id/bloodtypeTextView" />
<EditText
android:id="@+id/emergencyContactEditText"
style="@style/Value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="010-0000-0000"
android:inputType="phone"
app:layout_constraintBaseline_toBaselineOf="@id/emergencyContactTextView"
app:layout_constraintEnd_toEndOf="@+id/nameEditText"
app:layout_constraintStart_toStartOf="@id/guideLine" />
<!-- 주의사항 -->
<TextView
android:id="@+id/warningTextView"
style="@style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="주의사항"
app:layout_constraintStart_toStartOf="@id/emergencyContactTextView"
app:layout_constraintTop_toBottomOf="@+id/emergencyContactTextView" />
<EditText
android:id="@+id/warningEditText"
style="@style/Value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="주의사항"
app:layout_constraintEnd_toEndOf="@+id/nameEditText"
app:layout_constraintStart_toStartOf="@id/guideLine"
app:layout_constraintTop_toBottomOf="@id/warningCheckBox" />
<!-- 주의사항을 보여줄지 여부를 선택하는 CheckBox -->
<CheckBox
android:id="@+id/warningCheckBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="end|center_vertical"
android:text="주의사항 노출"
app:layout_constraintBottom_toBottomOf="@+id/warningTextView"
app:layout_constraintEnd_toEndOf="@+id/nameEditText"
app:layout_constraintStart_toStartOf="@+id/warningEditText"
app:layout_constraintTop_toTopOf="@+id/warningTextView" />
<!-- value 는 사용자의 입력값에 따라 길이가 변경됨 : 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/goInputActivityButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="36dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_baseline_edit_24" />
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/birthdateLayer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="birthdateValueTextView,birthdateImageView"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>