You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:10:32