일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 기기고유값
- 생명주기
- Load failed
- kotlin
- SSAID
- Room
- Collections Function
- NumberPIcker
- Navigation
- studywithme
- todo
- Popup menu background color
- Retrofit2
- DialogFragment
- 화면 회전
- gradle plugin
- findNavController
- layout_constrainedWidth
- BottomSheetDialogFragment
- multipart
- json
- DataBinding
- ThreeTen Backport
- WorkManager
- log
- layout_constrainedHeight
- http
- Android
- RecyclerView
- Lifecycle
- Today
- Total
chacha's
📁 Dialog Fragment 이것 저것 본문
목차
모서리 둥글게 하기 ( Round Corner )
아래와 같이 커스텀 다이얼로그를 만들 때, 모서리를 둥글게 하고 싶은 경우가 있습니다.
1. [ @drawable/bg_dialog ] 배경으로 사용될 drawable 파일을 생성합니다.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/dialog_background" />
<corners android:radius="10dp" />
</shape>
2. [ layout/dialog_custom.xml ] layout에서 Background로 지정합니다.
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_dialog"
...>
...
<!-- 추가 요소 -->
...
</androidx.constraintlayout.widget.ConstraintLayout>
3. Custom Dialog 코드 작성
위의 2가지 코드만 적용하면 해결될 것이라고 생각하지만, 코드에서 다이얼로그의 백그라운드를 제거해줘야 모서리가 둥글게 나옵니다.
class CustomDialog : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.dialog_custom, container, false)
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
...
return view
}
}
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
은 삽입하지 않아도 됩니다. 하지만 Android 4.4 버전 이하에서는 파란 선이 다이얼로그 상단에 나타난다고 하니 minSdk를 확인하고 작성 여부를 결정하면 될 것 같습니다.
사이즈(width, height) 조절하기
DialogFragment를 띄울 때 내가 설정한 값과 상관없이 Dialog 내용물에 따라 자동으로 뷰가 랜더링됩니다. 분명 android:layout_width="match_parent"
로 선언했기 때문에 꽉 차게 랜더링될 것 같지만, 결과물을 보는 순간 내 바램이었다는 것을 깨닫게 되었습니다.
위의 문제를 해결하기 위해서는 2가지 방법이 있습니다.
1. 코드에서 setLayout(width, height)
을 지정하는 방법
이상하게 xml에서 Dp로 지정한 경우에도 match_parent로 설정했던 경우와 똑같이 내용물에 맞게 뷰가 랜더링되었습니다. 이를 해결하기 위해서는 미리 지정해둔 dp 크기를 불러와서 코드상에서 dialog의 width/height를 지정해야 했습니다. 이 방법은 구글링 시 stack Overflow에 가장 빈번하게 나오는 해결 방법입니다.
- layout/dialog_custom.xml
android:layout_width="match_parent"
android:layout_height="match_parent"
- CustomDialog.kt
override fun onResume() {
...
val width = resources.getDimensionPixelSize(R.dimen.popup_width)
val height = resources.getDimensionPixelSize(R.dimen.popup_height)
getDialog().getWindow().setLayout(width, height)
...
}
하지만 위의 방법은 디바이스 크기에 따라서 동일한 뷰를 보장받을 수 없다는 문제가 있습니다. 또한 height는 wrap_content로 구현하고 싶은 경우 해당 방법은 적절하지 않습니다.
2. 디바이스의 화면 크기를 구해서 setAttributes
를 지정하는 방법
이 방법은 가로 크기에 비례하여 지정한 비율로 레이아웃을 구상하는 경우입니다. 첫번째 방법과 달리, width의 비율만 지정하고 height는 내용물에 따라 자동으로 조절됩니다.
- layout/dialog_custom.xml
android:layout_width="match_parent"
android:layout_height="wrap_content"
- CustomDialog.kt
class CustomDialog: DialogFragment() {
...
override fun onResume() {
super.onResume()
// 디바이스 size 구하기
val params: ViewGroup.LayoutParams? = dialog?.window?.attributes
val windowManager = activity?.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val size = windowManager.currentWindowMetricsPointCompat() // 디바이스 가로,세로 길이
val deviceWidth = size.x // 디바이스 가로 길이
params?.width = (deviceWidth * 0.9).toInt() // 여백 비율을 지정
dialog?.window?.attributes = params as WindowManager.LayoutParams
}
}
DialogFragment의 onResume()
에서 실행해야 합니다.
fun WindowManager.currentWindowMetricsPointCompat(): Point {
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
val windowInsets = currentWindowMetrics.windowInsets
var insets: Insets = windowInsets.getInsets(WindowInsets.Type.navigationBars())
windowInsets.displayCutout?.run {
insets = Insets.max(
insets,
Insets.of(safeInsetLeft, safeInsetTop, safeInsetRight, safeInsetBottom)
)
}
val insetsWidth = insets.right + insets.left
val insetsHeight = insets.top + insets.bottom
Point(
currentWindowMetrics.bounds.width() - insetsWidth,
currentWindowMetrics.bounds.height() - insetsHeight
)
} else {
Point().apply {
defaultDisplay.getSize(this)
}
}
}
🧶 DialogFragment에 ViewBinding 사용하기
How to correctly use Android View Binding in DialogFragment? - stack overflow
View를 직접 Inflate 하는 대신에 binding.root
를 사용하여 뷰를 설정해주면 됩니다. DialgoFragment에서 뷰바인딩(View Binding)을 사용하는 것도 Fragment에서 사용하는 것과 같이 onDestroyView()
에서 해제해주어야 합니다. ( 참고: Use view binding in fragments - Docs )
class CustomDialog : DialogFragment() {
private var _binding: DialogCustomBinding? = null
// This property is only valid between onCreateDialog and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = DialogCustomBinding.inflate(LayoutInflater.from(context))
return AlertDialog.Builder(requireActivity())
.setView(binding.root)
.create()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
🥾 DialogFragment로부터 Click Event 전달받기
Passing Events Back to the Dialog's Host - Doc
How to get data from DialogFragment to a Fragment? - stack overflow
Dialog의 어떤 버튼이 클릭되었는지 Dialog를 호출한 곳에서 알 수 있는 방법입니다.
1. DialogFragment 선언하기
class StopDialog : DialogFragment() {
// Dialog의 Host에게 Event를 전달하기 위한 리스너
private lateinit var listener: StopDialogListener
/* Activity에서 아래의 interface를 구현해주어야 합니다.
* 각 메서드는 Dialog를 파라미터로 전달합니다.
*/
interface StopDialogListener {
fun onDialogPositiveClick(dialog: DialogFragment)
fun onDialogNegativeClick(dialog: DialogFragment)
}
private lateinit var positiveButton: TextView
private lateinit var negativeButton: TextView
override fun onAttach(context: Context) {
super.onAttach(context)
try {
// 리스너를 인스턴스화함으로서 host에 event를 보냄
listener = context as StopDialogListener
} catch (e: ClassCastException) {
// Activity에 interface가 구현되지 않았으면 Excetion thorw
throw ClassCastException(
(context.toString() +
" must implement NoticeDialogListener")
)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
positiveButton.setOnClickListener {
listener.onDialogPositiveClick(this)
}
negativeButton.setOnClickListener {
listener.onDialogNegativeClick(this)
}
...
}
}
2. Activity에서 Listener 선언하기
class RecordActivity : AppCompatActivity(), StopDialog.StopDialogListener {
/**
* Record Stop And Save Dialog
**/
private fun showSaveDialog() {
val dialog = StopDialog()
dialog.show(supportFragmentManager, "StopDialog")
}
override fun onDialogPositiveClick(dialog: DialogFragment) {
Toast.makeText(dialog.context, "Positive clicked!", Toast.LENGTH_SHORT).show()
dismiss()
}
override fun onDialogNegativeClick(dialog: DialogFragment) {
Toast.makeText(dialog.context, "Negative clicked!", Toast.LENGTH_SHORT).show()
dismiss()
}
}
Fragment에서 DialogFragment의 Click Event 전달 받기
Custom dialog interface for multiple Activities/ Fragments - stack overflow
DialogFragment Listener - stack overflow
위의 Acitivity에서 전달 받는 것과 유사하지만, onAttach
부분을 수정하지 않으면 Activity에 리스너를 선언하지 않았다는 오류를 만나게 됩니다. onAttach
부분을 아래와 같이 변경하면 Fragment에 리스너가 전달되는 것을 확인할 수 있습니다.
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = parentFragment as StopDialogListener
} catch (e: ClassCastException) {
...
}
}
이외에도 동일 DialogFragment를 Activity와 동시에 사용할 수도 있습니다. parentFragment
는 Acitivity에서 호출한 경우, null을 반환합니다. 이를 이용하여 아래와 같이 선언 시, Activity에서 호출하는 경우와 Fragment에서 호출하는 경우로 나눠서 리스너를 처리할 수 있습니다.
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = parentFragment?.let {
parentFragment as NoticeDialogListener
} ?: context as NoticeDialogListener
} catch (e: ClassCastException) {
...
}
}
End
'Android > TIL' 카테고리의 다른 글
🚧 layout_constrainedHeight/Width로 가변 길이 설정하기 (0) | 2021.12.24 |
---|---|
🆔 Android 기기 고유 값 사용하기 (2) | 2021.11.08 |
Gson을 이용하여 Assets에 있는 JSON 파일 읽기 (0) | 2021.08.09 |
🕶 Glide 이용할 때 Http URL 사용 시 화면 안 보임 (0) | 2021.08.07 |
🔀 Dialog에서 findNavController 접근 못 하는 문제 (0) | 2021.06.16 |