具有合成绑定和可空性的Kotlin视图

时间:2018-03-14 11:36:33

标签: android kotlin kotlin-android-extensions

我注意到在使用Kotlin的合成绑定时,返回的视图为非null(Kotlin将返回View!)。但这对我来说没有多大意义,因为findCachedViewById实际上可以返回null结果,这意味着视图实际上可以为null。

public View _$_findCachedViewById(int var1) {
  if(this._$_findViewCache == null) {
     this._$_findViewCache = new HashMap();
  }

  View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
  if(var2 == null) {
     View var10000 = this.getView();
     if(var10000 == null) {
        return null;
     }

     var2 = var10000.findViewById(var1);
     this._$_findViewCache.put(Integer.valueOf(var1), var2);
  }

  return var2;
}

那么为什么在这种情况下它们不是可选的呢?为什么Kotlin在使用合成绑定时只返回View?,这样开发人员在处理视图时会被迫检查可空性?

也许只是因为我是Kotlin的新手,但我认为这有点反直觉,因为变量不是可选的,但我们仍然应该检查View是否实际上不是null。

所以在这种情况下,做下面的代码是否有意义?

view?.let {
    // handle non null view here
}

4 个答案:

答案 0 :(得分:6)

我想通了,我发帖后总能找到正确的SO问题:)

View之后的单个惊叹号实际上并不意味着视图不能像我预期的那样为空。

另一个问题的answer基本上回答了我的确切问题。使用合成绑定时,View实际上可以为空,但我们无法确定,因此是单个感叹号。

因此,可以安全地假设我在上面发布的代码 - 使用?.let{...}是完全可以接受的方式来处理视图,当您不确定它们是否已经在访问时已经初始化。

视图可能为null的情况非常罕见,但可能会发生。

答案 1 :(得分:1)

正如您已经指出的,单个感叹号并不意味着它不为空,而是它是Java平台类型,并且编译器不知道它是否可以为空。

我认为您的建议很好,尽管在实际为null的实际情况下它默默地失败了,而这实际上可能并不是您想要的。

假设您尝试在onCreateView中调用视图,但忘记了它尚未初始化。该片段将无法正常运行,但不会产生有意义的错误来帮助您调试问题。

我仍在尝试自己解决一个问题,但我还是建议明确处理null的情况:

table * {
    border: none;
}

或者决定这次您实际上希望它抛出NullPointerException,在这种情况下,我建议:

view?.{
    //...
} ?: throwExceptionIfDebugElseLogToCrashlytics()

后者不会因为“应该”是不可能的边缘情况而使您的代码膨胀,并且不会默默地失败,但是它仍然使读者清楚地看到视图可能为空。显然是!编译器不需要它,只是使所选择的用于处理平台类型的策略更加明确。

答案 2 :(得分:0)

我们的想法是Android中的xml布局非常静态,为了使用合成视图,您必须创建直接导入已解析的布局:

import kotlinx.android.synthetic.main.activity_main.*

因此,没有真实的,非魔法的场景,View将为空。除非你选择了错误的合成布局,否则你将在第一次运行时遇到崩溃。

也就是说,如果你在运行时修改视图,删除Views等,它当然会中断。但同样,这不是合成Views的默认用法,需要采用不同的方法。

答案 3 :(得分:0)

如果尝试从活动或视图的上下文之外或在lambda中从侦听器访问视图,则合成视图绑定可能会发生空指针异常。

问题出在lambda上,Frantisek在这里发表了有关此问题的文章: https://stackoverflow.com/posts/comments/115183445?noredirect=1