본문 바로가기

안드로이드 Android/Android Study Jams

4. Navigation - Start an external activity

Safe Args 플러그인 설정하고 사용하기

 

한 Fragment에서 다른 Fragment로 데이터를 넘기는 방법 중 하나는 Bundle 클래스의 인스턴스를 사용하는 것이다. Android Bundle은 key-value store이다.

dictionary 또는 associative array로도 알려진 key-value store는 연관된 값을 추출하는 고유한 키(문자열)를 사용하는 자료 구조이다. 이 기술은 효과가 있지만 앱이 실행될 때 에러를 유발할 수 있다.

발생할 수 있는 에러로는 Type mismatch errors와 Missing key error가 있다.

 

이런 문제를 돕기 위해 Android의 Navigation Architecture Component는 Safe Args라는 특성을 포함한다. Safe Args는 컴파일타임에는 발생하지 않는 반면 앱이 실행되는 동안 발생할 수 있는 에러를 감지하는 것을 돕는 코드와 클래스를 생성하는 Gradle 플러그인이다.

 

Step 1: 스타터 앱을 열고 실행하기

앱을 실행하고 게임을 한다. 세 문제를 맞춰 게임에서 이겼을 때, Congratulations 화면이 보인다. 이 코드랩에서는 share 아이콘을 Congratulations 화면 상단에 추가할 것이다. share 아이콘은 사용자가 그들의 결과를 이메일이나 문자 메시지로 공유할 수 있게 한다.

 

Step 2: 프로젝트에 Safe Args 추가하기

프로젝트 레벨의 build.gradle 파일을 열고 다음 dependency를 추가한다.

// Adding the safe-args dependency to the project Gradle file
dependencies {
   ...
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"

}

앱 레벨의 build.gradle 파일을 연다. 파일의 상단에서부터 다른 모든 플러그인 뒤에 apply plugin 상태를 추가한다.

// Adding the apply plugin statement for safeargs
apply plugin: 'androidx.navigation.safeargs'

프로젝트를 다시 빌드한다. 추가로 빌드 툴을 설치하라고 하면 설치한다.

 

앱 프로젝트는 이제 생성된 NavDirection 클래스를 포함한다.

Safe Args 플러그인은 각 Fragment에 대해 NavDirection 클래스를 생성한다. 이 클래스들은 모든 앱의 동작으로부터의 navigation을 나타낸다.

예를 들어, GameFragment는 이제 생성된 GameFragmentDirections 클래스를 가진다. 앱에서 게임 Fragment와 다른 프래그먼트 간에 type-safe 인자를 넘기는 GameFragmentDirections 클래스를 사용하면 된다.

생성된 파일을 보려면 [Project > Android] 페인에서 generatedJava 폴더를 찾아보면 된다.

Caution: NavDirection 클래스를 수정하지 말 것. 이 클래스는 프로젝트가 컴파일 될 때마다 다시 생성되고 직접 수정한 내용은 사라질 것이다.

 

Step 3: 게임 Fragment에 NavDirection 클래스 추가하기

GameFragment.kt 파일을 연다. onCreateView() 메서드 안에 게임 승리 조건 부분으로 이동한다. navigate()의 매개변수를 바꾼다.

// Using directions to navigate to the GameWonFragment
view.findNavController()
        .navigate(GameFragmentDirections.actionGameFragmentToGameWonFragment())

이와 같이, 게임 패배 조건 부분으로 간다. action ID를 GameFragmentDirections 클래스의 게임오버 메서드를 사용하는 ID로 바꾼다.

// Using directions to navigate to the GameOverFragment
view.findNavController()
        .navigate(GameFragmentDirections.actionGameFragmentToGameOverFragment())

 

인자 추가하고 넘겨주기

Step 1: 게임 승리 Fragment에 인자 추가하기

navigation.xml을 연다. Design 탭에서 gameWonFragment를 선택한다.

Attribute 페인에서 다음과 같이 인자를 추가한다. 이 인자는 사용자가 답한 문제의 수를 나타낸다.

같은 방법으로 사용자가 올바르게 답한 문제의 수를 나타내는 인자인 numCorrect 인자를 추가한다.

 

Step 2: 인자 넘겨주기

GameFragment.kt 파일을 열고 게임 승리 조건 부분으로 간다.

actionGameFragmentToGameWonFragment()에 numQuestions와 questionIndex 매개변수를 넘겨준다.

// Adding the parameters to the Action
view.findNavController()
      .navigate(GameFragmentDirections
            .actionGameFragmentToGameWonFragment(numQuestions, questionIndex))

문제의 총 수를 numQuestions로, 현재 시도되고 있는 문제를 questionIndex로 넘긴다. 맞춘 문제의 수는 항상 답한 문제의 수와 같다. (이 게임 로직을 나중에 바꿀 수 있음)

