띠오니 개발자 성장일지
article thumbnail
반응형

앱이 실행되는 동안 ImageView를 업데이트 하는 방법을 알아본다.

다양한 조건에 따라 앱 동작을 맞춤설정하는 방법을 알아본다. (when)

Button으로 주사위를 굴리고 화면 이미지를 업데이트 하는 Dice Roller App

 

 

이제 버튼을 누를 때 마다 TextView가 아닌 이미지를 바꿔야하기 때문에, TextView 를 지우고 ImageView를 추가한다.

적절히 Constraint 제약을 줘 화면 가운데에 오도록 정렬한다.

주사위 이미지 추가

주사위 이미지 다운로드

여기에서 주사위 이미지를 다운로드 할 수 있다.

앱에 주사위 이미지 추가

  1. Android 스튜디오의 메뉴에서 View > Tool Windows > Resource Manager를 클릭하거나 Project 창 왼쪽에 있는 Resource Manager 탭을 클릭합니다.
  2. Resource Manager 아래의 +를 클릭하고 Import Drawables를 선택합니다. 파일 브라우저가 열립니다.
  3. 파일 6개를 찾아 선택하고 Open을 누른다
  4. Next 클릭 하고 Import 클릭해 6개의 리소스 가져오기를 확인한다.
  5. 파일 가져오기가 완료되면 이미지 6개가 Resource Manager(app>res>drawable)에 표시된다.

이 이미지는 Resource ID로 코틀린 코드에서 참조할 수 있다.

R.drawable.dice_1 ... 

 

주사위 이미지 사용

샘플 아바타 이미지 바꾸기

ImageView의 srcCompat 속성에 dice_1 이미지를 선택하면, ImageView가 전체화면을 차지하게 된다.

(srcCompat은 앱을 빌드할 때만 개발자에게 표시되고, 실제로 에뮬레이터나 기기에서 앱을 실행할 때는 표시되지 않는다.)

ImageView 너비와 높이를 조정해 버튼이 가려지지 않도록 한다.

밀도 독립형 픽셀(dp)을 단위로 사용하여 이런 크기를 정의하면, 픽셀 해상도가 다른 기기에서 이미지 크기가 적절하게 조정된다.

 

버튼을 클릭할 때 주사위 이미지 변경

자 이제 버튼을 누를 때마다 이미지를 바꿔보자.

앞서 숫자가 적힌 textView를 지웠기 때문에 MainActivity.kt 에서 여전히 참조하는 TextView도 지워주자.

드래그 된 4줄을 지워주자. 앞서 문제에서 연습문제까지 풀어서 resultTextView2까지 있다. 

 

private fun rollDice() {
    val dice = Dice(6)
    val diceRoll = dice.roll()

    val diceImage : ImageView = findViewById(R.id.imageView)
    diceImage.setImageResource(R.drawable.dice_2)
}

위와 같이 수정해 버튼을 눌렀을 때 주사위2 그림이 나오는지 테스트한다.

빌드를 돌려보자!!

처음에는 아무 이미지도 뜨지 않은채로 실행
버튼을 누르면 주사위 2가 나오는 것을 확인!

 

주사위 굴리기에 따라 올바른 주사위 이미지 표시

앞의 주사위 굴리기 조건부 동작추가 CodeLab에서 배운대로 제어 흐름 로직을 사용하면, 주사위를 굴릴 수 있다.

코드를 입력하기 전에 발생해야 하는 작업을 설명하는 슈도코드(의사코드)를 작성해 앱의 동작 방식에 관해 개념적으로 생각해볼 수 있다.

사용자가 1을 굴리면, dice_1 이미지 표시

사용자가 2을 굴리면, dice_2 이미지 표시

사용자가 3을 굴리면, dice_3 이미지 표시 ... 

 

위 슈도코드는 주사위 값에 따라서 Kotlin에서 if/else 문으로 작성할 수 있다.

또는 when 문으로도 가능하다. when문으로 rollDice() 메소드를 업데이트 해보자.

private fun rollDice() {
        val dice = Dice(6)
        val diceRoll = dice.roll()

        val diceImage : ImageView = findViewById(R.id.imageView)

        when(diceRoll){
            1-> diceImage.setImageResource(R.drawable.dice_1)
            2-> diceImage.setImageResource(R.drawable.dice_2)
            3-> diceImage.setImageResource(R.drawable.dice_3)
            4-> diceImage.setImageResource(R.drawable.dice_4)
            5-> diceImage.setImageResource(R.drawable.dice_5)
            6-> diceImage.setImageResource(R.drawable.dice_6)
        }
    }

 

코드 최적화

when (diceRoll) {
    1 -> diceImage.setImageResource(R.drawable.dice_1)
    2 -> diceImage.setImageResource(R.drawable.dice_2)
    3 -> diceImage.setImageResource(R.drawable.dice_3)
    4 -> diceImage.setImageResource(R.drawable.dice_4)
    5 -> diceImage.setImageResource(R.drawable.dice_5)
    6 -> diceImage.setImageResource(R.drawable.dice_6)
}

각 사례 간에 변경되는 유일한 내용은 이미지의 리소스 ID이다. 사용할 리소스 ID를 저장할 변수를 만들 수 있다.

코드에서는 diceImage.setImageResource() 를 한번만 호출해 올바른 리소스 ID를 전달할 수 있다.

 

val drawableResource = when (diceRoll) {
    1-> R.drawable.dice_1
    2-> R.drawable.dice_2
    3-> R.drawable.dice_3
    4-> R.drawable.dice_4
    5-> R.drawable.dice_5
    6-> R.drawable.dice_6
}

diceImage.setImageResource(drawableResource)

