[Android] Jetpack의 View 활용하기

leeeha·2022년 12월 20일
0
post-thumbnail

Jetpack

  • Jetpack이란 구글에서 안드로이드 개발을 위해 지원하는 다양한 라이브러리들의 집합
  • 대부분 androidx로 시작하는 패키지명을 사용하는 라이브러리
  • 앱 개발의 권장 아키텍처 (AAC) 제공
  • API Level에 의한 호환성 문제 해결
  • 표준에서 제공하지 못하는 다양한 기능 제공
  • androidx.appcompat: 앱의 API Level 호환성을 위한 라이브러리
  • androidx.recyclerview: 목록 화면을 구성하기 위한 라이브러리
  • androidx.viewpager2: 스와이프에 의한 화면 구성을 위한 라이브러리
  • androidx.fragment: 액티비티처럼 동작하는 뷰를 제공하는 라이브러리
  • androidx.drawerlayout: 옆에서 밀리면서 보여지는 화면을 구성하기 위한 라이브러리

Fragment

  • 액티비티처럼 동작하는 "뷰"
  • 자체적으로는 화면에 아무것도 출력되지 않는다.
  • 액티비티에 작성할 수 있는 모든 코드를 프래그먼트에서 작성 가능

  • 한 화면에 들어가는 내용이 많아지면서 프래그먼트로 화면을 분리해야 할 필요성이 생겼다.
  • 여러 화면 간의 전환 시에도 액티비티 간의 인텐트를 사용하는 게 아니라, 한 액티비티 내에서 여러 프래그먼트들을 전환하는 게 더 간편하다.

사용 방법

  • Fragment를 상속 받은 클래스를 작성한다.
class OneFragment: Fragment() {
	// ... 
}
  • 액티비티의 레이아웃 xml 파일에 등록하여 프래그먼트를 출력한다.
<fragment
  android:name="com.example.OneFragment"
  android:id="@+id/fragmentView"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />
  • 액티비티 코드 상에서 프래그먼트를 동적으로 출력하려면?
  • add(int containerViewId, Fragment fragment): 새로운 프래그먼트를 id 영역에 추가
  • replace(int containerViewId, Fragment fragment): id 영역에 추가된 프래그먼트를 새로운 프래그먼트로 대체
  • remove(Fragment fragment): 추가된 프래그먼트 제거
  • commit(): 프래그먼트를 화면에 적용
val fragmentManager: FragmentManager = supportFragmentManager
val transaction: FragmentTransaction = fragmentManager.beginTransaction()

transaction.add(R.id.fragment_content, oneFragment)
transaction.commit() 

실습 예제

OneFragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    tools:context=".OneFragment">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="20sp"
        android:text="I am OneFragment" />

</LinearLayout>
package com.tutorial.c91

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class OneFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}

MainActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical">
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/oneButton"
            android:text="one"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/twoButton"
            android:text="two"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
    </LinearLayout>

</LinearLayout>
package com.tutorial.c91

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c91.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val oneFragment = OneFragment()
        val twoFragment = TwoFragment()

        val manager = supportFragmentManager
        val transaction = manager.beginTransaction()
        transaction.add(R.id.fragment_container, oneFragment)
        transaction.commit() // 이때 화면에 프래그먼트가 출력되면서, 트랜잭션 객체는 닫히게 됨.

        binding.oneButton.setOnClickListener {
            val tran = manager.beginTransaction() // 따라서, 다시 얻어줘야 함.
            tran.replace(R.id.fragment_container, oneFragment)
            tran.commit()
        }

        binding.twoButton.setOnClickListener {
            val tran = manager.beginTransaction()
            tran.replace(R.id.fragment_container, twoFragment)
            tran.commit()
        }
    }
}


RecyclerView

  • RecyclerView는 목록 화면을 만들기 위해 사용하는 뷰
  • 플랫폼에서 제공하는 리스트뷰 보다 커스터마이징 하기 더 좋기 때문에 많이 사용된다.

리사이클러뷰의 구성요소

  • Adapter: 항목을 구성하는 역할 (필수)
  • ViewHolder: 항목에 필요한 뷰 객체를 갖고 있는 역할 (필수)
  • LayoutManager: 항목을 배치하는 역할 (필수)
  • ItemDecoration: 항목을 꾸미기 위한 역할 (옵션)

목록의 한 항목 구성하려면 여러 개의 뷰가 필요하다. 이러한 뷰 객체들을 갖고 있는 게 뷰 홀더의 역할이다. 그리고 어댑터에 의해 뷰에 데이터를 출력하며, 이러한 항목들은 레이아웃 매니저에 의해 배치되어 리사이클러뷰가 완성된다.

