GDSC HUFS 3기/Android with Kotlin Team 4

[4팀] 11. (2) Kotlin을 위한 기본 문법 : 화면에 그려지는 디자인 요소 위젯

먉. 2021. 10. 29. 17:15

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

작성자 : 김예진

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

 

 

1. 에디트텍스트


* EditText

사용자로부터 데이터를 입력받을 때 사용하는 위젯

 

xml 창에서 Palette > Text > Plain Text 를 선택해 새로운 텍스트를 하나 생성해줍니다.

 

💡 새로운 뷰를 추가할 때는 반드시 우측의 Layout > Constraint Widget에서 뷰의 상하좌우 위치를 고정(constraint)시켜야 합니다. 왼쪽과 오른쪽에 같은 값을 입력하면 뷰를 화면 가운데에 위치시킬 수 있습니다.

 

class MainActivity : AppCompatActivity() {

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root) //binding 연결
  }
}

메인 액티비티로 넘어와 새로운 바인딩을 생성합니다. 바인딩을 선언해주고 난 뒤 setContentView에 binding.root를 넘겨주어야 xml 파일과 연결이 완료됩니다.

with(binding){
    editText.addTextChangedListener{
        Log.d("에딧텍스트", "입력된 값${it.toString()}"} //실행값을 확인하기 위한 로그
    }
}

바인딩이 완료되면, with 스코프 함수를 사용해 뷰 바인딩으로 editText 뷰를 참조하여 addTextChangedListener 함수를 실행합니다. addTextChangedListener 함수는 실시간으로 입력되는 동작을 확인하는 함수입니다. 실시간 동작으로 리턴된 인스턴스를 it에 넘겨주므로 다음과 같이 파라미터를 설정하여 작성하는 것 역시 가능합니다.

with(binding){
    editText.addTextChangedListener{ editable ->
        Log.d("에딧텍스트", "입력된 값${editable.toString()}"}
    }
}

코드를 실행한 뒤 logcat에서 "에딧텍스트"를 검색하고 에뮬레이터에서 값을 입력하면 키보드 입력에 따라 로그가 업데이트 됩니다.

 

addTextChangedListener를 응용하여 EditText로 입력받은 데이터를 실시간으로 TextView에 반영할 수도 있습니다. xml에서 textView를 생성해준 뒤 다음과 같이 입력합니다.

with(binding){
    editText.addTextChangedListener{ editable ->
        textView.text = editable.toString()
    }
}

 

에뮬레이터를 실행하면, editable로 전달된 실시간 입력값이 textView.text 함수를 통해 텍스트뷰 위젯에 그대로 반영되어 화면에 나타나는 것을 확인할 수 있습니다.

 

 

2. 라디오 그룹 / 라디오 버튼


* Radio group / Radio button

여러 옵션 중 한 가지 옵션을 선택하는 위젯. 하나의 라디오 버튼은 한 가지 항목을 나타내며 이러한 여러 라디오 버튼을 그룹화한 것이 라디오 그룹.

 

 

xml에서 Buttons > RadioGroup 을 선택해 하나의 라디오 그룹을 생성하고, 같은 위치의 RadioButton을 드래그해 Component Tree의 RadioGroup 밑으로 옮겨 RadioGroup 하위의 라디오 버튼들을 생성합니다. 라디오 버튼들의 id를 각각 radioApple, radioBanana, radioOrange로 설정한 뒤, 아래의 text에서 내용을 '사과', '바나나', '오렌지'로 설정합니다. 메인 액티비티로 넘어와 앞서 생성한 라디오 버튼들에 클릭 처리가 가능하도록 코드를 작성합니다.

 

class MainActivity : AppCompatActivity() {

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        
        binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> //라디오 버튼을 클릭하면 이벤트 값이 두 파라미터에 전달
            when(checkedId) { //이벤트가 발생한 id를 값으로 when 조건문 실행
                R.id.radioApple -> Log.d("라디오버튼", "사과가 선택되었습니다.") //각 id에 맞춰 로그 업데이트
                R.id.radioBanana -> Log.d("라디오버튼", "바나나가 선택되었습니다.")
                R.id.radioOrange -> Log.d("라디오버튼", "오렌지가 선택되었습니다.")
            }
        }
  }
}

