Kotlin Android 扩展替换 findViewById 如何防止空视图?

How does Kotlin Android Extensions replacement for findViewById prevent null views?

我知道 Kotlin 的 Android 扩展创建合成属性 + 缓存函数来替换任何需要调用 findViewById:

所有这些示例都表明类似的 java 代码看起来像

private HashMap _$_findViewCache;
...
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) {
      var2 = this.findViewById(var1);
      this._$_findViewCache.put(Integer.valueOf(var1), var2);
   }

   return var2;
}

public void _$_clearFindViewByIdCache() {
   if(this._$_findViewCache != null) {
      this._$_findViewCache.clear();
   }
}

我不明白的是这如何防止潜在的 NPE? var2 = this.findViewById(var1); 可能仍然 return 为空。

使用上一个 link 中的示例:

<TextView
        android:id="@+id/welcomeMessage"
        ... 
        android:text="Hello World!"/>

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    welcomeMessage.text = "Hello Kotlin!"
}

welcomeMessage是什么类型? TextViewTextView?

What I don't understand is how this prevents potential NPEs?

没有。如果您尝试引用不存在的小部件,则会崩溃。

只要您的 import 语句仅用于您的 Kotlin 代码的相关布局,您就不应该引用不存在的小部件。如果您不小心从另一个布局导入了合成属性,就会出现问题。

例如,假设您有一个具有 activity_main.xmlscrap.xml 布局的项目,并且您的 activity 是:

package com.commonsware.android.myapplication

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.scrap.*

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    scrapView.visibility = View.GONE
  }
}

在这里,我们在 scrap 布局中引用了一个 scrapView 视图。我们没有膨胀那个布局,所以这会崩溃 IllegalStateException:

 Caused by: java.lang.IllegalStateException: scrapView must not be null
    at com.commonsware.android.myapplication.MainActivity.onCreate(MainActivity.kt:14)

What type is welcomeMessage? TextView or TextView?

从技术上讲,它是 TextView!,其中 ! 表示 "it's a platform type, so we don't know if it can be null or not"。实际上,TextView!TextView 的用法相同,这就是为什么当它变成 null.

时你会崩溃的原因

虽然@CommonsWare 的 是正确的,但我也想在这个问题上节省我的 2 美分。

正如@CommonsWare 所指出的,您必须导入相关布局才能使用Kotlin Extensions。这里棘手的部分是,它不仅涉及导入相关布局,还涉及在使用 Kotlin Extensions 调用它之前扩充布局。

所以,如果你有类似下面的东西

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

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    welcomeMessage.text = "Hello Kotlin!"
    setContentView(R.layout.activity_main)

  }
}

你仍然会得到

java.lang.IllegalStateException: welcomeMessage must not be null

你的应用程序会崩溃。

我在监听器中也遇到了 NPE 崩溃。 我制作了 ZoomRecyclerView,它在缩放时调用 onZoom 监听器方法 onZoom。 然后将此事件传播到 activity,我在其中调用合成 imagebuttonview 方法,以设置图像资源以设置缩放按钮图像(如果项目是否缩放)。 它只是在合成调用时崩溃。

示例:

zoomRecycler.onZoom {
  // exception here : zoomButton must be not null
  zoomButton.setImageDrawable(...)
}

zoomButton.click {
  // calling zoom which raise onZoom inside zoomRecycler
  zoomRecycler.toggleZoom();
}