안드로이드 3주차 정리

Fragment

학습목표

  • Activity 내에서 Fragment를 만들 수 있다.
  • navigation 라이브러리와 navigation editor를 사용하여 fragment에 대한 navigation graph를 만들 수 있다.
  • 앱에서 navigation path를 만들 수 있다.
  • options menu를 사용하여 navigation을 추가할 수 있다.
  • 사용자가 앱의 어디에서나 타이틀 화면으로 돌아갈 수 있도록 Up button을 구현할 수 있다.
  • navigation drawer menu를 추가할 수 있다.

Fragment

Fragment는 Activity에서 동작 또는 UI(사용자 인터페이스)의 일부를 나타냅니다. Fragment는 앱 UI의 재사용 가능한 부분입니다.
Fragment는 자체 레이아웃을 정의 및 관리하고 자체 수명 주기를 가지며 자체 입력 이벤트를 처리할 수 있습니다. Fragment는 단독으로 존재할 수 없으며 Activity 또는 다른 Fragment에서 호스팅해야 합니다. Fragment의 뷰 계층은 호스트의 뷰 계층의 일부가 되거나 연결됩니다.

Fragment는 다른 Activity에서 사용할 수 있는 하위 Activity의 개념과 같은 모듈화 된 부분이라고 생각하세요.

  • Fragment에는 자체 수명 주기가 있으며 자체 입력 이벤트를 수신합니다.
  • Activity가 실행되는 동안 프래그먼트를 추가하거나 제거할 수 있습니다.
  • Fragment는 코틀린 클래스로 정의됩니다.
  • Fragment의 UI는 XML 레이아웃 파일로 정의됩니다.

Navigation

Navigation은 사용자가 앱 내의 다양한 콘텐츠를 탐색하고, 들어가고, 뒤로 이동할 수 있도록 하는 상호 작용을 나타냅니다.
Android Jetpack의 Navigation component는 단순한 버튼 클릭부터 앱 바와 Navigation drawer과 같은 더 복잡한 패턴에 이르기까지 Navigation을 구현하는 데 도움이 됩니다.

Navigation component의 주요 3요소.

  • Navigation graph : xml 파일로 네비게이션과 관련된 정보를 담고 있다. 레이아웃 에디터와 비슷하게 캔버스 안에 화면들을 배치하고, 선으로 연결하여 경로를 설정 할 수 있다.
  • NavHost : Navigation graph의 목적지를 나타내는 빈 컨테이너.
  • NavController : NavHost 내에서 앱 탐색을 관리하는 개체이다. NavController는 사용자가 앱 전체에서 이동할 때 NavHost에서 대상 콘텐츠의 스와핑을 조정한다.

Navigation component의 장점.

  • fragment transactions 관리가 편함.
  • Up and Back actions을 정확하게 잘 처리함.
  • 애니메이션 및 전환을 위한 표준화된 리소스를 제공합니다.
  • 딥링킹 구현 및 처리.
  • Safe Args
  • ViewModel 지원 - ViewModel의 범위를 탐색 그래프로 지정하여 그래프의 대상 간에 UI 관련 데이터를 공유할 수 있습니다.

프로젝트에 Navigation component 추가.

  • 의존성 추가
  1. build.gradle(project)에 아래의 코드를 추가한다.
ext {
        ...
        navigationVersion = "2.3.0"
        ...
    }
  1. build.gradle(app)에 아래의 코드를 추가한다.
dependencies {
  ...
  implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
  implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
  ...
}
  1. 상단의 Sync Now를 클릭한다.
  • Navigation Graph 추가
  1. res 디렉토리를 우클릭하여 New > Android Resource File을 클릭한다.
  2. New Resource File창에서 Resource type을 Navigation으로 선택한다.
  3. File name 필드에서 이름을 설정한다.
  4. res/navigation/filename.xml이 잘 생성됐는지 확인한다.

NavHostFragment 생성.

navigation host fragment은 navigation graph에서 fragment의 호스트 역할을 합니다. navigation host fragment의 이름은 일반적으로 NavHostFragment입니다.

  1. res > layout > activity_main.xml의 코드탭을 엽니다.
  2. 존재하는 fragment의 이름을 androidx.navigation.fragment.NavHostFragment로 바꿉니다.
  3. fragment의 아이디를 myNavHostFragment로 바꿉니다.
  4. navigation host fragment는 사용할 navigation graph 리소스를 알아야 합니다. app:navGraph 속성을 추가합니다.
  5. app:defaultNavHost 속성을 추가하고 "true"로 설정합니다. 이제 이 navigation host가 기본 host이며 시스템 back 버튼을 가로챕니다.

