【问题标题】:BaseFragment with viewbindingBaseFragment 与视图绑定
【发布时间】:2021-03-19 03:29:27
【问题描述】:

要在 Android 应用程序中使用 viewbinding,我基本上是为 ActivityFragment 创建基类,以删除每次编写膨胀代码的样板。

活动:

BaseActivityviewbinding

abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = getViewBinding()
    }

    abstract fun getViewBinding(): VB

}

MainActivity:

class MainActivity : BaseActivity<ActivityMainBinding>() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        //we can directly use binding now and it works fine inside activity
        //binding.view.doSomething() 
    }

 override fun getViewBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
}

片段

BaseFragment:

abstract class BaseFragment<VB : ViewBinding> : Fragment() {

    var binding: VB? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = getViewBinding(view)
    }

    abstract fun getViewBinding(view: View): VB
}

DemoFragment:

class DemoFragment : BaseFragment<DemoFragmentBinding>() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //problem is here
        binding.txtData.text="Something"
    }

    override fun getViewBinding(view: View): DemoFragmentBinding = DemoFragmentBinding.bind(view)

}

demo_fragment.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.fragments.DemoFragment">

    <TextView
        android:id="@+id/txt_data"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello" />

</FrameLayout>

问题:无法使用 Demofragment 中的绑定访问视图。我不知道为什么它适用于活动而不适用于片段。

我不想做的第二种方式:

实现 'androidx.fragment:fragment-ktx:1.3.1'

class DemoFragment : Fragment(R.layout.demo_fragment) {

    lateinit var binding: DemoFragmentBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = DemoFragmentBinding.bind(view).apply {
            txtData.text = "Hello World"
        }
    }
}

【问题讨论】:

标签: android kotlin android-mvvm android-viewbinding


【解决方案1】:

您需要在 BaseFragment 中覆盖 onCreateView 并初始化视图绑定

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

那就改这一行

override fun getViewBinding(view: View): DemoFragmentBinding = DemoFragmentBinding.bind(view)

override fun getViewBinding() = DemoFragmentBinding.inflate(layoutInflater)

BaseFragment

abstract class BaseFragment<VB : ViewBinding> : Fragment() {
    private var _binding: VB? = null
    val binding get() = _binding!!

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

    abstract fun getViewBinding(): VB
}

DemoFragment

class DemoFragment : BaseFragment<DemoFragmentBinding>() {

    override fun getViewBinding() = DemoFragmentBinding.inflate(layoutInflater)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.apply {
            txtData.text = "Something"
        }
    }
}

【讨论】:

    【解决方案2】:

    这是用ViewBinding 实现此factory abstraction 的另一种方法。我在下面分享实现代码。我在这里使用genrics。如果有人需要进一步解释,我在这里。确保您已经在build.gradle 文件中启用了viewbinding 功能。然后使用以下BaseFragment.kt 作为您的片段抽象。

    BaseFragment:

    typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T
    
    abstract class BaseFragment<V: ViewBinding>(
        private val inflate: Inflate<V>
        ) : Fragment() {
    
        private lateinit var _binding: V
        val binding get() = _binding
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            _binding = inflate(inflater, container, false)
            return binding.root
        }
    }
    

    N:B:进一步了解typealias

    HomeFragment:

    // Implement the BaseFragment like below
    class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            // usages by calling public variable 'binding' from base class
            binding.message.text = "update $value"
        }
    }
    

    【讨论】:

      【解决方案3】:

      我查看了文档,我不确定这是否会破坏您对 BaseFragment 中抽象模式的要求,但我使用此更改测试了您的代码并且它有效。唯一的变化是DemoFragment

      //Add this for the onCreateView implementation
          override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
              binding = DemoFragmentBinding.inflate(LayoutInflater.from(context), null, false)
              val view = binding!!.root
              return view
          }
      

      然后我在onViewCreated进行测试:

          override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
              super.onViewCreated(view, savedInstanceState)
              //problem is here
              binding?.txtData?.text="Something"
      
          }
      

      它奏效了。我查看的文档是this section

      我认为这样做仍然可以让你避免一些样板,但 onCreateView 覆盖对于每个片段都是必要的,因为不同的视图绑定膨胀(例如DemoFragmentBinding.inflate etc etc

      【讨论】:

      • 我可以直接使用 androidx.fragment:fragment-ktx:1.2.0 在 Fragment 构造函数参数中传递 demo_fragment.xml 而无需覆盖 onCreateView 但我不想这样做!虽然感谢您的回答。
      • 什么意思可以传递layout_file?
      • 传递布局 xml 不会给你视图绑定。您需要专门使用DemoFragmentBinding.inflate( 方法进行充气
      【解决方案4】:

      使用https://github.com/hoc081098/ViewBindingDelegate

      abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragment(layoutId) {
        protected abstract val binding: VB
      }
      
      
      import com.hoc081098.viewbindingdelegate.*
      class MainFragment: BaseFragment<FragmentMainBinding>(R.layout.fragment_main) {
        override val binding by viewBinding()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-20
        • 2020-06-20
        相关资源
        最近更新 更多