Intro
- 요즘 늘 알고리듬 2문제씩 풀면서 하루를 시작중이다. 오랜만에 수학문제푸는것같은 기분도들고, 일단 문제가 풀리면 기부니가 좋다. 이 말은 문제를 어떻게든 반드시 푼다는 것을 뜻한다..!
- 오늘 스터디 내용은 아래와같다.
알고리듬 문제풀기
오늘 문제 회고
- 문제를 풀면서 내가 문제를 접근하는 방식이 효율적인가? 에대해 많이 생각해보게된다. 다른 사람들이 푼 방식도 보는데, 나와 다른 접근을 하는 사람들을 보면서 자극이 되긴 한다. 그러나 내가 기존에 문제를 대하던 방식의 큰 틀이 바뀌지는 않는 것 같다.
- 최근 회고글을 적다보니 내가 문제를 대할때 너무 문제의 어떤 일부분에만 빠져있어 다양하게 시도를 못하지는 않나 생각해보게되었다.
퀴즈앱
- 안드로이드 공부를 시작하며 최근 간단한 앱을 만들어보고있다. 만들었고, 만들고있는 앱들은 이전에 사뒀던 Udemy강의 내용에 나오는 프로젝트들이다.
- 강의에 나오는 UI부분을 일시정지해놓고 일단 내가 만들어본 후 강사분은 어떻게 접근했는지 차이점을 보는데, UI는 내가 생각하는 부분과 큰 차이가 없다고 느껴졌다.
- 최근 UI의 drawable에대해 공부중인데 shape, selector를 다루고있다. 오늘 만든 앱에서는 아래와같이 간단하게 stroke를 만들어 사용했다.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#e8e8e8" />
<solid android:color="@color/white" />
<corners android:radius="25dp" />
</shape>
- 그래고 아래의 두가지 UI를 만들었는데, 강의에서는 LinearLayout으로만 구현하던데 나는 ConstraintLayout과 섞어서 썼다. 뭐가 더 효율적인지는 잘 모르겠다. 이건 개발자의 취향으로 봐야하는 것일까?