navigation graph에 fragment 추가.

  1. res/navigation/navigation.xml을 연다. navigation editor에서 New Destination 버튼을 누른다. fragment와 activity의 목록이 나온다.
  2. fragment_title을 선택합니다. TitleFragment는 앱 사용자가 앱을 처음 열 때 시작하는 위치이기 때문에 먼저 fragment_title을 추가합니다.
  3. New Destination 버튼을 사용하여 GameFragment를 추가합니다.

    미리보기에 "Preview Unavailable" 메시지가 표시되면 코드 탭을 클릭하여 탐색 XML을 엽니다. 아래와 같이 gameFragment의 프래그먼트 요소에 tools:layout="@layout/fragment_game"이 포함되어 있는지 확인합니다.

<fragment
   android:id="@+id/gameFragment"
   android:name="com.example.android.navigation.GameFragment"
   android:label="GameFragment"
   tools:layout="@layout/fragment_game" />
  1. 미리 보기에서 title fragment 위에 포인터를 놓습니다. Fragment 보기의 오른쪽에 원형 연결점이 나타납니다. 연결 지점을 클릭하고 gameFragment로 드래그하거나 gameFragment 미리보기의 아무 곳이나 드래그합니다. 두 조각을 연결하는 action이 생성됩니다.

play버튼에 clickhandler 추가.

  1. TitleFragment.kt 파일을 엽니다. onCreateView() 메서드 내에서 return 문 앞에 다음 코드를 추가합니다.
binding.playButton.setOnClickListener { view : View ->
       view.findNavController().navigate(R.id.action_titleFragment_to_gameFragment)
}
  1. 앱을 빌드하고 필요한 모든 import가 있는지 확인합니다. 예를 들어 TitleFragment.kt 파일에 다음 줄을 추가해야 할 수 있습니다.
import androidx.navigation.findNavController

Invoke-customs are only supported starting with Android O (--min-api 26) 발생 시 해결 방법

android {
        compileOptions {
            sourceCompatibility 1.8
            targetCompatibility 1.8
        }
    }

build.grade(Module)에 위의 코드를 붙여넣어준다.

conditional navigation 추가.

  1. GameWonFragment와 GameOverFragment를 navigation graph에 추가한다.
  2. GameFragment를 GameWonFragment와 GameOverFragment에 연결한다.
  3. Fragment에서 다른 Fragment로 이동하는 코드를 추가한다.
  • GameFragment.kt 파일을 엽니다.
  • 게임에서 승리하기 위한 else 조건 안에 gameWonFragment로 이동하는 다음 코드를 추가합니다. 작업 이름(이 예에서는 action_gameFragment_to_gameWonFragment)이 navigation.xml 파일에 설정된 것과 정확히 일치하는지 확인하십시오.
// We've won!  Navigate to the gameWonFragment.
view.findNavController()
   .navigate(R.id.action_gameFragment_to_gameWonFragment)
  1. 게임의 패배에 대한 else 조건 내에서 gameOverFragment로 이동하는 다음 코드를 추가합니다.
// Game over! A wrong answer sends us to the gameOverFragment.
view.findNavController().
   navigate(R.id.action_gameFragment_to_gameOverFragment)

back 버튼의 목적지 변경

Android 시스템은 사용자가 Android 기반 장치에서 탐색하는 위치를 추적합니다. 사용자가 기기의 새 목적지로 이동할 때마다 Android는 해당 목적지를 백 스택에 추가합니다.