setOnCheckedChangeListener 함수는 라디오 그룹과 라디오 버튼의 상태가 변경될 때마다의 이벤트를 해당 라디오 그룹의 id와 라디오 버튼의 id를 각각 group, checkedId 파라미터로 넘겨줍니다. 이렇게 해서 전달 받은 id를 when 문을 통해 값을 확인하고, 그 값에 따라 실행할 내용을 작성해줍니다.

 

 

에뮬레이터를 실행하여 화면에서 라디오 버튼을 클릭할 때 logcat에서 로그가 업데이트됨을 확인할 수 있습니다.

 

세로로 정렬되어 있는 라디오 버튼들을 가로로 정렬하고 싶을 때는, xml로 넘어와 우측 상단의 Atrributes 검색창에 orientation을 검색합니다.

 

 

horizontal과 vertical 옵션 중 하나를 선택하여 라디오 버튼들의 정렬형태를 바꿀 수 있습니다.

 

 

3. 체크박스


* Check Box

여러 옵션 중 다중선택할 수 있는 위젯

 

체크 박스는 라디오 버튼과 유사하지만 두 가지 이상의 옵션 선택이 가능하다는 차이점이 있습니다. 또한 라디오 그룹과 같은 상위 그룹 위젯이 존재하지 않아 그룹화를 위해 레이아웃을 사용합니다. 체크 박스를 생성하기 전에, xml 파일에서 Palette > Layouts > LinearLayout 을 선택합니다. 가로형(horizontal)과 세로형(vertical) 중에 선택이 가능합니다. 해당 포스팅에서는 가로형을 사용하겠습니다. 레이아웃 틀을 불러와 최대한 넓게 constrain 해줍니다. (상하좌우 constraint 값 0으로 설정)

 

LinearLayout이 생성되고 나면 Buttons > CheckBox 를 드래그해 화면에 끌어다놓습니다. 세 번 반복해 세 개의 체크박스를 생성해줍니다.

 

상단부에 생성된 체크박스들을 상하좌우 모두 가운데로 정렬하고 싶을 때는 우측 Attributes 탭에서 Constraints > layout_width과 layout_height를 wrap_content로 설정해주면 됩니다. 체크박스 설정이 완료되면 라디오 버튼을 수정해주었던 것처럼 id와 text를 수정해줍니다. 해당 포스팅에서는 각각의 id를 checkApple, checkBanana, checkOrange로, 내용은 사과, 바나나, 오렌지로 수정해주었습니다.

 

체크박스들 간의 간격이 너무 좁거나 넓어 수정하고 싶을 때는 간격을 변경할 체크박스를 선택하고 원하는 마진값을 설정할 수 있습니다.

 

checkBanana 체크박스를 선택한 뒤 우측 Attributes 탭에서 layout_marginLeft의 값을 10dp로 변경해주었습니다. 우측 간격을 수정하고 싶을 때는 layout_marginRight, 위 아래 간격을 수정하고 싶을 때는 각각 layout_marginTop, layout_marginBottom을 수정하면 됩니다.

 

xml에서 뷰 설정이 완료되면 메인 액티비티 파일로 넘어와 코드를 작성합니다.

 

class MainActivity : AppCompatActivity() {

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        
        with(binding){
            checkApple.setOnCheckedChangeListener { buttonView, isChecked -> //체크박스 자체와 선택여부를 파라미터로 전달
                if(isChecked) Log.d("체크박스", "사과가 선택되었습니다.") //isChecked의 boolean 값 체크 후 로그 출력
                else Log.d("체크박스", "사과가 선택 해제되었습니다.")
            }
        }
  }
}

setOnCheckedChangeListener 함수는 buttonView이벤트가 발생한 체크박스 자체를 넘겨주고, isChecked해당 체크박스의 선택 여부를 넘겨줍니다. isChecked가 true면 사과가 선택되었음을 알리는 로그를 출력하고, false일 때는 사과가 선택 해제되었음을 알리는 로그를 출력합니다.

 

 

