GDSC HUFS 3기/Android with Kotlin Team 1

[1팀] 11-8~10. 화면에 그려지는 디자인 요소 위젯: 프로그래스바, 시크바, 레이팅바

dev-yen 2021. 11. 16. 22:47

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 김예은

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

 

1. ProgressBar

프로그래스바는 작업 진행률을 나타내기 위해 사용하는 뷰 위젯이다. 안드로이드의 프로그레스바는 진행 상태를 표시함에 있어 두 가지 모드를 지원하는데, 불확정적(indeterminate) 상태 표시 모드확정적(determinate) 상태 표시 모드이다. 이 두 가지 모드를 구분하는 기준은, 진행 상태를 표시할 때 명확한 수치 또는 범위 값을 지정하여 현재의 진행 단계를 표시할지(=determinate), 아니면 명확한 수치 또는 범위 값을 사용하지 않고 막연히 작업이 진행되고 있음을 표시할지(=indeterminate)여부이다.

불확정적 상태 표시 모드가 프로그래스바의 기본 동작 모드이며, 화면에 표시될 때 특정 값을 표시하지 않고 반복적인 애니메이션을 통해 어떠한 작업이 진행 중임을 보여준다.

  • LinearLayout을 백그라운드 layout으로 깔아주고 ProgressBar와 textView를 배치해주었다.
  • 화면 중앙에 배치하기 위해 progressLayout의 layout_width와 layout_height을 wrap_content로 변경해주었다.

*3초간 프로그래스바가 돌아간 뒤 사라지는 코드

package com.example.myapplicationkotlin

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplicationkotlin.databinding.ActivityMainBinding
import kotlin.concurrent.thread

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}
    //Activity가 가지고 있는 property를 넘김

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root) //binding이 생성되면 항상 해줘야하는 부분분

        with(binding){
            /// <- 메인스레드
            thread(start=true){ /// -> 서브스레드
                Thread.sleep(3000) //3초간 sleep
                //화면에 영향을 미치는 코드는 메인스레드로 다시 보내야 한다.
                runOnUiThread{
                    showProgress(false)
                }
            } /// <- 서브스레드

            /// <- 메인스레드드
        }
       //현재 코드는 화면을 그려주는 mainThread와 함께 있음.
        //이때 sleep함수를 써버리면 그 코드까지 멈춰버리는 상황이 생김.
        //이를 방지하기 위해 thread를 사용한다.
        //start=true로 인해, 이 코드를 만난 순간 바로 실행이 된다.
        //start=true를 주지 않으면, thread블럭 마지막에 .start()를 붙여야 실행됨
   }

    fun showProgress(show: Boolean){
        binding.progressLayout.visibility = if(show) View.VISIBLE else View.GONE
        //화면에서 다운로딩할 땐 View.VISIBLE, 다운로드가 끝나면 GONE
    }
}
  • ViewBinding을 사용해 layout에 있는 위젯들을 사용하였다.
  • with함수 안에 thread블럭을 사용하지 않고 코드를 짜는 경우, 메인 스레드가 진행되는 과정에서 sleep코드와 progressLayout.visibility 코드가 다 진행되버리므로, 화면에 프로그래스바가 보이기도 전에 visibility가 GONE이 되버린다.
    • 이를 방지하기 위해 코틀린에서 제공하는 thread라이브러리를 사용해 서브 thread를 만들고, 그 안에 코드를 구현해주었다. 이 때, 프로그래스바가 사라지는 코드는 화면에 즉, UI에 영향을 미치는 코드이므로 runOnUiThread블럭 내부에서 코드를 구현해주었다.

추가로,

thread(start=true) {}

서브 thread를 실행시키는 방법으로는 다음과 같은 start=true를 지정해주는 방법이있고,

thread(){

//코드

}.start()

이런식으로, 블럭 마지막에 .start()를 붙여서 실행시킬 수도 있다.

 


 

2. SeekBar

시크바는 드래그 가능한 기능을 추가한 프로그래스바의 확장과 같다. 사용자는 손가락으로 시크바를 터치해 왼쪽이나 오른쪽으로 드래그하여 현재 진행 수준을 설정하거나 화살표 키를 사용할 수 있다.

시크바는 볼륨 조절 컨트롤러에서 가장 많이 쓰이며, 음악 플레이어에서 구간을 찾아갈때도 사용한다.

textView와 seekBar를 한 개씩 배치시킨 layout

progressBar의 수치 최대값은 attributes의 max속성을 통해 조절할 수 있다. 디폴트 값은 100이다.

package com.example.myapplicationkotlin

import android.os.Bundle
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplicationkotlin.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        with(binding){
            seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
                override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                    //progress는 현재 탐색 구간값이 넘어옴.
                    //fromUser는 시크바를 내가 터치해서 탐색할 수도있지만 코드에 의해 움직일 수도 있다.
                    //fromUser가 true이면 사람이 터치하고 있는 것.

                     if(fromUser) {//사람이 터치로 동작시킬 때만 실행하는 코드
                        textView.text = "$progress"
                     }
                }
                override fun onStartTrackingTouch(seekBar: SeekBar?) {}
                override fun onStopTrackingTouch(seekBar: SeekBar?) {}
            })
        }
    }
}

시크바의 경우, 람다함수로 만들어진 setOn~Listener가 없기 때문에 object키워드를 붙이고 OnSeekBarChangeListener를 사용해주어야 한다. 이때, OnSeekBarChangeListener인터페이스에 구현된 함수 3개를 모두 구현해 주어야 하는데 다음과 같다.

  • onProgressChanged : 시크 바를 조작하고 있는 중 작동
  • onStartTrackingTouch : 시크 바를 조작하기 시작했을 때 작동
  • onStopTrackingTouch : 시크 바 조작을 마무리했을 때 작동

시크바 코드 AVD 돌린 모습

 


 

3. RatingBar

레이팅바는 별 등급을 표시하는 시크바 및 프로그래스바의 확장 버전이다. 사용자는 레이팅바를 사용할 때 터치 또는 드래그 또는 화살표 키를 사용하여 등급을 설정할 수 있다.

ratingBar와 textView를 하나씩 배치한 모습

  • ratingBar의 경우,  numStart속성을 통해 별 개수를 바꿀 수 있다. (디폴트는 5개이다)
  • rating 속성을 통해 원하는 만큼 별을 미리 채워둘 수 있다.
  • stepSize 속성을 통해 rating step을 지정할 수 있는데, 0.1로 변경하면 점수를 좀 더 디테일하게 줄 수 있다. (디폴트는 0.5이므로 점수를 0부터 0.5간격으로 줄 수 있다.)
    stepSize를 0.1로 했을 때 결과
    *사용한 코드
package com.example.myapplicationkotlin

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplicationkotlin.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        with(binding){
            ratingBar.setOnRatingBarChangeListener { ratingBar, rating, fromUser ->
                if(fromUser)
                    textView.text = "$rating"
            }
        }
    }
}

프로그래스바, 시크바와 마찬가지로 setOn~Listener를 사용해주었고, fromUser가 true일 때, 즉 사용자가 ratingBar를 터치했을 때의 값인 rating변수를 textView에 넣어주었다. 

rating변수는 숫자값이므로 textView에 string값으로 넣고 싶다면 문자열 템플릿인 $기호를 사용하여 넣어주면 된다.