사용 방법

  • ViewHolder는 RecyclerView.ViewHolder를 상속 받아 작성한다.
  • item_main.xml 파일에 있는 뷰 객체들을 뷰 홀더가 갖고 있다. (뷰 바인딩 사용)
class MyViewHolder(val binding: ItemMainBinding): RecyclerView.ViewHolder(binding.root)
  • Adapter는 RecyclerView.Adapter를 상속 받아 작성한다.
class MyAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>(){
	override fun getItemCount(): Int {
		TODO("Not yet implemented")
	}

	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
		TODO("Not yet implemented")
	}
	override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
		TODO("Not yet implemented")
	}
}
  • getItemCount(): 항목의 개수를 판단하기 위해 자동 호출
  • onCreateViewHolder(): 항목의 뷰 객체들을 갖고 있는 뷰 홀더를 준비하기 위해 자동 호출
  • onBindViewHolder(): 뷰 홀더가 갖고 있는 뷰 객체에 데이터를 출력하기 위해 자동 호출
  • RecyclerView에 어댑터와 레이아웃 매니저를 등록시켜 화면에 출력한다.
binding.recycler.layoutManager = LinearLayoutManager(this)
binding.recycler.adapter = MyAdapter(datas) 
  • RecyclerView의 항목을 배치하는 레이아웃 매니저
    • LinearLayoutManager: 가로 혹은 세로 방향으로 항목 배치
    • GridLayoutManager: 그리드 모양으로 항목 배치
    • StaggeredGridLayoutManager: 높이가 불규칙한 그리드 모양으로 항목 배치

실습 예제

item_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/item_img"
        android:src="@drawable/ic_launcher_background"
        android:layout_width="50dp"
        android:layout_height="50dp" />

    <TextView
        android:id="@+id/item_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginStart="16dp"/>

</LinearLayout>

MyAdapter.kt

package com.tutorial.c92

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.tutorial.c92.databinding.ItemMainBinding

class MyViewHolder(val binding: ItemMainBinding): RecyclerView.ViewHolder(binding.root)

class MyAdapter(val datas: List<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun getItemCount(): Int {
        return datas.size
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return MyViewHolder(ItemMainBinding.inflate(LayoutInflater.from(parent.context)))
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val binding = (holder as MyViewHolder).binding
        binding.itemTitle.text = datas[position]
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</androidx.recyclerview.widget.RecyclerView>

MainActivity.kt

package com.tutorial.c92

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.tutorial.c92.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val datas = mutableListOf<String>()
        for(i in 1..10){
            datas.add("item $i")
        }

        binding.recyclerView.layoutManager = LinearLayoutManager(this)
        binding.recyclerView.adapter = MyAdapter(datas)
        binding.recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
    }
}

참고: 카드를 사용하여 이미지 목록 표시하는 방법 (코드랩)


ViewPager2

  • 스와이프 이벤트 (손으로 화면을 미는 이벤트)에 의한 화면 전환을 목적으로 한다.
  • 뷰 페이저도 리사이클러뷰처럼 항목을 여러 개 나열하는 건 맞는데, 그 항목을 화면 한 장으로 뿌려주는 어댑터뷰라고 볼 수 있다.

  • androidx.viewpager 라이브러리와 별개로 2019년 androidx.viewpager2가 새롭게 발표되었다.
  • androidx.viewpager2 사용을 권장하고 있다.

implementation 'androidx.viewpager2:viewpager2:1.0.0'

<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/viewpager"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

Adapter

  • ViewPager2에서 사용할 수 있는 Adapter로는 두가지가 제공된다.
  • RecyclerView.Adapter
  • FragmentStateAdapter
  • RecyclerView.Adapter는 앞서 살펴보았던 내용과 동일하다.
  • 그런데 뷰 페이저는 화면 전체가 하나의 항목이므로, 화면 구성이 복잡해질수록 이 방법으로 뷰 페이저를 구현하는 데는 한계가 있다.
class MyPagerViewHolder(val binding: ItemPagerBinding): RecyclerView.ViewHolder(binding.root)

class MyPagerAdapter(val datas: MutableList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
  
  override fun getItemCount(): Int{
  	return datas.size
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
  = MyPagerViewHolder(ItemPagerBinding.inflate(LayoutInflater.from(parent.context), parent, false))

  override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
  		// ... 
  }
}
  • 항목이 프래그먼트로 작성되었다면 FragmentStateAdapter를 이용한다.
  • FragmentStateAdapter를 상속 받아 어댑터를 작성한다. (add, replace, commit 등으로 프래그먼트를 전환하는 것은 상위 클래스에서 알아서 처리해준다.)
  • getItemCount() 함수와 createFragment() 함수를 오버라이드 해야 한다.
  • createFragment() 함수에서 리턴한 프래그먼트가 한 항목으로 출력된다.
