GDSC HUFS 4기/Kotlin Team #3

[3팀] 퀴즈 앱 안드로이드 12 - 질문모델생성과 UI모델 연결

비코(gyeom) 2022. 11. 5. 12:35

이 글은 유데미 강의 Android 12 및 Kotlin 개발 완전 정복을 참고하여 작성하였습니다.

작성자 : 김인겸 

개발환경은 Mac, Android Studio입니다. 

 

강의영상 104-106까지의 내용입니다.

 

1.  질문 모델 생성하고 질문 준비하기

시작화면에서 이름을 작성하고 시작을 누르면 다음화면에서 질문과, 옵션을 나타내도록 하는 코드를 작성합니다.

이 때, 두번째 화면을 나타내는 새 Activity를 MainActivity가 존재하는 패키지 내에 만들어 줍니다.

(이름은 "QuizQuestionActivity"로 합니다.)

 

class QuizQuestionsActivity : AppCompatActivity() {
     override fun onCreate(savedInstanceState:Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_quiz_questions)}

 

위 클래스를 지닌 새 액티비티가 생성됩니다. 또한, 위 액티비티와 연관된 레이아웃 파일은 activity_quiz_questions.xml 이 됩니다. 

 

다음화면으로 넘어가는 버튼을 구현시켜보면, Activity_main.XML파일 내 버튼의 id를 설정하여 MainActivity에 연결합니다.

 

val buttonStart : Button = findViewById(R.id.btn_start)

이 Value를 MainActivity에 작성하면 버튼을 사용할 수 있게 됩니다.

 

OnClickListener메소드를 이용하여, 만약 이름을 작성하는칸 안에 텍스트가 없다면 다음화면으로 넘어가지 못하도록 하고, 있다면 QuizQuestionsActivity로 이동할수 있게합니다. 

val etName : AppCompatEditText = findViewById(R.id.et_name) //변수 선언

