如何在自定义LinearLayout中实例化AndroidViewModel?
问题描述
我正在创建用于自定义日历的自定义LinearLayout视图,之前用SQLiteHelper获取数据,现在迁移到Room数据库,但无法实例化AndroidViewModel并获取数据。强转Context为Application时出现错误,请问该如何解决?是否有方法获取Application来实例化AndroidViewModel,或者仅使用Context就能实现?
AndroidViewModel代码
class AdminExpensesVM(application: Application):AndroidViewModel(application) { private val repository:AdminExpensesRepo val readAll :LiveData<List<AdminExpensesData>> init { val adminExpensesDB=AdminExpensesDatabase.getInstance(application).adminExpensesDao repository= AdminExpensesRepo(adminExpensesDB) readAll=repository.getAllExpenses() } }
自定义LinearLayout代码
class Admin_Calender : LinearLayout { private lateinit var adminExpensesVM: AdminExpensesVM; constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val view: View = inflater.inflate(R.layout.admin_calender, this) adminExpensesVM=ViewModelProvider.AndroidViewModelFactory.getInstance(context as Application).create(AdminExpensesVM::class.java) } }
解决方案
方法1:安全获取Application实例并初始化ViewModel
不要直接强转Context为Application,改用applicationContext获取全局实例并做类型校验,避免崩溃:
class Admin_Calender : LinearLayout { private lateinit var adminExpensesVM: AdminExpensesVM private var dataObserver: Observer<List<AdminExpensesData>>? = null constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater inflater.inflate(R.layout.admin_calender, this) context?.let { ctx -> val application = ctx.applicationContext as? Application application?.let { app -> val factory = ViewModelProvider.AndroidViewModelFactory.getInstance(app) adminExpensesVM = ViewModelProvider(this, factory)[AdminExpensesVM::class.java] // 观察数据更新日历 dataObserver = Observer { data -> // 处理数据,更新日历UI } adminExpensesVM.readAll.observeForever(dataObserver!!) } } } // 销毁时移除观察者,避免内存泄漏 override fun onDetachedFromWindow() { super.onDetachedFromWindow() dataObserver?.let { adminExpensesVM.readAll.removeObserver(it) } } }
方法2:通过宿主组件管理ViewModel(更推荐)
自定义View本身不适合直接持有ViewModel,更合理的方式是在Activity/Fragment中初始化ViewModel,再将数据传递给自定义View,这样能更好地遵循Android生命周期规范:
1. 在宿主Activity中初始化ViewModel
class AdminCalendarActivity : AppCompatActivity() { private lateinit var adminExpensesVM: AdminExpensesVM override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_admin_calendar) adminExpensesVM = ViewModelProvider(this)[AdminExpensesVM::class.java] val customCalendar = findViewById<Admin_Calender>(R.id.admin_calender) // 观察数据并传递给自定义View adminExpensesVM.readAll.observe(this) { data -> customCalendar.updateCalendarData(data) } } }
2. 自定义View中添加数据更新方法
class Admin_Calender : LinearLayout { constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater inflater.inflate(R.layout.admin_calender, this) } // 接收外部传递的数据并更新UI fun updateCalendarData(expensesData: List<AdminExpensesData>) { // 这里实现日历UI的更新逻辑 } }
关键注意事项
- 禁止直接强转Context为Application,Context可能是Activity、Dialog等类型,强转会触发
ClassCastException - 使用
applicationContext获取全局Application实例时,必须做类型安全校验 - 若在自定义View中使用
observeForever,务必在View销毁时调用removeObserver,防止内存泄漏
内容的提问来源于stack exchange,提问作者Supun Ayeshmantha
相关产品推荐
相关产品推荐

