就是现在 發表於 2025-10-24 16:54:40

在 Kotlin 中ViewModel 的获取及使用指南

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>在 Kotlin 中,ViewModel 的获取</li><li>Kotin- Fragment 获取 ViewModel 实例正确方式</li><ul class="second_class_ul"><li>1. 第一种获取方式:</li><li>2. 第二种获取方式</li></ul></ul></div><p class="maodian"></p><h2>在 Kotlin 中,ViewModel 的获取</h2>
<p>在 Kotlin 中,<code>by viewModels()</code> 和 <code>by viewModels { }</code> 都是用来在 Activity 或 Fragment 中获取 ViewModel 实例的属性委托。它们之间的区别在于是否传递自定义的 ViewModelProvider.Factory<strong>:</strong></p>
<ul><li><code>by viewModels()</code>: 使用默认的 ViewModelProvider.Factory,通常,这个默认工厂会使用 ViewModel 的无参构造函数来创建 ViewModel 实例。
<ul><li>如果 ViewModel 没有参数,或者使用了依赖注入(如 Hilt)来提供 ViewModel,可以使用这种形式;</li></ul></li><li><code>by viewModels {...}</code>: 花括号内是一个 Lambda,用于提供自定义的 ViewModelProvider.Factory。当 ViewModel 需要参数时使用,必须提供自定义的 Factory 来创建 ViewModel 实例;</li></ul>
<p><strong>使用指南:</strong></p>
<ul><li>如果 ViewModel 不需要外部参数:使用 <code>by viewModel()</code>;</li><li>如果 ViewModel 需要参数或依赖:使用 <code>by viewModels { customFactory }</code>;</li><li>如果使用 Hilt/Dagger 等 DI 框架:使用 <code>by viewModels()</code>(框架自动处理);</li></ul>
<p><strong>添加依赖:</strong></p>
<div class="jb51code"><pre class="brush:java;">dependencies {
    // 必需:ViewModel 核心
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0"
    // 必需:Activity 或 Fragment 的委托支持(二选一或都选)
    implementation "androidx.activity:activity-ktx:1.8.2"      // Activity 中使用
    implementation "androidx.fragment:fragment-ktx:1.6.2"       // Fragment 中使用
}</pre></div>
<p>例如:假设我们有一个需要参数的 ViewModel:</p>
<div class="jb51code"><pre class="brush:java;">class MyViewModel(private val repository: MyRepository) : ViewModel() {
    // ...
}</pre></div>
<p>那么,在 Activity 或 Fragment 中,我们需要提供一个 Factory 来创建 MyViewModel:</p>
<div class="jb51code"><pre class="brush:java;">private val viewModel: MyViewModel by viewModels {
    object : ViewModelProvider.Factory {
      override fun &lt;T : ViewModel&gt; create(modelClass: Class&lt;T&gt;): T {
            // 假设我们已经有了MyRepository的实例
            return MyViewModel(myRepository) as T
      }
    }
}</pre></div>
<p>或者,你可以定义一个 ViewModelFactory 类:</p>
<div class="jb51code"><pre class="brush:java;">class MyViewModelFactory(private val repository: MyRepository) : ViewModelProvider.Factory {
    override fun &lt;T : ViewModel&gt; create(modelClass: Class&lt;T&gt;): T {
      if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return MyViewModel(repository) as T
      }
      throw IllegalArgumentException("Unknown ViewModel class")
    }
}</pre></div>
<p>然后,在委托属性中使用:</p>
<div class="jb51code"><pre class="brush:java;">private val viewModel: MyViewModel by viewModels {
    MyViewModelFactory(myRepository)
}</pre></div>
<p><strong>补充:Kotin- Fragment 获取 ViewModel 实例正确方式</strong></p>
<p class="maodian"></p><h2>Kotin- Fragment 获取 ViewModel 实例正确方式</h2>
<p class="maodian"></p><h3>1. 第一种获取方式:</h3>
<p>通常获取 ViewModel 方法:</p>
<div class="jb51code"><pre class="brush:java;"> val model : BlankViewModel by lazy{
      ViewModelProvider(this,BlankViewModel.BlankViewModelFactory()).get(BlankViewModel::class.java)
}
//该方法已被废弃:viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
</pre></div>
<p>对应的 BlankViewModelFactory 方法</p>
<div class="jb51code"><pre class="brush:java;">class BlankViewModel : ViewModel() {
    // TODO: Implement the ViewModel
    private val number = MutableLiveData&lt;Int&gt;();
    private var i=0
    fun getNumber() : LiveData&lt;Int&gt;{
      return number
    }
    fun addNumber(){
      i++
      number.value = i
    }
    class BlankViewModelFactory(): ViewModelProvider.Factory{
      override fun &lt;T : ViewModel?&gt; create(modelClass: Class&lt;T&gt;): T {
            return BlankViewModel() as T
      }
    }
}</pre></div>
<p>当我们创建 Fragment 页面,并将页面添加到一个 Activity 页面中时,想要实现在 Activity 中调用 ViewModel 中的方法使得 livedata 的数据改变,并希望 Fragment 页面也可以监听到变化,此时就需要获取到同一个 ViewModel 实例,才能保证两个页面的数据同时变化。</p>
<p>首先,在 Activity 中获取 BlankViewModel ,并通过点击按钮调用其中的 addNumber 方法,让变量+1</p>
<div class="jb51code"><pre class="brush:java;">class FragmentActivity : AppCompatActivity() {
    val model : BlankViewModel by lazy{
      ViewModelProvider(this,BlankViewModel.BlankViewModelFactory()).get(BlankViewModel::class.java)
    }
    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_fragment)
      val button = findViewById&lt;Button&gt;(R.id.add).setOnClickListener {
            model.addNumber()
      }
    }
}</pre></div>
<p>第一个 Fragment 页面:</p>
<div class="jb51code"><pre class="brush:java;">class LeftFragment : Fragment() {
    val model : BlankViewModel by lazy{
      ViewModelProvider(requireActivity(),BlankViewModel.BlankViewModelFactory()).get(BlankViewModel::class.java)
    }
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
      return inflater.inflate(R.layout.blank_fragment, container, false)
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      val left = view.findViewById&lt;TextView&gt;(R.id.left_tv)
      model.getNumber().observe(requireActivity(), Observer {
            Log.d("IT",""+it)
            left.text = it.toString()
      })
    }
}</pre></div>
<p>在获取 ViewModel 实例时,ViewModelProvider 中的第一个参数 ViewModelStoreOwner 对象传递的是requireActivity 方法,这个方法里就是获取当前的 Activity 实例 ;如果该位置仍然传 this ,则获取的是与 Activity 不同的 ViewModel 实例,这样 Activity 点击按钮时 Fragment 页面的数值并不会变化。</p>
<div class="jb51code"><pre class="brush:java;">val model : BlankViewModel by lazy{   
ViewModelProvider(requireActivity(),BlankViewModel.BlankViewModelFactory()).get(BlankViewModel::class.java)
}
</pre></div>
<p>在观察变化的方法中,也是同样的道理:</p>
<div class="jb51code"><pre class="brush:java;">model.getNumber().observe(requireActivity(), Observer {
            left.text = it.toString()
})
</pre></div>
<p>另一个 Fragment 页面和它一样,这里就不添加了。</p>
<p class="maodian"></p><h3>2. 第二种获取方式</h3>
<p>通过ktx简化:<br />添加 ktx 依赖,这里要注意版本,我的 kotlin 版本过低,下载高版本会报红:</p>
<div class="jb51code"><pre class="brush:java;">implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.activity:activity-ktx:1.0.0-alpha03"
</pre></div>
<p>在 Activity 页面中:</p>
<div class="jb51code"><pre class="brush:java;">//这里省略掉其他部分
val model by viewModels&lt;BlankViewModel&gt;()
</pre></div>
<p>在 Fragment 页面中</p>
<div class="jb51code"><pre class="brush:java;">//这里省略掉其他部分
val model by activityViewModels&lt;BlankViewModel&gt;()
//这里可以正常传this
model.getNumber().observe(this, Observer {
            Log.d("IT",""+it)
            left.text = it.toString()
})</pre></div>
<p>以上就是获取同一 ViewModel 实例的方法。如有不对,请指正。</p>
<p>到此这篇关于在&nbsp;Kotlin&nbsp;中ViewModel&nbsp;的获取及使用指南的文章就介绍到这了,更多相关Kotlin&nbsp;&nbsp;ViewModel&nbsp;获取内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>在 Kotlin 中ViewModel 的获取及使用指南</li><li>Kotlin&nbsp;ViewModelProvider.Factory的使用实例详解</li><li>Kotlin&nbsp;Jetpack组件ViewModel使用详解</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 在 Kotlin 中ViewModel 的获取及使用指南