GameWonFragment.kt에서 번들로부터 인자를 추출하고 나서 인자를 보여주는 Toast를 사용한다. onCreateView() 메서드에서 return 문이 나오기 전에 다음 코드를 넣는다.

val args = GameWonFragmentArgs.fromBundle(requireArguments())
Toast.makeText(context, "NumCorrect: ${args.numCorrect}, NumQuestions: ${args.numQuestions}", Toast.LENGTH_LONG).show()

 

Step 3: Fragment 클래스를 NavDirection 클래스로 교체하기

TitleFragment, GameOverFragment, GameWonFragment의 navigate() 메서드의 인자들을 같은 방법으로 교체해준다.

 

게임 결과 공유하기

암시적 인텐트

Android는 다른 앱이 제공하는 액티비티로 이동하는 인텐트를 사용하는 것을 허용한다. 사용자가 게임 플레이 결과를 친구들과 공유할 수 있도록 이 기능을 AndroidTrivia 앱에서 사용할 것이다.

Intent는 Android 컴포넌트 간에 커뮤니케이션을 위해 사용하는 간단한 메시지 객체이다. 인텐트 타입에는 두 가지가 있다. 명시적 인텐트와 암시적 인텐트이다. 명시적 인텐트를 사용하면 특정한 타깃으로 메시지를 보낼 수 있다. 암시적 인텐트로는, 무슨 앱 혹은 Activity가 태스크를 조작할 지 모르는 채로 Activity를 초기화할 수 있다. 다수의 Android 앱이 같은 암시적 인텐트를 수행할 수 있을 때, Android는 사용자에 선택지를 줘서 사용자가 요청을 수행할 앱을 선택할 수 있다.

각 암시적 인텐트는 수행되어야 하는 것의 타입을 설명하는 ACTION을 반드시 가져야 한다. ACTION_VIEW, ACTION_EDIT 그리고 ACTION_DIAL 같이 흔한 액션은 Intent 클래스에 정의되어 있다.

Terminology alert! Intent 액션은 앱의 내비게이션 그래프에서 보여지는 액션과 관계 없음

참고: developer.android.com/training/basics/intents/sending

 

다른 앱으로 사용자 보내기  |  Android 개발자  |  Android Developers

Android의 가장 중요한 특징 중 하나는 실행하려는 '작업'에 기반하여 사용자를 다른 앱으로 보낼 수 있는 앱의 기능입니다. 예를 들어, 지도에 표시하려는 비즈니스 주소가 있는 앱에서는...

developer.android.com

 

Step 1: Congrtulations 화면에 옵션 메뉴 추가하기

GameWonFragment.kt 파일을 연다. onCreateView() 메서드 안에서 return 전에 다음 코드를 추가한다.

setHasOptionsMenu(true)

 

Step 2: 암시적 인텐트를 빌드하고 호출하기

GameWonFragment 클래스 안에서 onCreateView() 메서드 뒤에 다음 코드를 추가한다.

// Creating our Share Intent
private fun getShareIntent() : Intent {
   val args = GameWonFragmentArgs.fromBundle(requireArguments())
   val shareIntent = Intent(Intent.ACTION_SEND)
   shareIntent.setType("text/plain")
            .putExtra(Intent.EXTRA_TEXT, getString(R.string.share_success_text, args.numCorrect, args.numQuestions))
   return shareIntent
}

이 코드에 다음 코드를 추가한다.

// Starting an Activity with our new Intent
private fun shareSuccess() {
   startActivity(getShareIntent())
}

스타터 코드는 이미 winner_menu.xml 메뉴 파일을 포함하고 있다. winner_menu를 inflate하기 위해 GameWonFragment 클래스의 onCreateOptionsMenu()를 오버라이드 한다.

패키지 매니저에 접근하기 위해 Activity의 packageManager 속성을 사용하고 resolveActivity()를 호출한다. 결과가 shareIntent가 작동되지 않음을 의미하는 null과 같다면, inflate 된 메뉴에서 공유되는 메뉴 아이템을 찾고 메뉴 아이템을 안 보이게 만든다.

// Showing the Share Menu Item Dynamically
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
       super.onCreateOptionsMenu(menu, inflater)
       inflater.inflate(R.menu.winner_menu, menu)
       if(getShareIntent().resolveActivity(requireActivity().packageManager)==null){
            menu.findItem(R.id.share).isVisible = false
       }
}

메뉴 아이템을 조작하기 위해 GameWonFragment 클래스에서 onOptionsItemSelected()를 오버라이드 한다. 메뉴 아이템이 클릭 되면 shareSuccess() 메서드를 호출한다.

// Sharing from the Menu
override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.share -> shareSuccess()
        }
        return super.onOptionsItemSelected(item)
}

 

 

Homework

Answer these questions

Q1. 1, 3, 4 / Q2. 1, 4 / Q3. 3

 

Navigation quiz에서

이 문제는 답이 1,2,4인 것 같은데 4가 왜 맞는지 모르겠다.