에뮬레이터를 실행하여 '사과' 체크박스를 선택하고 해제할 때마다 Logcat에서 다음과 같은 로그들이 출력됨을 확인할 수 있습니다. 이어서 '바나나' 체크박스와 '오렌지' 체크박스에도 리스너를 달아 같은 동작을 확인할 수 있습니다.

 

        with(binding){
            checkApple.setOnCheckedChangeListener { buttonView, isChecked -> //사과
                if(isChecked) Log.d("체크박스", "사과가 선택되었습니다.")
                else Log.d("체크박스", "사과가 선택 해제되었습니다.")
            }
            
            checkBanana.setOnCheckedChangeListener { buttonView, isChecked -> //바나나
                if(isChecked) Log.d("체크박스", "바나나가 선택되었습니다.")
                else Log.d("체크박스", "바나나가 선택 해제되었습니다.")
            }
            
            checkOrange.setOnCheckedChangeListener { buttonView, isChecked -> //오렌지
                if(isChecked) Log.d("체크박스", "오렌지가 선택되었습니다.")
                else Log.d("체크박스", "오렌지가 선택 해제되었습니다.")
            }
        }

 

위와 같은 코드로 모든 체크박스에 리스너 함수를 사용해 동작시킬 수 있지만, 체크박스의 수가 많아지면 코드가 반복되고 길어지게 됩니다. 이러한 경우에는 inner class로 구현한 하나의 리스너 함수로 여러 개의 체크박스를 관리할 수 있습니다.

 

class MainActivity : AppCompatActivity() {

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        
        with(binding){ //checkBoxListener 함수에서 리턴된 출력문을 리턴
            checkApple.setOnCheckedChangeListener(checkBoxListener)
            checkBanana.setOnCheckedChangeListener(checkBoxListener)
            checkOrange.setOnCheckedChangeListener(checkBoxListener)
        }
    }
  
    val checkBoxListener = CompoundButton.OnCheckedChangeListener { checkBox, isChecked -> 
    
    //checkBox 파라미터로 전달받은 각 체크박스의 아이디를 확인하여 다음 동작을 실행
    
        when(checkBox.id){
            R.id.checkApple -> {
                if(isChecked) Log.d("체크박스", "사과가 선택되었습니다.")
                else Log.d("체크박스", "사과가 선택 해제되었습니다.")
            }
            R.id.checkBanana -> {
                if(isChecked) Log.d("체크박스", "바나나가 선택되었습니다.")
                else Log.d("체크박스", "바나나가 선택 해제되었습니다.")
            }
            R.id.checkOrange - > {
                if(isChecked) Log.d("체크박스", "오렌지가 선택되었습니다.")
                else Log.d("체크박스", "오렌지가 선택 해제되었습니다.")
            }
        }
    }
}

CompoundButton.OnCheckedChangeListener는 인터페이스 함수이므로 그 내용을 직접 구현해 사용해야 합니다. 이 함수는 setOnCheckedChangeListener 함수와 마찬가지로 체크박스 그 자체와 체크박스의 선택 여부를 각각 checkBoxisChekced 파라미터로 넘겨받습니다. 어떤 체크박스에 이벤트가 발생했는지 확인하는 작업이 필요하므로 이전의 코드에서는 사용하지 않았던 checkBox 파라미터를 사용하는 것이 관건입니다. 이는 when문을 통해 구현하고, 이벤트가 발생한 체크박스의 id에 따라 각기 다른 코드를 실행해주도록 작성해줍니다.

 

 

4. 토글 / 스위치


* Toggle / Switch

ON/OFF를 제어할 수 있는 버튼

 

토글과 스위치 버튼은 UI의 모양만 다를 뿐 동일한 기능을 수행합니다. 두 버튼을 동시에 xml 파일에 생성해 동작해보겠습니다.

 

토글 버튼은 Palette > Buttons > ToggleButton에서 생성하고, 스위치 버튼은 Palette > Buttons > Switch에서 생성합니다. 각자의 아이디를 부여해준 뒤 토글 버튼과 스위치 버튼의 동작에 따라 변경되는 값을 화면에 표시해주기 위해 두 개의 텍스트뷰를 생성합니다. 토글 버튼에 할당된 텍스트뷰는 textToggle, 스위치 버튼에 할당된 텍스트뷰는 textSwitch로 설정하였습니다. 또한 현재 토글 버튼과 스위치 버튼이 Off를 가리키고 있으므로, 두 개의 텍스트뷰의 내용은 'Off'로 설정해줍니다.

 

