개발자 끄적끄적

메뉴(Menu), 다이얼로그(Dialog), 네비게이션(Navigation) UI 본문

안드로이드 프로그래밍

메뉴(Menu), 다이얼로그(Dialog), 네비게이션(Navigation) UI

햏치 2023. 5. 7. 20:55

<App Bar(ActionBar)>
- 앱의 상단에 표시
- 타이틀, 액션아이콘, 옵션메뉴를 표시
- AppBar 또는 Action Bar라고 불린다
- 주의 : 테마에서 NoActionBar를 쓰면 App Bar가 나타나지 않는다
- 참고 : Toolbar
  - Jetpack 라이브러리로 제공되며, 레이아웃 내에 한 위젯처럼 취급
    - 안드로이드 레이아웃 디자인에서 Toolbar(ID가 toolbar)를 추가
    - Theme에서 parent를 NoActionBar로 변경
    - Activity에서 setSupportActionBar(findViewById(R.i.toolbar))
  - App Bar보다 더 유연하게 커스터마이즈 할 수 있다



<액션아이콘, 메뉴>
- App Bar에 액션아이콘, 옵션메뉴를 하나의 메뉴로 관리
- App Bar에 타이틀 표시하고 남은 공간에 액션아이콘과 메뉴 아이템을 배치
  - 공간이 부족하여 배치가 어려운 것들은 오버플로우 메뉴에 표시

- 메뉴 작성 방법
  - 메뉴 리소스 정의 : menu/파일이름.xml
  - 액티비티의 onCreateOptionsMenu() 에서 메뉴 생성
  - 액티비티의 onOptionsItemSelected() 에서 메뉴 선택 처리



<액션아이콘, 메뉴 - 메뉴 리소스 정의>
- menu/파일이름.xml
- <menu>태그 사이에 
- <item/> 태그로 메뉴 항복

- 정의
  - android:id - 항목 실별자
  - android:icon - 항목 아이콘
  - android:title - 항목 텍스트
  - app:showAsAction
    - "always" : 항상 액션 아이콘
    - "ifRoom" : 공간이 가능하면 
    - "" 디폴트 : 항상 옵션 메뉴로



<액션아이콘, 메뉴 - 메뉴 생성>
- 액티비티의 onCreateOptionsMenu()



<액션아이콘, 메뉴 - 메뉴 선택 처리>
- 액티비티의 onOptionsItemSelected()



<팝업 메뉴>
- 안드로이드에서 메뉴 종류
  - 옵션 메뉴, 팝업 메뉴, 컨텍스트 메뉴
  - 컨텍스트 메뉴 : 롱클릭으로 해당 항목(뷰)와 관련된 메뉴 표시
 
- 팝업 메뉴
  - 뷰 위에 메뉴 표시
  - Popup 클래스

- 팝업 메뉴 생성과 선택 처리
private fun showPopup(v: View) { // v는 팝업 메뉴를 보여줄 뷰, 클릭한 뷰를 보통 사용
PopupMenu(requireContext(), v).apply { // requireContext()는 프래그먼트에서
inflate(R.menu.nav_menu) // 메뉴 리소스로 메뉴 생성
setOnMenuItemClickListener { // 메뉴 선택 처리를 위한 리스너
when (it.itemId) {
R.id.homeFragment -> {
Snackbar.make(v, "HomeFragment", Snackbar.LENGTH_SHORT).show()
true
      }
      else -> false
      }
    }
  }.show() // 팝업 메뉴 나타나게 함
}
- 주의 : Jetpack의 클래스 사용 권장 : androidx.appcompat.widget.PopupMenu



<다이얼로그(Dialog, 대화상자)>
- 사용자의 결정이나 입력을 받기 위해 화면에 표시되는 작은 윈도우
- 다이얼로그 생성 클래스
  - AlertDialog
  - DatePickerDialog, TimePickerDialog
- DialogFragment 내에서 다이얼로그 클래스로 만들어 사용해야한다



<다이얼로그 - 생성>
- DialogFragment에서 onCreateDialog 정의
  - AlertDialog.Builder 사용하여 AlertDialog에 객체 생성 리턴

- requireActivity() : 프래그먼트에서 액티비티 객체를 받는 메소드, 없으면 exception 발생
- requireContext() : 프래그먼트에서 컨텍스트 객체를 받는 메소드, 없으면 exception 발생
  보통 activity는 context를 상속 받기 때문에 실제로 같다고 보면 된다



<다이얼로그 - 보이기>
- 다이얼로그 프로그먼트 생성하고 show()호출
  - OkCancelDialogFragment().show(supportFragmentManager, "OkCancelDialog")



<다이얼로그 - DatePickerDialog>
- DatePickerDialog 생성, 선택한 날짜 받기 위한 리스터 필요
class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
  return DatePickerDialog(requireContext(), this,
    2021, 4, 5) // Jan: 0, Feb:1, Mar:2, Apr: 3, May:4 ...
}
  override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
    println("$year, $month, $dayOfMonth")
  }
}



