로또 번호 생성 앱에서 했던 것 처럼, drawble 폴더에 xml 파일을 만들어주면 된다. 이번엔 단색으로 칠하는 solid가 아닌, gradient 속성을 사용했다.
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#ff2d9a59"
android:endColor="#ff23729a"
android:angle="90" />
</shape>
Intent 객체를 생성할 때는 컨텍스트와 이동할 대상 액티비티가 필요하다. 그래서 여태 this
를 사용해 컨텍스트를 넘겨줬었는데! 이번엔 this
뒤에 @MainActivity
를 붙여줬다. 이게 과연 뭘 뜻하는 것일까?
btn_start.setOnClickListener {
val intent = Intent(this@MainActivity, TestActivity::class.java)
startActivity(intent)
}
찾아보니, this
가 중첩 클래스 같은 곳에서 사용될 때 부모 클래스를 뜻하는지, 자식 클래스를 뜻하는지 헷갈릴 수 있어서 @
를 사용해 어떤 클래스를 뜻하는지 지정해준다고 한다. 보기에도 아! MainActivity를 나타내는구나! 하고 명확하게 알 수 있어서 좋은 것 같다.
viewPager 라는 뷰를 한 액티비티 내에서 전역으로 사용하고 싶은데, 레이아웃에 접근할 수 있는 건 onCreate()
에서 setcontentView()
이후에 가능하다. 이럴 때 사용할 수 있는 게 lateinit
이다.
private lateinit var viewPager: ViewPager2
override fun onCreate(savedInstanceState: Bundle?) {
...
// 화면이 출력된 이후에 viewPager 에 id로 접근할 수 있다.
viewPager = findViewById(R.id.viewPager)
}
lateinit 은 변수에 객체를 할당하는 것을 선언과 동시에 할 수 없을 때 사용하고, 초기 값의 할당은 나중에 할 수 있도록 한다. 단, 초기 값 할당 전까지 변수를 사용할 수 없고, String을 제외한 기본 자료형에는 사용할 수 없다고 한다.
로또 앱을 만들 때, 액티비티를 이동한다고 해서 기존의 액티비티가 사라지는 게 아니라고 했었다. 조금 더 자세히 말해보면 액티비티는 스택(Stack) 구조로 되어있는 태스크(Task)에 차곡차곡 쌓이게 된다고 한다.
태스크에 쌓여져 있는 액티비티의 중복을 방지한다던가 흐름을 제어하고 싶을 때! 바로 이 flag를 사용한다. 찾아보니 종류가 엄청 많았다. 필요할 때마다 찾아서 사용하면 될 것 같다. 우선은 오늘 사용된 플래그만 알아보도록 하자.
다음은 intent에 FLAG_ACTIVITY_CLEAR_TASK
와 FLAG_ACTIVITY_NEW_TASK
를 같이 적용하는 코드이다.
val btn_retry: Button = findViewById(R.id.btn_res_retry)
btn_retry.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
그렇다면 각 플래그가 어떤 의미인지 살펴보자.
따라서 재시작 버튼을 누르게 되면, 기존 태스크의 모든 액티비티(시작 화면, 질문 화면, 결과 화면)를 제거한 후, 새로운 태스크에서 새로운 액티비티를 시작하게 된다!
코틀린에는 nullable 변수라는 것이 존재한다. null을 허용하는 변수이지만, 연산 전에는 꼭 null 값인지 확인해줘야 하는데 이걸 조금 더 편하게 할 수 있게 하는 연산자들이 있다. 오늘은 그 중에서도 엘비스 연산자가 쓰여서, 요것만 알아보겠다.
엘비스 연산자는 객체가 null이 아니라면 그대로 사용하지만, null이라면 연산자 우측의 객체로 대체되어 사용된다.
// results에 담긴 값이 없다면 빈 arraylist를 할당한다.
val results = intent.getIntegerArrayListExtra("results") ?: arrayListOf()
결과로 나온 MBTI에 맞는 이미지를 보여주려면 어떻게 해야 할까? 유형이 16개나 되는데 하나하나 when 으로 확인해가면서 넣어주어야 할까?
설마 그럴까. 다 방법이 있었다. 이미지 파일명이 규칙성이 있을 때, getIdentifier()
를 사용하면 쉽게 파일을 불러와 사용할 수 있다고 한다.
val iv_resImg: ImageView = findViewById(R.id.iv_resImg)
// getIdentifier(리소스 이름, 리소스 타입, 패키지 이름)
val imageResource = resources.getIdentifier("ic_${resultString.toLowerCase(Locale.ROOT)}", "drawable", packageName)
iv_resImg.setImageResource(imageResource)
Intent에 대해 아직 모르는 기능이 많은 것 같다. 모든 기능을 항상 머리에 들고 다닐 수는 없겠지만, Intent가 이런 역할을 하니 내가 원하는 이런 기능이 Intent에 있겠구나!라는 생각이 들 정도로는 알아놓고 싶다.
가장 기억에 남는 건 getIdentifier()
같다. 동적으로 파일명을 생성할 수 있다는 게 많은 부분에 쓰일 것 같은 녀석이다.