使用 Kotlin 中的自定义布局查看 DialogFragment 中的绑定

View Binding in DialogFragment with custom layout in Kotlin

我喜欢使用 Kotlin synthetic,因为它的简单性和代码优雅,但现在他们将其贬低并迫使您使用那些丑陋的视图绑定。

关于如何在 Activites 和 Fragments 中使用它有很多答案,但找不到任何自定义布局警报对话框的示例。

这是与 Kontlin 合成器完美配合的代码。

import kotlinx.android.synthetic.main.dialog_reward.*

class RewardDialog: DialogFragment() {
    private var mView: View? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return mView
    }
    
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            mView = it.layoutInflater.inflate(R.layout.dialog_reward, null)
            AlertDialog.Builder(it).apply {
                setView(mView)
            }.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        //reference layout elements by name freely
        
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        mView = null
    }
}

如何将其迁移到视图绑定?

class RewardDialog : DialogFragment() {

    private var mView: View? = null
    private lateinit var dialogBinding: DialogRewardBinding

    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // either this way we can init dialogBinding
        dialogBinding = DialogRewardBinding.inflate(inflater, container, false)
        return dialogBinding.root
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            // either this way we can init dialogBinding
            dialogBinding = DataBindingUtil.setContentView(it, R.layout.dialog_reward)
            AlertDialog.Builder(it).apply { setView(mView) }.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        with(view) {
            myText.text = "Demo"
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mView = null
    }
}

代替mView,您可以使用dialogBinding.root

布局

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/myText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="demo"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

您可以在这里简单地使用生成的 ViewBinding 视图而不使用 onCreateDialog

@AndroidEntryPoint
class RewardDialog : DialogFragment() {
private var binding: DialogRewardBinding by autoCleared()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setStyle(STYLE_NORMAL, R.style.Theme_MaterialComponents_Light_Dialog_MinWidth)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    binding = DialogRewardBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    //reference layout elements by name freely
    binding.tvReward.setOnClickListener { }
   }

}

autoCleared() 是一个扩展函数,它将清空 onDestroy() 中取自 google's architecture component sample here

的所有视图

您可以在 onCreate() 中设置 R.style.Theme_MaterialComponents_Light_Dialog_MinWidth 主题,以便 DialogFragment 遵循 Material Compnent 主题中定义的 minWidth 在不同的屏幕尺寸上就像 AlertDialog

编辑:

如果您没有使用 material 组件库,那么您可以使用 Kotlin 扩展在 onViewCreated() 中设置宽度。

 setWidthPercent(ResourcesCompat.getFloat(resources, R.dimen.dialogWidthPercent).toInt())

Kotlin 扩展函数

fun DialogFragment.setWidthPercent(percentage: Int) {
val percent = percentage.toFloat() / 100
val displayMetrics = Resources.getSystem().displayMetrics
val rect = displayMetrics.run { Rect(0, 0, widthPixels, heightPixels) }
val percentWidth = rect.width() * percent
dialog?.window?.setLayout(percentWidth.toInt(), ViewGroup.LayoutParams.WRAP_CONTENT)

} 

我最终得到了以下解决方案。感谢@Kishan Maurya 提供有关 binding.root.

的提示
private var _binding: DialogRewardBinding? = null
private val binding get() = _binding!!

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.run {
        //initiate the binding here and pass the root to the dialog view
        _binding = DialogRewardBinding.inflate(layoutInflater).apply {
            //reference layout elements by name freely here
        }
        AlertDialog.Builder(this).apply {
            setView(binding.root)
        }.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}


override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}