<다이얼로그 - 커스텀 다이얼로그>
- DialogFragmnet의 onCreateView와 onViewCreated 재정의
class MyBottomSheetDialog : BottomSheetDialogFragment() {
  private val myViewModel: MyViewModel by activityViewModels()

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
  savedInstanceState: Bundle?
  ): View {
  isCancelable = false
  return inflater.inflate(R.layout.my_bottom_dialog, container,false)
  }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  view.findViewById<EditText>(R.id.editTextName)?.setText(myViewModel.nameLiveData.value)
  view.findViewById<Button>(R.id.buttonOk)?.setOnClickListener {
  myViewModel.nameLiveData.value = view.findViewById<EditText>(R.id.editTextName)?.text.toString()
  dismiss()
  }
}
- 일반 다이얼로그는 BottomSheetDialogFragment 대신 DialogFragment 사용하면 된다




<네비게이션 UI>
- NavigationUI 클래스
- App Bar, 네비게이션 Drawer, Bottom 네비게이션과 함께 Jetpack Navigation을 도와주는 기능을 갖고 있다
- Jetpack Navigation
  - Navigation 그래프
  - NavHostFragment
  - NavController



<네비게이션 UI : App Bar>
- App Bar에 Up버튼 표시
- App Bar에 프래그먼트 label
- AppBarConfiguration 생성
  - Top 목적지 지정, setOf(그래프에서 프래그먼터들의 ID)
  - Top 목적지에는 Up버튼 표시하지 않는다
  - 따로 정하지 않으면 Home이 Top목적지가 된다

- NavController와 AppBarConfiguration 연결
- onSupportNavigateUp() 재정의

- Up과 Back의 차이
  - Up : parent 목적지로 이동
  - Back : 이전 목적지로 이동
  - 보통은 이전(back)목적지가 parent와 같은 경우가 대부분


<네비게이션UI : App Bar>
- AppBarConfiguration 생성과 NavController 연결
class MainActivity : AppCompatActivity() {
  private lateinit var appBarConfiguration: AppBarConfiguration
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
  
  // setup actionbar with nav controller to show up button
  val navHostFragment =
      supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment
  val navController = navHostFragment.navController
  // CAUTION: findNavController(R.id.fragment) in on Create will fail.
  appBarConfiguration = AppBarConfiguration(navController.graph) // Home이 Top 목적지
  setupActionBarWithNavController(navController, appBarConfiguration) // 연결
  }
- AppBarConfiguration(setOf(R.id.homeFragment, R.id.page1Fragment))라고 하면 homeFragment와 page1Fragment가 Top목적지가 되며, 
  이 프래그먼트가 나타날 때는 Up버튼 표시가 안된다



<네비게이션UI : App Bar>
- onSupportNavigateUp() 재정의
class MainActivity : AppCompatActivity() {
  private lateinit var appBarConfiguration: AppBarConfiguration

  override fun onCreate(savedInstanceState: Bundle?) {
    // 생략 – 앞 슬라이드 참고
}

override fun onSupportNavigateUp(): Boolean {
  val navController = findNavController(R.id.fragment)

  return navController.navigateUp(appBarConfiguration)
    || super.onSupportNavigateUp()
}
- Jetpack Toolbar를 사용하면 onSupportNavigateUp 재정의 필요 없다




<네비게이션UI : App Bar + 옵션 메뉴>
- 옵션 메뉴의 메뉴 항복 ID를 네비게이션 그래프의 프래그먼트(목적지) ID와 일치시키면, MenuItem의
  onNavDestinationSelected() 호출 하여 해당 목적지로 전환

<?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/page1Fragment"
    android:icon="@drawable/ic_baseline_inbox_24"
    android:title="@string/page1" />
  // 일부 항목 생략 – 전체는 뒤에 나오는 nav_menu.xml 참고
</menu>

class MainActivity : AppCompatActivity() {

  // onCreate, onSupportNavigteUp() 생략
  
  override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.main_menu, menu)
    return true
}

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
      R.id.page1Fragment ->
        item.onNavDestinationSelected(findNavController(R.id.fragment))
      else -> return super.onOptionsItemSelected(item)
    }
return true
  }
}



<네비게이션UI : App Bar + Drawer
- 서랍(Drawer)형태로 메뉴가 나왔다 들어가는 UI
- 서랍에 나타낼 메뉴 항목 정의하는 메뉴 리소스 작성
- DrawerLayout 사용하고 그 안에 아래 2개 포함 시켜야한다
  - FragmentContainerView - NavHostFragment를 위한 프래그먼터 자리
  - Navigation View - 서랍 레이아웃

- onCreate() 코드 추가
  - AppBarConfiguration 생성
  - NavController와 AppBarConfiguration 연결
  - NavController와 NavigationView 연결



<네비게이션 UI: Bottom Navigation>
- 화면 아래 목적지에 해당하는 버튼을 나열, 클릭하면 목적지로 이동
- Bottom Navigation에 나타낼 메뉴 항목 정의하는 메뉴 리소스 작성
  - 앞의 Navigation Drawer에서 사용한 메뉴 리소스와 동일한 것 사용
  
- LinearLayout내에 2개 포함 시켜야 한다
  - FragmentContainerView : NavHostFragment를 위한 프래그먼터 자리
  - BottomNavigationView : Bottom Navigation 버튼들

- onCreate() 코드 추가
  - NavController와 BottomNavigationView 연결

- Bottom Nav도 App Bar와 같이 사용 가능하지만 여기에서는 사용하지 않는다

'안드로이드 프로그래밍' 카테고리의 다른 글

목록표시 UI  (0) 2023.05.14
Activity, Intent  (0) 2023.04.15
위젯, 리소스  (0) 2023.04.10