首页 > 解决方案 > 智能投射到“DrawerLayout!” 是不可能的,因为 'drawerLayout' 是一个可变属性,此时可能已更改

问题描述

我是 android 开发的完全新手,过去两天我一直被这个问题困扰,我的生活中从未感到如此沮丧。先来个小故事,

我正在创建最基本的图书库应用程序,并尝试向应用程序添加导航抽屉。在 kotlin 文件中,当我使用lateinit声明所有变量(对应于布局文件中创建的标签)时,它会抛出一个nullPointerException。出于这个原因,我开始像这样声明我的变量:

var drawerLayout: DrawerLayout? = null

这有助于我避免异常。现在来到真正的问题,

我试图以这种方式在 onCreate 方法中为我的 actionBarDrawerToggle创建一个单击侦听器:

    val actionBarDrawerToggle = ActionBarDrawerToggle(this@MainActivity, drawerLayout,  R.string.open_drawer, R.string.close_drawer)

    drawerLayout.addDrawerListener(actionBarDrawerToggle)   
    actionBarDrawerToggle.syncState()

并且由于某种原因,“drawerLayout.addDrawerListener(actionBarDrawerToggle)”行的drawerLayout部分带有红色下划线,表示存在错误。当我运行它时,这是它在构建窗口中显示的错误:

    Smart cast to 'DrawerLayout!' is impossible, because 'drawerLayout' is a mutable property that could have been changed by this time

我不知道如何进行此操作。我已经尝试了很多东西,但它们都不起作用。我认为该错误可能与我上面描述的我使用的声明方法有关。如果有人可以帮助我,那就太好了

标签: androidxmlandroid-studiokotlinnavigation-drawer

解决方案


您已将其定义drawerLayout为可空 ( DrawerLayout?) var,这意味着该值可以随时更改,并且该值可以为空。因此,即使您进行空检查以确保它不为空,也可能随时改变!编译器无法保证调用是安全的。

以下是一些选项,从坏到好:


通过引用将变量显式标记为非空drawerLayout!!。这是你告诉编译器“我知道发生了什么,这很好,相信我”。您应该避免这样做,因为这是一个危险信号,您会忽略警告,最终您会遇到错误的情况


将非空值复制到一个临时变量中,你知道它不会改变。这是在 Kotlin 中处理可空值的典型方法,您可以使用以下范围函数之一let

drawerLayout?.let { drawer -> //  do stuff with drawer }

那是在做一个空检查(使用?),如果它不为空,它会调用let具有该非空值的块。而且您知道该值在块内不会改变。我已经调用了该变量drawer,但您可以省略它,it默认情况下会调用它。还有其他范围功能,例如run,做同样事情的方式略有不同。

如果您只需要这样做,您也可以只调用检查变量本身:

drawerLayout?.doAThingItsNotNullSoItsFine()

这基本上在引擎盖下做同样的事情


首先不要使drawerLayoutnullable !它实际上永远不应该为空,对吧?它应该始终存在,并且在您尝试访问它时具有价值?然后为自己省去仇恨和一直对它进行空检查的需要——这就是lateinit目的。

lateinit允许你声明一个变量而不实际给它一个值。通常你必须这样做,这就是你在这里所做的正确 - 你已经让它可以为空,这样你就可以在那里粘贴一个临时的空值。lateinit是对编译器的一个承诺,它没关系,你还没有提供一个值,但你肯定会在任何尝试访问它之前设置一个值。(这就是为什么它被称为后期初始化!)

因此,您遇到的问题是在尝试读取它之前您实际上并没有设置该值。您可以检查它是否已设置为::drawerLayout.isInitialized,但实际上您应该充分了解您的代码流程,以确保只有在初始化之后才能读取。如果您不能保证这一点,那么您可能处于一种状态,即您没有其他东西可能需要访问的变量的值 - 这是使其可空并使用空处理功能的一个很好的候选者顺利处理


推荐阅读