TIL 2022.07.06
Android
오늘은 생일을 선택하면 생일로부터 오늘까지 몇 시간이 지났는지 알려주는 앱을 만들어보았다.



1. Linear Layout
Linear Layout을 활용하여 상하(Vertical), 좌우(Horizontal)로 레이아웃을 잡는 방법을 공부하였다. Linear Layout 내의 children은 특정한 방향으로 정렬되며 gravity, orientation 등의 속성을 활용해 어떻게 정렬될지 지정할 수 있다.
gravity 속성을 통해 children을 가운데 정렬, 오른쪽 정렬, 왼쪽 정렬, 위로 정렬, 아래로 정렬 등의 방법 중 어떤 방법으로 정렬할지 정할 수 있다. 가능하면 right 정렬보다는 end 정렬, left 정렬보다는 start 정렬을 사용하는 것이 좋다. 한국어는 좌측에서 우측으로 읽지만 외국어 중에는 우측에서 좌측으로 읽는 언어도 있기 때문이다. 관련 안드로이드 문서: 다영한 언어 및 문화 지원
iOS에서도 유사한 이유로 left 대신 leading, right 대신 trailing 속성 사용을 권장한다.
iOS 앱 개발에서 사용하는 UIStackView와 유사한 특징을 가지고 있다. 단 UIStackView의 spacing과 같은 속성은 내장되어 있지 않아 가로(또는 세로) 길이가 지정된 커스텀뷰를 만들어 divider로 설정해야 한다. 관련 StackOverflow 링크
2. DP vs SP
dp와 sp는 안드로이드에서 특정 뷰의 크기를 지정할 때 사용하는 단위이다. 예를 들어 layout_width를 20dp로 설정하는 형태로 사용한다. 주로 글자 크기를 지정할 때는 sp를 사용하고 뷰의 가로 길이, 세로 길이와 같은 크기를 지정할 때는 dp 단위를 사용하는데 두 단위의 차이점은 무엇일까?
눈이 불편한 앱 사용자 분들께서 화면의 글자가 작아 글자 크기를 크게 키우는 설정을 하고 앱을 사용하는 것을 간혹 볼 수 있다. 이 때 dp 단위로 설정한 글자 크기는 핸드폰 설정에서 글자가 크게 나오도록 설정해도 설정과 무관하게 우리가 지정한 크기만큼 나타나고, sp 단위로 설정한 글자 크기는 핸드폰 설정에서 글자가 크게 나오도록 설정했을 때 글자가 기존보다 더 크게 확대되어 나타난다. 만약 핸드폰 설정에서 글자가 작게 나오게 하였다면 마찬가지로 sp 단위로 지정된 글자는 더 작게 나타난다.
관련 안드로이드 문서: 다양한 픽셀 밀도 지원
밀도가 서로 다른 화면에서 UI 표시 크기를 유지하려면 밀도 독립형 픽셀(dp)을 측정 단위로 사용해서 UI를 디자인해야 합니다. 1dp는 중밀도 화면(160dpi, '기준' 밀도)의 1픽셀과 거의 동일한 가상 픽셀 단위입니다. Android는 이 값을 밀도마다 적합한 실제 픽셀 수로 변환합니다.
예를 들어 그림 1의 두 기기를 생각해 보세요. 뷰 너비를 '100px'로 정의하면 뷰가 왼쪽 기기에서 훨씬 더 크게 표시됩니다. 따라서 '100dp'를 대신 사용하여 뷰가 두 화면에서 동일한 크기로 표시되도록 해야 합니다.
그러나 텍스트 크기를 정의할 때는 확장 가능 픽셀(sp)을 단위로 사용해야 합니다(단, 레이아웃 크기에는 sp를 사용하지 않아야 함). sp 단위는 기본적으로 dp와 같은 크기이지만, 사용자가 선호하는 텍스트 크기에 따라 크기가 조절됩니다.
3. Android Activity Lifecycle - onCreate
onCreate 메서드를 override하여 해당 메서드 내에서 해당 Activity와 관련된 설정을 초기화하였다. onCreate 메서드는 해당 Activity가 처음 생성되어 보여졌을 때에만 호출된다. findViewById 등의 메서드를 활용해 뷰를 변수에 불러와 저장할 때에 주로 이 메서드에서 처리한다.
class MainActivity : AppCompatActivity() {
private lateinit var selectedDateTextView: TextView
private lateinit var ageInHoursTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val selectDateButton = findViewById<Button>(R.id.selectDateButton)
selectDateButton.setOnClickListener {
showDatePicker()
}
selectedDateTextView = findViewById(R.id.selectedDateTextView)
ageInHoursTextView = findViewById(R.id.ageInHoursTextView)
}
private fun showDatePicker() {
// 코드 생략...
}
}
관련 안드로이드 문서: 활동 수명 주기에 관한 이해
4. Nullable과 let 메서드
private fun showDatePicker() {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
val datePickerDialog = DatePickerDialog(
this,
DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
val selectedDateString = "${year}/${month + 1}/${dayOfMonth}"
selectedDateTextView.text = selectedDateString
val simpleDateFormat = SimpleDateFormat("yyyy/MM/dd")
val selectedDate = simpleDateFormat.parse(selectedDateString)
selectedDate?.let { selectedDate ->
val selectedDateInMinutes = selectedDate.time / 3600000
val currentDate = simpleDateFormat.parse(simpleDateFormat.format(Date()))
currentDate?.let { currentDate ->
val currentDateInMinutes = currentDate.time / 3600000
val ageInHours = currentDateInMinutes - selectedDateInMinutes
ageInHoursTextView.text = ageInHours.toString()
}
}
},
year,
month,
dayOfMonth
)
datePickerDialog.datePicker.maxDate = System.currentTimeMillis()
datePickerDialog.show()
}
Nullable과 관련된 변수의 값을 활용할 때에는 let, apply와 같은 메서드를 활용하면 가독성이 증가한다. 그 외에도 elvis 연산자를 활용하여 해당 값이 null일 경우 return시키는 것도 좋은 방법이 될 수 있다.
Kotlin의 elvis 연산자에서 return문을 작성하는 것과 비슷한 문법으로 Swift에서는 guard문을 활용한 Optional Binding이라는 문법을 제공한다.
Kotlin의 let과 유사한 메서드는 Swift에서 자체적으로 제공되지 않아 대신 then이라는 Library를 활용하여 작성한다.