class MainActivity : AppCompatActivity() {

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        
        with(binding){
            toggleButton.setOnCheckedChangeListener { buttonView, isChecked -> //토글 버튼 리스너 함수
                textToggle.text = if(isChecked) "On" else "Off"
            }
        }
    }
}

 

ON/OFF를 설정하는 버튼인 만큼 동작원리 역시 간단합니다. 이전에 했던 것과 마찬가지로 토글 버튼에 리스너를 달아주고, buttonView에는 토글 버튼 자체를, isChecked에는 토글 버튼의 상태를 넘겨줍니다. isChecked의 값이 true일 때는 토글 버튼에 할당한 textToggle 텍스트뷰에 On을 띄우고, isChecked의 값이 false일 때는 토글 버튼에 할당한 textToggle 텍스트뷰에 Off를 띄웁니다.

 

 

에뮬레이터를 실행하여 토글 버튼을 눌러보면 텍스트뷰의 내용 역시 함께 변하는 것을 확인할 수 있습니다. 또한 토글 버튼은 텍스트뷰의 연결 없이도 버튼 자체의 text를 변경해줄 수도 있습니다.

 

스위치 버튼에 리스너를 설정하는 방법은 토글 버튼에 리스너를 설정하는 방법과 동일합니다. with 스코프 함수 안에 스위치 버튼의 리스너 함수를 작성해줍니다.

 

with(binding){
            toggleButton.setOnCheckedChangeListener { buttonView, isChecked -> //토글 버튼 리스너 함수
                textToggle.text = if(isChecked) "On" else "Off"
            }
            switchButton.setOnCheckedChangeListener { buttonView, isChecked -> //스위치 버튼 리스너 함수
                textSwitch.text = if(isChecked) "On" else "Off"
            }
        }

에뮬레이터를 실행하여 스위치 버튼을 눌러보면 토글 버튼과 동일하게 작동되는 것을 확인할 수 있습니다.

 

 

5. 프로그래스바


* Progress bar

작업의 진행 중임을 나타내는 위젯

 

프로그래스바는 우리가 흔히 생각하는 로딩창을 떠올리면 됩니다. 현재 어떤 작업이 진행 중이고, 이 작업이 완료되기까지 기다려야 한다는 것을 사용자에게 알리기 위해 사용합니다.

 

 

체크 박스를 생성할 때 했던 것처럼, 먼저 xml 파일에서 Palette > Layouts > LinearLayout 을 생성해줍니다. 우리에게 익숙한 로딩창을 구현하기 위해 vertical로 설정하고, constraint 값은 모두 0으로 설정합니다. 그 후 직전에 설정한 레이아웃 하위에 Widgets > ProgressBar (원형) 으로 프로그래스바를 생성합니다. 텍스트뷰도 하나 생성하여 그 내용을 "Downloading..."으로 설정해주었습니다. 이제 메인 액티비티로 넘어가 코드를 작성합니다.

 

class MainActivity : AppCompatActivity() {

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

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

        with(binding){ //<- 메인 스레드 (시작)
            showProgress(true) //프로그래스바가 보이도록 설정
            thread(start=true){ //<- 서브 스레드 (분기)
                Thread.sleep(3000) //3초동안 대기
                runOnUiThread { //화면에 영향을 미치는 코드를 메인 스레드로 합류
                    showProgress(false)  //프로그래스바가 보이지 않도록 설정
                }

            } //<-서브 스레드 (종료)       
        }//<- 메인 스레드 (끝)
    }

    fun showProgress(show: Boolean){
        binding.progressLayout.visibility = if(show) View.VISIBLE else View.GONE
    }
}

 

우리가 구현하려는 기능은 대부분의 로딩 화면이 그렇듯이 프로그래스바를 화면에 띄웠다가 일정 시간이 지난 후 사라지게 만드는 것입니다. 우선 하단에 작성한 showProgress 함수를 살펴보겠습니다.

 