여기서 알아야 할 것은, when 표현식이 실제로 값을 반환할 수 있다는 점이다. when 표현식은 올바른 리소스 ID를 반환하고, 이 ID는 drawableResource 변수에 저장된다. 그럼, 이 변수를 이용해 이미지를 업데이트 할 수 있다.

when은 빨간 밑줄이 그어진다. 오류 메시지는 when 표현식은 완전해야 한다. 필요한 else 분기를 추가하라.('when' expression must be exhaustive, add necessary 'else' branch)

오류가 발생한 이유는, when 표현식 값이 drawableResource에 할당되기 때문이다. when은 완전해야 한다. 가능한 모든 사례를 처리해 주사위 면이 6개에서 12개로 바뀌어도 항상 값이 반환되도록 해야한다. 

6의 케이스를 else 로 변경해 이 오류를 해결 할 수 있다.

앱이 잘 실행 되는지 확인해보자.

숫자가 랜덤으로 바뀌는 모습

 

ImageView의 적절한 콘텐츠 설명

스크린 리더가 이미지를 설명할 수 있도록 콘텐츠 설명을 추가하자.

diceImage.contentDescription = diceRoll.toString()

 

 

조금 더 보완하기 

더 유용한 시작 환경 만들기

지금 만든 앱은 처음에 시작할 때 이미지가 비어있고 Roll 버튼만 있어서 허전해보인다.

사용자가 앱을 처음 시작해 Activity를 만들 때, 랜덤 주사위 이미지를 표시하도록 UI를 변경할 수 있다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val rollButton : Button = findViewById(R.id.button)
    rollButton.setOnClickListener {
        rollDice()
    }

    // 버튼을 누르지 않고도 화면(Activity)에 들어오면 주사위가 굴려지도록 한다.
    rollDice()
}

 

코드에 주석 달기

/**
 * 주사위를 굴리고, 결과를 화면에 업데이트 한다.
 * */
private fun rollDice() {
    // 새로운 6면의 Dice 주사위 객체를 만들고, 굴린다
    val dice = Dice(6)
    val diceRoll = dice.roll()

    // 레이아웃에서 이미지뷰를 찾는다
    val diceImage : ImageView = findViewById(R.id.imageView)

    // 주사위 굴려진 결과가 어떤 주사위 이미지가 사용되는지 찾아낸다
    val drawableResource = when (diceRoll) {
        1-> R.drawable.dice_1
        2-> R.drawable.dice_2
        3-> R.drawable.dice_3
        4-> R.drawable.dice_4
        5-> R.drawable.dice_5
        else -> R.drawable.dice_6
    }

    // 숫자에 맞는 drawable ID로 주사위 이미지 변경
    diceImage.setImageResource(drawableResource)

    // 컨텐츠 설명 추가
    diceImage.contentDescription = diceRoll.toString();
}

 

 

요약

setImageResource() 를 사용해 ImageView에 표시되는 이미지를 변경할 수 있다.

if/else 표현식이나, when 표현식과 같은 제어 흐름문으로 앱의 다양한 케이스를 처리한다.

 

자세히 알아보기

Android Kotlin 용어집

Android Kotlin if 표현식

Android Kotlin when 표현식

ImageView.setImageResource()

접근성

 

 

연습문제

  1. 앱에 다른 주사위를 추가하여 Roll 버튼 하나로 주사위 두 개의 결과를 제공하도록 합니다. 레이아웃에 ImageViews가 몇 개 필요한가요? MainActivity.kt 코드에 어떤 영향을 미치나요?
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rollButton : Button = findViewById(R.id.button)
        rollButton.setOnClickListener {
            rollDice()
        }

        // 버튼을 누르지 않고도 화면에 들어오면 주사위가 굴려지도록 한다.
        rollDice()
    }

    /**
     * 주사위를 굴리고, 결과를 화면에 업데이트 한다.
     * */
    private fun rollDice() {
        // 새로운 6면의 Dice 주사위 객체를 만들고, 굴린다
        val dice = Dice(6)
        val dice2 = Dice(6)
        val diceRoll = dice.roll()
        val diceRoll2 = dice2.roll()


        // 레이아웃에서 이미지뷰를 찾는다
        val diceImage : ImageView = findViewById(R.id.imageView)
        val diceImage2 : ImageView = findViewById(R.id.imageView2)

        // 주사위 굴려진 결과가 어떤 주사위 이미지가 사용되는지 찾아낸다
        val drawableResource = when (diceRoll) {
            1-> R.drawable.dice_1
            2-> R.drawable.dice_2
            3-> R.drawable.dice_3
            4-> R.drawable.dice_4
            5-> R.drawable.dice_5
            else -> R.drawable.dice_6
        }

        val drawableResource2 = when (diceRoll2) {
            1-> R.drawable.dice_1
            2-> R.drawable.dice_2
            3-> R.drawable.dice_3
            4-> R.drawable.dice_4
            5-> R.drawable.dice_5
            else -> R.drawable.dice_6
        }

        // 숫자에 맞는 drawable ID로 주사위 이미지 변경
        diceImage.setImageResource(drawableResource)
        diceImage2.setImageResource(drawableResource2)

        // 컨텐츠 설명 추가
        diceImage.contentDescription = diceRoll.toString()
        diceImage2.contentDescription = diceRoll2.toString()
    }
}

class Dice(private val sidesNum: Int) {
    fun roll(): Int{
        return (1..sidesNum).random()
    }
}

버튼을 누를 때 마다 결과가 다르게 나온다

 

 

이미지 추가하기 과정 끝!

반응형
profile

띠오니 개발자 성장일지

@띠오니

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!