class MyFragmentPagerAdapter(activity: FragmentActivity): FragmentStateAdapter(activity){
  val fragments: List<Fragment>
  
  init {
  	fragments = listOf(OneFragment(), TwoFragment(), ThreeFragment())
  	Log.d("kkang" ,"fragments size : ${fragments.size}")
  }
  
  override fun getItemCount(): Int = fragments.size
  
  override fun createFragment(position: Int): Fragment = fragments[position]
}
  • 스와이프 하는 화면 방향도 조정 가능하다. (기본은 가로 방향)
  • 세로 방향으로 나오게 하려면 orientation 속성값을 조정하면 된다.
binding.viewpager.orientation = ViewPager2.ORIENTATION_VERTICAL

실습 예제

라이브러리 dependency 설정

implementation 'androidx.viewpager2:viewpager2:1.0.0'

MyAdapter.kt

package com.tutorial.c93

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

class MyAdapter(activity: FragmentActivity): FragmentStateAdapter(activity) {
    val fragments: List<Fragment> = listOf(OneFragment(), TwoFragment(), ThreeFragment())

    override fun getItemCount(): Int {
        return fragments.size
    }

    override fun createFragment(position: Int): Fragment {
        return fragments[position]
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

MainActivity.kt

package com.tutorial.c93

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c93.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.viewpager.adapter = MyAdapter(this)
    }
}


Toolbar

  • 툴바는 액션바를 개발자 뷰로 이용하기 위한 뷰
  • 툴바와 액션바는 목적이 동일하다.
  • 그러나, 액션바는 액티비티 윈도우가 자동으로 출력시켜주는 액티비티의 구성요소이지만, 툴바는 개발자가 직접 핸들링 하는 뷰이다.
  • 액션바에서 제약이 있었던 것을 툴바에서는 직접 구현하는 것이 가능하다. (ex. 세로 스크롤 시, 툴바가 같이 스크롤 되도록, 툴바 영역을 더 다양하게 꾸밀 수 있도록)

위의 그림을 보면 액션바는 컨텐츠 영역과 구분되어 있지만, 툴바는 컨텐츠 영역에 포함된다. 따라서, 툴바는 우리가 직접 구현하는 레이아웃 xml 파일에 포함되는 뷰이다.

사용 방법

  • 액티비티 테마 설정에서 기본 액션바가 화면에 출력되지 않도록 설정
  • 레이아웃 xml 파일에 툴바를 등록
<androidx.appcompat.widget.Toolbar
  android:id="@+id/toolbar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  style="@style/Widget.MaterialComponents.Toolbar.Primary"/>
  • 아래 코드 한 줄만 작성해주면, 액티비티의 액션바에 적용되었던 내용이 그대로 툴바에 적용된다.
setSupportActionBar(binding.toolbar) 

실습 예제

values/themes.xml (액션바 없애기)

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.AndroidLab" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu1"
        android:title="menu1"/>
    <item
        android:id="@+id/menu2"
        android:title="menu2"
        android:icon="@android:drawable/ic_menu_add"
        app:showAsAction="always"/>
</menu>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.Toolbar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@style/Widget.MaterialComponents.Toolbar.Primary"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"/>

</LinearLayout>

MainActivity.kt

package com.tutorial.c94

import android.os.Bundle
import android.view.Menu
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c94.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.toolbar)
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return super.onCreateOptionsMenu(menu)
    }
}


FloatingActionButton

  • ExtendedFloatingActionButton은 화면에 떠있는 듯한 느낌의 버튼을 제공하는 뷰 (컨텐츠 영역의 데이터들이 스크롤 된다고 해도 버튼은 우측 하단에 고정될 수 있도록)
  • 원래는 FloatingActionButton 뷰가 제공되었고 많이 사용되었다.
  • ExtendedFloatingActionButton은 FloatingActionButton과 동일한 목적으로 사용되지만, 버튼에 문자열까지 출력할 수 있다.

ExtendedFloatingActionButton

  • icon 속성으로 이미지를 지정하면, 이 이미지를 둥그렇게 감싸서 화면에 떠있는 듯한 느낌으로 출력할 수 있다.
  • text 정보를 지정하지 않고 icon 정보만 지정하면 둥근 모양
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
  android:text="extended FAB"
  app:icon="@android:drawable/ic_input_add"/>
  • 코드 상에서 버튼을 확장시켜 문자열까지 나오게 하거나, 축소시켜 아이콘만 나오게 하는 등의 처리가 가능하다.
