【问题标题】:View Binding in DialogFragment with custom layout in Kotlin在 Kotlin 中使用自定义布局查看 DialogFragment 中的绑定
【发布时间】:2021-03-17 17:42:09
【问题描述】:

我喜欢使用 Kotlin 合成,因为它的简单性和代码优雅,但现在他们贬低了它,并推动你使用那些丑陋的视图绑定。

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

这是与 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
    }
}

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

【问题讨论】:

    标签: android kotlin android-viewbinding


    【解决方案1】:

    我最终得到了以下解决方案。感谢@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
    }
    

    【讨论】:

      【解决方案2】:
      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
          }
      }
      

      你可以用dialogBinding.root代替mView

      布局

      <?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>
      

      【讨论】:

      • onCreateView 应返回 null 类型的值,但 binding.root 不是 null 类型。
      • 这会导致问题吗?它期待 null 类型,但如果我们传递的不是 null 类型,那么它不会是一个问题。
      • 如果它给出警告,那么这有问题
      • 这是使用 ViewBinding 的错误方式。使用 DataBindingUtil 解决另一个问题是造成 OP 问题的原因。 @yaugenka 您在这里使用警报对话框生成器的任何具体原因?
      • @SomeshKumar,我希望它看起来像 AlertDialog 并且不要手动弄乱对话框!!.window?.setLayout
      【解决方案3】:

      您可以在这里简单地使用生成的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 一样

      编辑:

      如果您不使用材质组件库,则可以使用 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)
      
      } 
      

      【讨论】:

      • 此解决方案是否需要在项目中添加材料组件?
      • @yaugenka 是的。您可以使用 AppCompact,也可以使用 Kotlin 函数,该函数将在运行时设置对话框窗口的宽度。
      • 我没有使用材质组件,而是更喜欢使用警报对话框构建器,而不是弄乱对话框?.window?.setLayout 调用。
      • @yaugenka 查看windowFixedWidthMajorwindowFixedWidthMinor
      • 在这里设置样式并不好,因为它会将所有样式自定义设置为默认值。
      猜你喜欢
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      • 2013-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多