fun showProgress(show: Boolean){
   binding.progressLayout.visibility = if(show) View.VISIBLE else View.GONE
   }

 

showProgress 함수는 우리가 설정한 progressLayout을 화면에 노출하고 숨기는 기능을 수행하기 위한 함수입니다. 함수의 파라미터로 넘어오는 Boolean 값에 따라 레이아웃의 visibility 속성을 설정해주는 것이 해당 함수의 작동원리입니다. visibility 속성은 View.VISIBLE 또는 View.GONE 등으로 설정할 수 있습니다. 전자는 화면에 객체를 띄우는 설정값, 후자는 화면에서 객체를 삭제하는 설정값입니다. 이제 메인 액티비티 클래스를 살펴보겠습니다.

 

        with(binding){ //<- 메인 스레드 (시작)
            showProgress(true) //프로그래스바가 보이도록 설정
            thread(start=true){ //<- 서브 스레드 (분기)
                Thread.sleep(3000) //3초동안 대기
                runOnUiThread { //화면에 영향을 미치는 코드를 메인 스레드로 합류
                    showProgress(false)  //프로그래스바가 보이지 않도록 설정
                }
            } //<-서브 스레드 (합류)       
        }//<- 메인 스레드 (끝)

 

일정시간 로딩 상태가 유지되고 종료하는 작업을 실행하기 위해서는 thread 함수가 필요합니다. 스레드란 코드 진행에 따라 이어지는 길이라고 이해할 수 있습니다. with 스코프 함수를 사용함으로써 메인 스레드가 시작되는데, 메인 스레드는 화면에 UI를 만들어내는 과정을 수행합니다. 오직 메인 스레드만이 UI 생성에 관여할 수 있습니다.

 

Thread.sleep 함수는 해당 스레드를 지정한 시간만큼 일시 정지하는 동작을 수행하는데, 만약 메인 스레드 내에서 Thread.sleep 함수가 호출되면 메인 스레드 자체가 sleep 상태에 들어가므로 프로그램 내부에서 동작하던 UI 생성 과정까지 멈추게 됩니다. 따라서 메인 스레드는 그대로 실행되는 상황에서 새로운 분기점을 주어 또 다른 길을 만들어야 합니다. 특정 지점에서 두 갈래로 길이 나뉘고, 각자 뻗어나가다 또 다른 특정 지점에서 합류하는 그림을 떠올리면 됩니다. 이 때 새로 분기하는 길을 서브 스레드라고 하고, 서브 스레드가 시작하는 특정 지점, 즉 분기점을 설정해주는 함수가 세번째 줄의 thread(start=true) 함수입니다. 해당 함수에 start=true 파라미터를 입력함으로써 서브 스레드가 실행 시작됩니다. 이 함수의 블럭 안에 Thread.sleep(3000)을 입력하여 UI에 관여하지 않고 서브 스레드의 진행만으로 3초간 스레드를 sleep 상태로 만들 수 있습니다.

 

3초가 지났다면 우리는 프로그래스바를 화면에서 숨겨야 합니다. 하지만 우리는 서브 스레드에 머물고 있고, 서브 스레드에서는 UI에 접근할 수 없습니다. 이 때 사용하는 함수가 바로 runOnUiThread 함수입니다. 해당 함수는 블럭 내에 작성된 코드를 메인 스레드에서 실행하도록 만들어줍니다. 즉, thread(start=true)가 분기점이라면 runOnUiThread 함수는 합류점이 됩니다. runOnUiThread 함수 내부에서 showProgress(false) 를 실행하면 이 코드는 메인 스레드에서 실행되어 프로그래스바를 화면에서 지웁니다.

 

 

에뮬레이터를 실행하면 입력한 코드대로 프로그래스바가 3초간 돌다 사라지는 것을 확인할 수 있습니다.

 

 

6. 시크바


* Seek bar

슬라이더 형태의 게이지바

 