navigation action은 백 스택을 수정할 수 있습니다. 이 작업에서는 GameFragment에서 탐색하는 작업을 변경하여 작업이 백 스택에서 GameFragment를 제거하도록 합니다. 사용자가 게임에서 이기거나 졌을 때 뒤로 버튼을 누르면 앱이 GameFragment를 건너뛰고 TitleFragment로 돌아갑니다.

  • 작업의 popUpTo 속성은 탐색하기 전에 백 스택을 지정된 대상으로 "pop up"합니다. (목적지는 백 스택에서 제거됩니다.)
  • popUpToInclusive 속성이 false이거나 설정되지 않은 경우 popUpTo는 지정된 대상까지 대상을 제거하지만 지정된 대상은 백 스택에 남깁니다.
  • popUpToInclusive가 true로 설정되면 popUpTo 속성은 백 스택에서 지정된 대상까지 포함하여 모든 대상을 제거합니다.
  • popUpToInclusive가 true이고 popUpTo가 앱의 시작 위치로 설정된 경우 작업은 백 스택에서 모든 앱 대상을 제거합니다. 뒤로 버튼은 사용자를 앱에서 완전히 나오게 합니다.
  1. res > navigation 폴더에서 navigation.xml을 엽니다. 탐색 그래프가 레이아웃 편집기에 나타나지 않으면 디자인 탭을 클릭합니다.
  2. gameFragment에서 gameOverFragment로 이동하기 위한 action을 선택합니다.
  3. 속성 창에서 popUpTo를 gameFragment로 설정합니다. popUpToInclusive를 체크합니다.
  4. gameWonFragment에 대해서도 똑같이 작업합니다.

더 많은 navigation action 추가 및 onClickHandler 추가

이 단계에서는 사용자 흐름의 두 단계를 더 구현합니다.

  • 사용자가 next match 또는 try again 버튼을 탭하면 앱이 GameFragment 화면으로 이동합니다.
  • 이 시점에서 사용자가 시스템 뒤로 버튼을 누르면 앱은 GameWon 또는 GameOver 화면으로 돌아가는 대신 TitleFragment 화면으로 이동합니다.
  1. Navigation.xml 파일 내에서 gameOverFragment를 gameFragment에 연결하는 navigation action을 추가합니다. action ID의 fragment name이 XML에 있는 fragment name과 일치하는지 확인하십시오. 예를 들어 action ID는 action_gameOverFragment_to_gameFragment일 수 있습니다.
  2. 속성 창에서 작업의 popUpTo 속성을 titleFragment로 설정합니다.
  3. gameWonFragment에 대해서도 똑같이 작업합니다.

이제 try again 및 next match 버튼에 기능을 추가하십시오. 사용자가 두 버튼 중 하나를 탭하면 앱이 GameFragment 화면으로 이동하여 사용자가 게임을 다시 시도할 수 있게 만들겁니다.

  1. GameOverFragment.kt 파일을 엽니다. onCreateView() 메서드 끝에서 return 문 앞에 다음 코드를 추가합니다. 코드는 try again 버튼에 clickListener를 추가합니다. 사용자가 버튼을 탭하면 앱이 GameFragment로 이동합니다.
// Add OnClick Handler for Try Again button
        binding.tryAgainButton.setOnClickListener{view: View->
        view.findNavController()
                .navigate(R.id.action_gameOverFragment_to_gameFragment)}
  1. GameWonFragment.kt 파일을 엽니다. onCreateView() 메서드의 끝에서 return 문 앞에 다음 코드를 추가합니다.
// Add OnClick Handler for Next Match button
        binding.nextMatchButton.setOnClickListener{view: View->
            view.findNavController()
                    .navigate(R.id.action_gameWonFragment_to_gameFragment)}

app bar

앱 바(= 액션 바)는 앱 브랜딩과 아이덴티티를 위한 전용 공간입니다. 예를 들어 앱 바의 색상을 설정할 수 있습니다. 앱 바를 통해 사용자는 옵션 메뉴와 같은 친숙한 navigation 기능에 액세스할 수 있습니다.

The Up button

현재 앱에서 사용자는 시스템 뒤로 버튼을 사용하여 이전 화면으로 이동합니다. 그러나 Android 앱에는 앱 바의 왼쪽 상단에 표시되는 화면 위 버튼이 있을 수도 있습니다.

앱에서 타이틀 화면을 제외한 모든 화면에 Up 버튼이 나타나게 만들겁니다. 타이틀 화면이 앱의 화면 계층 구조의 맨 위에 있기 때문에 사용자가 타이틀 화면에 도달하면 Up 버튼이 사라집니다.

Up button과 Back button의 차이

  • Up 버튼은 화면 간의 계층적 관계를 기반으로 앱 내에서 탐색합니다. Up 버튼은 사용자를 앱 외부로 안내하지 않습니다.
  • Back 버튼은 사용자가 최근에 작업한 화면(백 스택)을 뒤로 탐색합니다.

Up 버튼 추가.