binding.fab.setOnClickListener {
  when(binding.fab.isExtended){
    true -> binding.fab.shrink()
    false -> binding.fab.extend()
  }
}

실습 예제

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="24dp"
        android:text="extended FAB"
        app:icon="@android:drawable/ic_input_add"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
package com.tutorial.c95

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c95.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.fab.setOnClickListener {
            when(binding.fab.isExtended){
                true -> binding.fab.shrink()
                false -> binding.fab.extend()
            }
        }
    }
}


DrawerLayout

  • 액티비티 화면에 안 보이던 내용이 왼쪽이나 오른쪽에서 유저의 스와이프 이벤트에 의해 끌리면서 나타나는 기능을 제공

  • 액티비티 레이아웃 xml 파일에서 Drawer가 출력되어야 하는 부분의 태그를 DrawerLayout으로 선언
  • DrawerLayout 하위에는 두 개의 뷰를 선언
    • 첫번째 하위 태그 부분을 액티비티 화면에 출력
    • 두번째 하위 태그 부분이 안보이다가 끌려나오며 출력
  • android:layout_gravity 속성값을 이용하여 화면에서 나오는 방향 지정 (left, right, start)
  • start 값을 주면 국가별 언어의 쓰는 방향에 따라 left, right가 자동으로 결정된다.
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent">
  
  <LinearLayout .................. >
  	<!-- ............. -->
  </LinearLayout>
  
  <TextView
  	android:layout_gravity="start"/>
  
</androidx.drawerlayout.widget.DrawerLayout>
  • 보통 DrawerLayout을 사용할 때는 툴바 영역에 토글 버튼을 같이 제공하는 게 일반적이다.

  • 토글 버튼은 ActionBarDrawerToggle 클래스에서 제공한다.
toggle = ActionBarDrawerToggle(this, binding.drawer, R.string.drawer_opened, R.string.drawer_closed)

supportActionBar?.setDisplayHomeAsUpEnabled(true)
toggle.syncState() // 토글 버튼 클릭과 스와이프 동작에 대한 싱크 맞추기 
override fun onOptionsItemSelected(item: MenuItem): Boolean {
  if(toggle.onOptionsItemSelected(item)){ // 토글 버튼을 클릭하는 경우 메뉴 이벤트로 처리 
  	return true 
  }
  
  return super.onOptionsItemSelected(item)
}
  • DrawerLayout에 의해 끌리면서 출력되는 부분을 위한 뷰
  • 리스트뷰나 리사이클러뷰를 사용하지 않고, menu만 이용해도 항목을 나열하는 화면을 구성할 수 있게 해준다.

<com.google.android.material.navigation.NavigationView
  android:id="@+id/main_drawer_view"
  android:layout_width="wrap_content"
  android:layout_height="match_parent"
  android:layout_gravity="start"
  app:headerLayout="@layout/navigation_header"
  app:menu="@menu/menu_navigation"/>

실습 예제

res/layout/nav_header.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:text="I am header"
    android:textStyle="bold"
    android:textSize="16sp"
    android:textColor="#ffffff"
    android:background="#ff0000"
    android:gravity="left|bottom"
    android:paddingBottom="32dp"
    android:paddingLeft="32dp"
    android:layout_height="200dp">
</TextView>

res/menu/menu_nav.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menu1"
        android:title="menu1"
        android:icon="@android:drawable/ic_input_add"/>
    <item
        android:id="@+id/menu2"
        android:title="menu2"
        android:icon="@android:drawable/ic_menu_search"/>
</menu>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:textSize="20sp"
            android:textStyle="bold"/>
    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/menu_nav">

    </com.google.android.material.navigation.NavigationView>

</androidx.drawerlayout.widget.DrawerLayout>

res/values/strings.xml

<resources>
    <string name="app_name">c96</string>
    <string name="open">open drawer</string>
    <string name="close">close drawer</string>
</resources>

MainActivity.kt

package com.tutorial.c96

import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c96.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    lateinit var binding: ActivityMainBinding
    lateinit var toggle: ActionBarDrawerToggle

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        toggle = ActionBarDrawerToggle(this, binding.drawer, R.string.open, R.string.close)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        toggle.syncState() // 토글 버튼 클릭과 스와이프 동작에 대한 싱크 맞추기
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if(toggle.onOptionsItemSelected(item)){
            return true
        }
        return super.onOptionsItemSelected(item)
    }
}

토글 버튼 클릭이나 스와이프 동작을 통해 네비게이션 뷰를 꺼내거나 숨길 수 있다.

profile
습관이 될 때까지 📝

0개의 댓글