buttonStart.setOnClickListener {
if (etName.text.toString().isEmpty()){
//이름 작성란이 비어있을 시, 토스트를 띄운다.
Toast.makeText(this,"Please Enter Your Name",Toast.LENGTH_SHORT).show() }
else{
//이름을 작성했다면 다른 액티비티로 넘어간다.(this에서 QuizQuestionsActivity로)
val intent = Intent(this,QuizQuestionsActivity::class.java)
startActivity(intent) }

이때, Activity_main.XML파일 내의 AppCompatEditText의 정보가 필요하므로, 이 정보의 id를 etname으로 설정하고, 이를 EditText로 부릅니다.

(변수 선언시, 위 buttonStart와 etName 처럼 '_'를 이용하지 않는것을 권장합니다.)

 

이제 질문지를 만들어 보겠습니다.

동일한 패키지 내에 Question 데이터 클래스를 만듭니다. 이 클래스 내에 문제의 사진, 선지, 정답을 모두 구현하는 매개변수를 작성합니다.

 

data class Question(
val id: Int, 
val question: String, 
val image: Int
val optionOne: String,
val optionTwo: String,
val optionThree: String,
val optionFour: String,
val correctAnswer: Int )

이때, 사진은 Int속성으로 이미지를 만듭니다

 

이를 이용하여 질문에 접근하겠습니다.

동일한 패키지 내에 Constant 오브젝트를 만들고, 이 오브젝트 내에 모든 문제의 정보들을 담습니다.

 

1번 질문만 나타내면, 
fun getQuestions(): ArrayList<Question> {
      val questionsList = ArrayList<Question>()
      val que1 = Question(
           1, "이국기는 어느나라 국기 일까요?",
           R.drawable.ic_flag_of_argentina,
           "Argentina", "Australia",
           "Armenia", "Austria", 1   )

questionsList.add(que1)
}

 

질문의 id, 내용, 사진, 선지, 정답을 ArrayList로서 저장하여 원하는 대로 질문들을 만들 수 있습니다.

 

만약 질문을 9개를 추가했다면 questionsList의 크기는 9가 됩니다. 이를 로그캣을 이용하여 확인할 수 있습니다.

 

val questionsList = Constants.getQuestions()
Log.e("Questions Size", "${questionsList.size}")
for (i in questionsList) {
Log.e("Questions", i.question)  }

 

위 식을 이용하면 스튜디오 하단의 Logcat을 통해 코드를 실행했을시, 이름을 입력하고 다음액티비티로 이동하면,

해당 액티비티 내에 questionsList를 보낼 수 있고 로그캣 내에 리스트의 사이즈를 알려줄 수 있게 됩니다.

 

2. 질문 UI 세팅 후 UI를 모델에 연결하기

activity_quiz_questions.xml을 이용합니다. 

<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=".QuizQuestionsActivity">
</ScrollView>

 

화면 전체를 올리고 내리기 위해, 스크롤 뷰가 전체 뷰 포트 에 보이도록 합니다.

위 스크롤 뷰 내에 선형 레이아웃을 첨가하여, 아이템들이 정렬하도록 할 수 있습니다.

아이템들의 종류에는 이미지 뷰와 진행 바가 있습니다.

 

//사진을 이미지뷰로 나타냄
<ImageView
android:id="@+id/iv_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:contentDescription="image" //화면을 보지 못하는 경우에 어떤 아이템인지 표시되는 기능
tools:src="@drawable/ic_flag_of_germany" />

//진행상태를 ProgressBar로 나타냄
<LinearLayout android:id="@+id/ll_progress_details" //ProgressBar에 대한 선형 레이아웃을 추가함
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="?android:attr/progressBarStyleHorizontal" //수평방향의 스타일을 알려줌
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:indeterminate="false" //진행 바가 움직이지 않도록 설정해줌
android:max="10"
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:textColorHint="#7A8089"
android:textSize="14sp"
tools:text="0/10" />

</LinearLayout>

이미지 뷰와 진행 바를 작성하면, 이러한 디자인 xml파일 내에서 볼 수 있습니다.

 

이제 우리가 선택할 옵션(선지)들을 만들어 보겠습니다. 버튼을 사용하여 옵션들을 추가하는게 아닌, 텍스트 뷰를 이용하여 이를 버튼처럼 보이도록 할 것입니다.

진행 바가 있는 선형레이아웃 바깥에 텍스트 뷰의 형태로 옵션들을 추가하면,

 

//예시 : 옵션 1
<TextView android:id="@+id/tv_option_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/default_option_border_bg" //리소스 파일을 새로 추가함
android:gravity="center"
android:padding="15dp"
android:textColor="#7A8089"
android:textSize="18sp"
tools:text="Apple" />

 

의 형태로 코드를 추가할 수 있습니다. 이때, 새로운 리소스 파일을 drawable패키지 내에 추가하게 되는데, 기본 XML파일을

 

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> //어떤 모양으로 만들지 정의함
<stroke android:width="1dp" android:color="#E8E8E8" /> //테두리 구현
<solid android:color="@android:color/white" /> //배경색 구현
<corners android:radius="5dp" /> //둥근 모서리 구현
</shape

 

이러한 형태로 변형하여 나타내면 아래 사진과 같이 구현할 수 있습니다. 또한, 제출하는 기능은 버튼을 이용하여 구현하면,

 

//제출 버튼 구현
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@color/colorPrimary"
android:text="SUBMIT"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" />

 

위 사진과 같이 나타낼 수 있습니다.

 

지금까지 추가한 요소들이 작동할 수 있도록 QuizQuestionsActivity내에 변수를 널러블로 설정하고, Oncreate메소드에 아이템들을 추가합니다.

 

// 변수 설정 및 onCreate 메소드에 추가
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 progressBar : ProgressBar? = null


  override fun onCreate(savedInstanceState:Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_quiz_questions)
progressBar = findViewById(R.id.progressBar) 
tvProgress = findViewById(R.id.tv_progress)
tvQuestion = findViewById(R.id.tv_question)
ivImage = findViewById(R.id.iv_image)
tvOptionOne = findViewById(R.id.tv_option_one)
tvOptionTwo = findViewById(R.id.tv_option_two)
tvOptionThree = findViewById(R.id.tv_option_three)
tvOptionFour = findViewById(R.id.tv_option_four)
}

 

다음으로 for 문을 이용하여 questionList의 질문들을 가지고 옵니다. 이때 로그 클래스를 이용하여 가지고 올 수 있습니다.

 

//로그캣 하단에 반복문 추가함

for (i in questionsList) {
Log.e("Questions", i.question)
}

//constants파일내 질문 리스트에서 각각의 질문을 가지고 옴
val currentPosition = 1
val question: Question? = questionsList[currentPosition - 1]
//1번 질문부터 시작하게 하기 위해 현재 위치를 1로 표시한 후, 리스트는 0부터 숫자를 셈

//모든 버튼과 옵션, 진행바들을 가지고 옴
tvProgress?.text = "$currentPosition/${progressBar?.max}"
progressBar?.progress = currentPosition
tvQuestion?.text = question!!.question
ivImage?.setImageResource(question.image) //사진을 Int로 저장했으므로, Bitmap기능을 사용하지 않는다.
tvOptionOne?.text = question.optionOne
tvOptionTwo?.text = question.optionTwo
tvOptionThree?.text = question.optionThree
tvOptionFour?.text = question.optionFour

 

모두 적용하면 위 사진과 같이 작동하기 전의 UI를 모두 구현할 수 있습니다.

 

이상으로 블로그 포스팅을 마치겠습니다. 감사합니다.