navigation component에는 NavigationUI라는 UI 라이브러리가 포함되어 있습니다. Navigation 구성 요소에는 NavigationUI 클래스가 포함되어 있습니다. 이 클래스에는 app bar, navigation drawer 및 bottom navigation을 사용하여 navigation을 관리하는 정적 메서드가 포함되어 있습니다. navigation controller는 app bar와 통합되어 Up 버튼의 동작을 구현하므로 직접 수행할 필요가 없습니다.

  1. MainActivity.kt 파일을 엽니다. onCreate() 메서드 내에서 navigation controller 개체를 찾는 코드를 추가합니다.
val navController = this.findNavController(R.id.myNavHostFragment)
  1. 또한 onCreate() 메서드 내부에 navigation controller를 app bar에 연결하는 코드를 추가합니다.
NavigationUI.setupActionBarWithNavController(this,navController)
  1. onCreate() 메서드 다음에 onSupportNavigateUp() 메서드를 override하여 navigation controller에서 NavigateUp()을 호출합니다.
override fun onSupportNavigateUp(): Boolean {
        val navController = this.findNavController(R.id.myNavHostFragment)
        return navController.navigateUp()
    }

options menus 추가.

이 작업에서는 options menu 항목을 옵션 메뉴에 추가합니다. 사용자가 about menu 항목을 탭하면 앱이 AboutFragment로 이동하고 사용자에게 앱 사용 방법에 대한 정보가 표시됩니다.

  1. navigation graph에 AboutFragment 추가
  2. options menu 리소스 추가
    • Android Studio 프로젝트 창에서 res 폴더를 마우스 오른쪽 버튼으로 클릭하고 new > Android resource file을 선택합니다.
    • New Resource File 대화 상자에서 File name을 options_menu로 지정합니다.
    • Resource Type으로 메뉴를 선택하고 확인을 클릭합니다.
    • res > menu 폴더에서 options_menu.xml 파일을 열고 Design 탭을 클릭하면 Layout Editor를 볼 수 있습니다.
    • 팔레트 창에서 메뉴 항목(아래 스크린샷에서 1로 표시)을 끌어 디자인 편집기 창(2)의 아무 곳에나 놓습니다. 메뉴 항목은 미리 보기(3)와 구성 요소 트리(4)에 나타납니다.
    • 미리 보기 또는 component tree에서 메뉴 항목을 클릭하여 속성 창에 속성을 표시합니다.
    • 메뉴 항목의 ID를 aboutFragment로 설정합니다. 제목을 @string/about으로 설정합니다.
  3. onClickHandler 추가.
    • TitleFragment.kt 파일을 엽니다. onCreateView() 메서드 내에서 return 전에 setHasOptionsMenu() 메서드를 호출하고 true를 전달합니다.
    override fun onCreateView(inflater: LayoutInflater, 	container: ViewGroup?,
                         savedInstanceState: Bundle?): View? {
         ...
         setHasOptionsMenu(true)
         return binding.root
     }
    • onCreateView() 메서드 다음에 onCreateOptionsMenu() 메서드를 재정의합니다. 메서드에서 옵션 메뉴를 추가하고 메뉴 리소스 파일을 inflate합니다.
    override fun onCreateOptionsMenu(menu: Menu,
    			inflater: MenuInflater) {
    	super.onCreateOptionsMenu(menu, inflater)
    	inflater.inflate(R.menu.options_menu, menu)
    }
    • 메뉴 항목을 탭할 때 적절한 조치를 취하도록 onOptionsItemSelected() 메서드를 override합니다. 이 경우 action은 선택한 메뉴 항목과 동일한 id를 가진 Fragment로 이동하는 것입니다.
    	override fun onOptionsItemSelected(item: MenuItem): 	Boolean {
    	return NavigationUI.
        	onNavDestinationSelected(item,requireView().findNavController())
    	|| super.onOptionsItemSelected(item)
    }

navigation drawer 추가