시크바는 주로 볼륨과 밝기 조절, 음악과 동영상 재생 제어에서 자주 찾아볼 수 있는 위젯입니다. 이번에는 시크바를 이용해 구간 탐색을 하면 해당 구간의 값을 화면에 출력하도록 코드를 작성해보겠습니다. 코드를 작성하기 이전에 위젯을 생성합니다.

 

 

xml의 Palette > Widgets > SeekBar 를 선택해 화면에 생성해줍니다. SeekBar (Discrete)는 구간 표시가 되어 있는 시크바입니다. 시크바의 특성상 가로가 화면에 꽉 차도록 설정합니다. 텍스트뷰도 하나 생성하여 내용을 '0'으로 입력해주겠습니다. 그 후 메인 액티비티로 넘어와 코드를 작성합니다.

 

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 {
            })
        }
    }
}

 

위의 코드처럼 시크바에 setOnSeekBarChangeListener 라는 리스너 함수를 달아주고 해당 리스너 함수의 파라미터로 오브젝트 타입의 SeekBar.OnSeekBarChangeListener{} 을 넘겨주면 코드에 오류가 발생합니다.

 

안드로이드 스튜디오 자체에서 제공하는 오류 해결 메시지

오류의 발생원인은 onSeekBarChangeListener 라는 인터페이스를 사용하기 위해 구현해야 하는 함수가 존재하지 않는다는 것인데, 메시지 속 implement members를 클릭하면 우리가 구현해야 하는 함수들의 목록이 나열됩니다.

 

 

이 함수들을 전부 선택하여 OK를 누르면 화면에 자동으로 함수들이 오버라이드 됩니다. 이제부터 오버라이드 된 함수 블록 내에 코드를 작성하면 되겠습니다.

 

        with(binding){
            seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
                override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                    if(fromUser){ // 사람이 터치로 동작시킬 때만 코드 실행
                        textView.text = "$progress" // 이동한 구간 값을 텍스트뷰에 나타내기
                    }
                }
                override fun onStartTrackingTouch(p0: SeekBar?) {}
                override fun onStopTrackingTouch(p0: SeekBar?) {}
            })
        }

 

우리가 사용할 함수는 첫번째 onProgressChanged 함수입니다. 세 개의 파라미터 seekBar, progress, fromUser은 각각 시크바 자체, 현재 탐색 구간값, 사용자가 시크바를 직접 터치하여 움직이는지 확인하는 boolean 값입니다. 텍스트뷰는 progress를 표시하도록 설정하고, 해당 코드를 실행시켜 에뮬레이터를 확인합니다.

 

 

에뮬레이터에서 직접 시크바를 조작해보면, 0부터 100까지 마우스로 점을 끌어다놓은 선택 위치에 따라 값이 변화하는 모습을 볼 수 있습니다. 만약 0부터 100까지가 아닌 다른 숫자를 최대로 하는 구간을 설정하고 싶다면, 다시 xml로 돌아와 seekBar를 선택하고 오른쪽 Attributes 검색창에서 'max'를 검색합니다.

 

 

미지정되어있는 max의 값을 새로이 바꾸고 다시 에뮬레이터를 실행해보면 변경된 시크바의 최댓값을 확인할 수 있습니다.

 

 

 

7. 레이팅바


* Rating bar

별점 주기 위젯

 

 

xml 파일에서 Palette > Widgets > RatingBar를 화면에 하나 생성합니다. 별점 수를 출력할 텍스트뷰 하나도 생성하여 constraint를 레이팅바와 연결해준 뒤 메인 액티비티에서 코드를 작성하겠습니다.

 

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"
            }
        }
    }
}

 

레이팅바의 리스너 함수인 setOnRatingBarChangeListener 함수는 시크바의 리스너 함수와 동일한 형태를 가지고 있습니다. 세 개의 파라미터 ratingBar, rating, fromUser은 각각 레이팅바 자체, 레이팅 값사용자가 레이팅바에 직접 터치하여 값을 주는지 확인하는 boolean 값입니다. 텍스트뷰는 별점의 숫자를 표시하도록 설정하고, 에뮬레이터를 실행시켜 확인합니다.

 

 

 

별을 직접 클릭하여 별점을 줄 수 있고, 오른쪽에 숫자가 표시되는 것을 확인할 수 있습니다.