Mic*_*l m 3 android mvvm viewmodel kotlin androidx
离开 Android 开发一段时间后,我尝试从一个简单的项目重新开始。
我创建了一个新项目,选择“基本活动”选项,这导致了一个 MainActivity 和两个片段。从这里开始,由于主要功能需要数据库,因此我遵循了“Room with a view”codelab,但它只有一个活动。在我的项目中,我在活动中设置了一个观察者,一切工作正常,但是,一旦我将观察者移动到第一个片段中并使用“by ActivityViewModels”“检索”ViewModel,应用程序就开始抛出实例化异常。原因:MyViewModel 没有零参数构造函数。
经过一些调试后,我注意到片段中的“by ActivityViewModel”属性在活动中的“by viewModel”之前被调用。ViewModel 有一个工厂,我希望它的范围仅限于活动,稍后可以从第二个片段访问。
视图模型:
class MyViewModelFactory(private val repository: MyRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return MyViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
class MyViewModel(private val repository: MyRepository): ViewModel() {
val list: LiveData<List<Item>> = repository.allItems.asLiveData()
}
Run Code Online (Sandbox Code Playgroud)
活动
...more imports
import androidx.activity.viewModels
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
val myViewModel: MyViewModel by viewModels {
MyViewModelFactory((application as MyApplication).repository)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
val navController = findNavController(R.id.nav_host_fragment_content_main)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
myViewModel.list.observe(this) { list ->
print(list.size)
}
}
}
Run Code Online (Sandbox Code Playgroud)
分段
...more imports
import androidx.fragment.app.activityViewModels
class ListFragment : Fragment() {
private var _binding: FragmentListBinding? = null
private val binding get() = _binding!!
val sharedViewModel: MyViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel.list.observe(viewLifecycleOwner) { list ->
print(list.size)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Run Code Online (Sandbox Code Playgroud)
依赖关系
def room_version = "2.4.2"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
// Room components
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
//same result enabling these dependencies
//implementation 'androidx.activity:activity-ktx:1.4.0'
//implementation 'androidx.fragment:fragment-ktx:1.4.1'
//implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
Run Code Online (Sandbox Code Playgroud)
据我了解,活动中的“by viewModels { //factory method }”属性应该使用工厂实例化viewModel,然后片段中的“:viewModelType by ActivityViewModel”属性(没有工厂选项)检索ViewModel定义的类型(如果已由父活动实例化)。
如果我理解正确的话,为什么在“by viewModels”之前调用“by ActivityViewModels”?不应该是相反吗?我该如何修复它?
您应该修改您的视图模型、活动和片段。
首先,对于您的 ViewModel,ViewModelProvider.Factory 已被弃用,因此请使用它:
class MyViewModel(application: Application): AndroidViewModel(application) {
private val repository by lazy { MyRepository.newInstance(application) }
val list: LiveData<List<Item>> = repository.allItems.asLiveData()
}
Run Code Online (Sandbox Code Playgroud)
然后,在你的活动课中:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
val myViewModel by viewModels<MyViewModel>()
Run Code Online (Sandbox Code Playgroud)
对于你的片段:
class ListFragment : Fragment() {
private var _binding: FragmentListBinding? = null
private val binding get() = _binding!!
val sharedViewModel by activityViewModels<MyViewModel>()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6965 次 |
| 最近记录: |