<?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"
android:background="@drawable/ic_bg"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:gravity="center|bottom"
android:text="QuizApp!"
android:textColor="@color/white"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/ll_box_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<LinearLayout
android:id="@+id/ll_box_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
app:cardCornerRadius="20dp"
android:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Welcome"
android:textColor="@color/black"
android:textSize="32sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Please enter your name."
android:textColor="@color/light_grey" />
<EditText
android:id="@+id/et_enter_name"
style="@style/Widget.Material3.TextInputEditText.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:hint="Name"
android:inputType="textCapWords"
android:textColor="#363A43"
android:textColorHint="#7A8089"
android:textSize="20sp" />
<Button
android:id="@+id/btn_main_start"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:text="START"
android:textSize="20sp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
android:fillViewport="true"
tools:context=".QuizActivity">
<LinearLayout
android:id="@+id/ll_quiz_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_question"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="Question"
android:textColor="#363A43"
android:textSize="22sp"
tools:text="What country does this flag belong to?" />
<ImageView
android:id="@+id/iv_question_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:contentDescription="Quiz Image"
tools:src="@drawable/ic_flag_of_argentina" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:indeterminate="false"
android:max="9"
android:minHeight="50dp"
android:progress="0" />
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"
android:textSize="14sp"
tools:text="0/9" />
</LinearLayout>
<TextView
android:id="@+id/tv_optionOne"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/drfault_option_border_bg"
android:gravity="center"
android:padding="15dp"
android:textColor="#7a8089"
android:textSize="18sp"
tools:text="Apple" />
<TextView
android:id="@+id/tv_optionTwo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/drfault_option_border_bg"
android:gravity="center"
android:padding="15dp"
android:textColor="#7a8089"
android:textSize="18sp"
tools:text="Apple" />
<TextView
android:id="@+id/tv_optionThree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/drfault_option_border_bg"
android:gravity="center"
android:padding="15dp"
android:textColor="#7a8089"
android:textSize="18sp"
tools:text="Apple" />
<TextView
android:id="@+id/tv_optionFour"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/drfault_option_border_bg"
android:gravity="center"
android:padding="15dp"
android:textColor="#7a8089"
android:textSize="18sp"
tools:text="Apple" />
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="SUBMIT"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
- 오늘 새롭게 사용한것은 CardView와 ProgressBar였다. 둘다 직관적이라 이해하고 사용하는데 큰 어려움은 없었다. progressBar의 경우 로딩할때 사용하는정도로만 생각했었는데 여기서 사용하는것을 보니 진행과정을 체크하는 용도로도 활용할수있다는것을 새롭게 배웠다.
- 오늘 앱을 만들고 강의를 보면서 느낀것은 강의에 나오는 코드들이 잘 정돈되어있다는 것이었다. 내가 개발하는 습관을 볼 때 단위기능을 구현하기보다 이것도, 저것도 머리속에 떠오르는게 너무 많아서 차근차근 쌓아올리지 못한다는 생각이 참 많이들었다. 어떻게 기능을 구현할지 머리속에서 바로 떠오르는것은 참 감사한 일이지만 그걸 어떤 순서로 구현할지도 정돈해야겠다는것을 참 많이 생각하게되었다.
- 또한가지 View.onClickListener의 사용에대해서도 새롭게 배웠다. 나는 안드로이드 개발하면서 아래와같이 사용할 생각을 전혀못했었는데 한곳에서 onClick 이벤트를 관리한다는게 좋아보였다.
package bootcamp.sparta.myquizapp
import android.graphics.Color
import android.graphics.Typeface
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.content.ContextCompat
class QuizActivity : AppCompatActivity(), View.OnClickListener {
private var mCurrentPosition: Int = 1
private var mQuestionsList:ArrayList<Question>? = null
private var mSelectedOptionPosition: Int = 0
private var progressBar: ProgressBar? = null
private var tvProgress: TextView? = null
private var tvQuestion: TextView? = null
private var ivImage: ImageView? =null
private var tvOptionOne: TextView? =null
private var tvOptionTwo: TextView? =null
private var tvOptionThree: TextView? =null
private var tvOptionFour: TextView? =null
private var btnSubmit: Button? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quiz)
progressBar = findViewById(R.id.progressBar)
tvProgress = findViewById(R.id.tv_progress)
tvQuestion = findViewById(R.id.tv_question)
ivImage = findViewById(R.id.iv_question_image)
tvOptionOne = findViewById(R.id.tv_optionOne)
tvOptionTwo = findViewById(R.id.tv_optionTwo)
tvOptionThree = findViewById(R.id.tv_optionThree)
tvOptionFour = findViewById(R.id.tv_optionFour)
btnSubmit = findViewById(R.id.btn_submit)
mQuestionsList = Constants.getQuestions()
setQuestion()
tvOptionOne?.setOnClickListener(this)
tvOptionTwo?.setOnClickListener(this)
tvOptionThree?.setOnClickListener(this)
tvOptionFour?.setOnClickListener(this)
btnSubmit?.setOnClickListener(this)
}
private fun setQuestion() {
val question: Question = mQuestionsList!![mCurrentPosition - 1]
progressBar?.progress = mCurrentPosition
ivImage?.setImageResource(question.image)
tvProgress?.text = "$mCurrentPosition/${progressBar?.max}"
tvQuestion?.text = question.question
tvOptionOne?.text = question.optionOne
tvOptionTwo?.text = question.optionTwo
tvOptionThree?.text = question.optionThree
tvOptionFour?.text = question.optionFour
if(mCurrentPosition == mQuestionsList!!.size) {
btnSubmit?.text = "FINISH"
} else {
btnSubmit?.text = "SUBMIT"
}
}
private fun selectedOptionView(tv: TextView, selectedOptionNum: Int) {
defaultOptionView()
mSelectedOptionPosition = selectedOptionNum
tv.setTextColor(Color.parseColor("#363a43"))
tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.background = ContextCompat.getDrawable(this, R.drawable.selected_option_border_bg)
}
private fun defaultOptionView() {
val options = ArrayList<TextView>()
tvOptionOne?.let{
options.add(0, it)
}
tvOptionTwo?.let{
options.add(1, it)
}
tvOptionThree?.let{
options.add(2, it)
}
tvOptionFour?.let{
options.add(3, it)
}
for(option in options) {
option.setTextColor(Color.parseColor("#7A8089"))
option.typeface = Typeface.DEFAULT
option.background = ContextCompat.getDrawable(this, R.drawable.drfault_option_border_bg)
}
}
override fun onClick(view: View?) {
when(view?.id){
R.id.tv_optionOne -> {
tvOptionOne?.let {
selectedOptionView(it, 1)
}
}
R.id.tv_optionTwo -> {
tvOptionTwo?.let {
selectedOptionView(it, 2)
}
}
R.id.tv_optionThree -> {
tvOptionThree?.let {
selectedOptionView(it, 3)
}
}
R.id.tv_optionFour -> {
tvOptionFour?.let {
selectedOptionView(it, 4)
}
}
R.id.btn_submit -> {
//TODO "Implement btn submit"
}
}
}
}
- 맨 처음 잘 이해되지않았다. onClickListener를 상속받는것까지 이해가 되었고, 그래서 onClick메서드를 override한것도 당연히 이해가되었다. 그래서 when구문을 통해 id로 클릭한 button을 구별해서 각각 이벤트를 처리해주는것도 이해가되었다. 여기까지만 만들어놓고 실행해서 버튼을 클릭해보면 작동을 하지않았다. 요때 아래 코드를 작성해주면 잘 작동하는것을 확인했다. 왜그럴까?
tvOptionOne?.setOnClickListener(this)
tvOptionTwo?.setOnClickListener(this)
tvOptionThree?.setOnClickListener(this)
tvOptionFour?.setOnClickListener(this)
btnSubmit?.setOnClickListener(this)
- 블로그글을 쓰다가 궁금해서 찾아본결과..! OnClickListener는 interface이고 여기에는 onClick이라는 메서드가 존재한다. 우리가 버튼에 setOnClickListener를 선언하는것은 해당 버튼에 Listener를 달아주는 것이다. 결국은 OnClickListener의 onClick을 실행시켜주는 것이다. 그러니 위의 선언은 각각의 객체에 Listener를 달아주는것이고, 우리는 위에서 본것과같이 class에 interface를 상속받아놨으니 상속받아 구현해놓은 onClick 메서드에서 위에 등록한 Listener들의 상태를 감지하여 id값을 받아올 수 있는것이다.
- 내가 onClickListener에 대한 이해가 없었을때에는 Listener를 등록해놓지않은 상태에서 실행했으니 당연히 내가 View를 마구 클릭해도 아무런 일이 일어나지 않은 것이다.
- 위와같이 정리해놓긴했지만 사실 아직 완벽하게 설명할 수 있는 상태는 아니다. 아직도 아리까리하다. 위 내용을 정리하기까지 여기의 글을 참고하여 작성했다. 이 설명보다 여기 글이 훨씬 더 디테일하니 혹시나 궁금하신분이 있다면 가서 보시길 :)
Outro
- 개념을 하나씩 정립해가는 중이다. 안드로이드가 나온지 벌써 10년도 넘었으니 그동안 쌓인 개념들이 얼마나 많을까싶다. 그리고 동시에 지금도 새로운 패러다임을 만들어가고있으니 갈길이 멀다 허헣
- 하나씩 쌓아가다보면 언젠가는 내 머리가 트이는 순간이 오지않을까 살포시..? 기대해본다 ㅎㅎ 끗!