본문 바로가기
⛏️ | 개발 기록/🪐 | Cosmic Detox

[Android] Android에서 BottomSheetDialogFragment의 높이를 최대 높이로 설정하기

by immgga 2024. 8. 27.

출처: unsplash.com

 

내일배움캠프 최종 프로젝트 기록 2

 

 

서론

이번에는 UI 개발을 할 차례이다.

나는 UI개발에서 마이페이지에 사용하는 bottomSheet들의 UI 구성을 맡았다.

그리고 이미 구현되어 있는 bottomSheet 틀을 사용해서 구현할 것이다.

sheet 틀은 xml로 이미 구현되어 있고, content 부분만 따로 xml로 구현해서 붙여주는 작업을 진행했다.

bottomSheet UI의 height 설정 부분에서 삽질을 했던 과정을 글로 적어 보겠다.

 

 

BottomSheet를 최대 높이로 설정해서 만들기

기존에는 BottomSheet를 최대 높이로 설정해서 띄우려면 BottomSheetBehavior를 사용해야 했다.

원래 BottomSheetBehavior를 사용해도 되지만, bottomSheetDialogFragment는 dialog 이외의 화면이 모두 어둡게 바뀌기 때문에 이를 이용하기 위해 BottomSheetDialogFragment를 구현하고자 했다.

 

해결 과정

우선 기존에 bottomSheet 영역을 따로 xml에 정의해야 했지만 따로 정의할 필요는 없다.

그러면 바로 bottomSheet xml로 가서 content 영역을 ViewStub으로 정의해 주겠다.

<ViewStub
    android:id="@+id/bottom_sheet_body"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/view_bottom_sheet_divider" />

ViewStub은 include와 비슷하게 다른 xml을 포함시켜 줄 수 있다. ViewStub에 대해서는 검색으로 공부해 보자.

 

BottomSheetDialogFragment 파일에서 xml 뷰들이 뭘 보여주게 할 건지 기능을 정의한다.

private val modalBottomSheetBinding by lazy { ModalBottomsheetBinding.inflate(layoutInflater) }
private lateinit var modalContentSetLimitAppBinding: ModalContentSetLimitAppBinding

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    modalBottomSheetBinding.bottomSheetBody.layoutResource = R.layout.modal_content_set_limit_app
    val viewStub = modalBottomSheetBinding.bottomSheetBody.inflate()
    modalContentSetLimitAppBinding = ModalContentSetLimitAppBinding.bind(viewStub)

    modalBottomSheetBinding.tvBottomSheetTitle.text = "앱 사용시간 제한"
    modalBottomSheetBinding.tvBottomSheetComplete.setOnClickListener {
        dismiss()
    }

    return modalBottomSheetBinding.root
}

binding을 2개를 사용했는데, 하나는 bottmSheetBinding이고, 나머지 하나는 bottomSheetContnetBinding이다.

onCreateView에서는 binding을 정의와 view에 들어갈 데이터를 넣어준다(아직 미완성).

 

BottomSheetDialogFragment에서는 onCreateDialog라는 함수를 override 받을 수 있다.

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog =  super.onCreateDialog(savedInstanceState)

    dialog.setOnShowListener {
        val bottomSheetDialog = it as BottomSheetDialog
        setUpRatio(bottomSheetDialog)
    }
    return dialog
}

이 함수 안에 dialog가 show 되면 setUpRatio를 정의한다.

setOnShowListener를 사용해서 bottomSheetDialog를 정의하고 높이를 설정하는 함수인 setUpRatio를 실행한다.

setUpRatio가 내가 구현한 높이를 최대로 설정하는 함수이다.

 

private fun setUpRatio(bottomSheetDialog: BottomSheetDialog) {
    val bottomSheet = bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as View
    val behavior = BottomSheetBehavior.from(bottomSheet)
    val layoutParams = bottomSheet.layoutParams
    layoutParams.height = getWindowHeight()

    bottomSheet.layoutParams = layoutParams
    behavior.apply {
        state = BottomSheetBehavior.STATE_EXPANDED
        isDraggable = false
    }
}

private fun getWindowHeight(): Int {
    val wm = context?.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        val windowMetrics = wm.currentWindowMetrics
        val insets = windowMetrics.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
        return windowMetrics.bounds.height() - insets.bottom - insets.top
    } else {
        val displayMetrics = DisplayMetrics()
        wm.defaultDisplay.getMetrics(displayMetrics)
        return displayMetrics.heightPixels
    }
}

getWindowHeight에서 현재 기기의 화면 높이를 구한 다음에 setUpRatio에서 높이를 적용한다.

bottomSheet를 정의해 behavior를 만들어서 STATE_EXPANDED의 sheet를 열어 준다.

isDraggable을 false로 설정해서 드래그로 종료를 못하도록 막아 준다.

 

height를 변경하고 싶으면 getWindowHeight에 비율을 설정해 준다.

layoutParams.height = getWindowHeight() * 85 / 100

아래와 같이 설정해 주면 전체 높이를 100%라고 가정했을 때, 85%의 높이를 가진 bottomSheet를 생성할 수 있다.

 

BottomSheet를 show 할 때는 아래와 같이 구현해 주면 된다.

binding.testBtn2.setOnClickListener {
    val bottomSheet = MyPageSetLimitAppBottomSheet()
    bottomSheet.show(parentFragmentManager, bottomSheet.tag)
}

bottomSheet를 생성해 주고, show를 해주는데, fragmentManager와 자신의 tag를 파라미터로 보내면 된다.

 

 

구현 중 발생한 문제 상황

검색해도 내 마음에 차지 않는 해결 과정이 나오지 않아서 youtube 강의를 찾아보았다.

youtube 강의들은 대부분이 내가 원하는 구현 결과가 아니었고, 내가 원하는 구현 결과가 있다고 해도, 설명이 부족한 것들이 많았다.

그리고 이제는 ui 쪽의 문제라고 생각해서 xml의 코드를 변경해 보았다. Constraint 기반의 UI를 LinearLayout 기반으로 변경해보기도 하고 이후에 설명하겠지만 ViewStub의 문제라고 생각해 include를 이용해 봤지만 소용이 없었다.

BottomSheetBehavior를 써야 하는 건 알게 되었다. BottomSheetBehavior는 CoordinatorLayout을 이용해야 되는 줄 알고 CoordinatorLayout의 속성에 대해서도 뭘 달아야 하는지 검색해서 찾아보는 과정도 있었다.

 

 

정리

어제부터 구현하기 시작해서 어제는 삽질만 하고 오늘 다시 찾아서 구현해 보니까 어렵지 않게 해결되었다.

어제 멘탈이 살짝 갈려있어서 그런가 시야가 좁아졌었나 보다.

어제 새벽까지 했으면 좀 짜증 났을 것 같다. 컨디션 관리를 이래서 잘해야 하나?

이걸 왜 어제는 못찾았지? 라는 생각이 든다.

728x90