navigation drawer는 화면 가장자리에서 바깥쪽으로 미끄러지는 패널입니다. drawer는 일반적으로 헤더와 메뉴를 포함합니다.
휴대폰 크기의 기기에서는 사용하지 않을 때 navigation drawer를 숨깁니다. 두 가지 유형의 사용자 작업으로 navigation drawer를 표시할 수 있습니다.

  • navigation drawer는 사용자가 화면의 모서리 끝에서 반대로 스와이프하면 나타납니다. AndroidTrivia 앱에서 탐색 창은 사용자가 왼쪽에서 오른쪽으로 스와이프하면 나타납니다.
  • navigation drawer는 사용자가 앱의 시작 위치에 있을 때 나타나고 앱 바에서 서랍 아이콘을 탭하면 나타납니다.
  1. 프로젝트에 material 라이브러리 추가

    • 앱 수준 Gradle 빌드 파일에서 Material 라이브러리에 대한 종속성을 추가합니다.
    dependencies {
    	...
    	implementation
    	"com.google.android.material:material:$version"
    	...
    }
    • Sync 버튼을 클릭합니다.
  2. destination fragment에 ID가 있는지 확인하십시오.

    • fragment_rules.xml 레이아웃 파일을 열어 어떻게 생겼는지 확인하세요. 디자인 탭을 클릭하여 디자인 편집기에서 미리보기를 봅니다.
    • navigation.xml 파일을 여십시오. new destination 버튼을 클릭하고 rules fragment을 추가합니다. ID를 rulesFragment로 설정합니다.
  3. drawer menu 및 drawer layout 만들기

    • drawer에 대한 메뉴를 만듭니다. 프로젝트 창에서 res 폴더를 마우스 오른쪽 버튼으로 클릭하고 new resource file을 선택합니다. file name을 navdrawer_menu로 지정하고 resource type을 menu로 설정한 다음 확인을 클릭합니다.
    • res > menu 폴더에서 navdrawer_menu.xml을 열고 디자인 탭을 클릭합니다. 팔레트 분할창에서 component tree 분할창으로 두 개의 메뉴 항목을 끌어서 추가하십시오.
    • 첫 번째 메뉴 항목의 경우 id를 rulesFragment로 설정합니다. (메뉴 항목의 ID는 Fragment의 ID와 같아야 합니다.) 제목을 @string/rules로, 아이콘을 @drawable/rules로 설정합니다.
    • 두 번째 메뉴 항목의 경우 id를 aboutFragment로, 제목 문자열을 @string/about으로, 아이콘을 @drawable/about_android_trivia로 설정합니다.
    • activity_main.xml 레이아웃 파일을 엽니다. 모든 서랍 기능을 얻으려면 DrawerLayout 안에 뷰를 넣으십시오. 전체 을 안에 래핑합니다. (즉, DrawerLayout을 루트 뷰로 추가합니다.)
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto">
      <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
     <LinearLayout
    	   . . . 
      	 </LinearLayout>
     </androidx.drawerlayout.widget.DrawerLayout>
    </layout>
    • 이제 방금 정의한 navdrawer_menu를 사용하는 NavigationView인 drawer을 추가합니다. DrawerLayout에서 요소 뒤에 다음 코드를 추가합니다.
    <com.google.android.material.navigation.NavigationView
       android:id="@+id/navView"
       android:layout_width="wrap_content"
       android:layout_height="match_parent"
       android:layout_gravity="start"
       app:headerLayout="@layout/nav_header"
       app:menu="@menu/navdrawer_menu" />
  4. navigation drawer 표시

    • MainActivity.kt 파일을 엽니다. onCreate()에서 사용자가 navigation drawer을 표시할 수 있도록 하는 코드를 추가합니다. setupWithNavController()를 호출하여 이 작업을 수행합니다. onCreate() 맨 아래에 다음 코드를 추가합니다.
    NavigationUI.setupWithNavController(binding.navView, navController)
  5. drawer button에서 navigation drawer 표시

    • MainActivity.kt 파일에서 drawer layout을 나타내는 lateinit drawer layout 멤버 변수를 추가합니다.
    private lateinit var drawerLayout: DrawerLayout
    • onCreate() 메서드 내에서 바인딩 변수가 초기화된 후 drawer layout을 초기화합니다.
    val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,
                R.layout.activity_main)
    
    drawerLayout = binding.drawerLayout
    • setupActionBarWithNavController() 메서드에 세 번째 매개변수로 drawer layout을 추가합니다.
    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
    • navController.navigateUp을 반환하는 대신 NavigationUI.navigateUp을 반환하도록 onSupportNavigateUp() 메서드를 편집합니다. navigation controller와 drawer layout을 navigationUp()에 전달합니다.
    override fun onSupportNavigateUp(): Boolean {
     val navController = this.findNavController(R.id.myNavHostFragment)
       return NavigationUI.navigateUp(navController, drawerLayout)
    }
    

좋은 웹페이지 즐겨찾기