Kotlin Android 扩展替换 findViewById 如何防止空视图?
How does Kotlin Android Extensions replacement for findViewById prevent null views?
我知道 Kotlin 的 Android 扩展创建合成属性 + 缓存函数来替换任何需要调用 findViewById
:
- https://www.raywenderlich.com/84-kotlin-android-extensions
- https://antonioleiva.com/kotlin-android-extensions/
所有这些示例都表明类似的 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
是什么类型? TextView
或 TextView?
What I don't understand is how this prevents potential NPEs?
没有。如果您尝试引用不存在的小部件,则会崩溃。
只要您的 import
语句仅用于您的 Kotlin 代码的相关布局,您就不应该引用不存在的小部件。如果您不小心从另一个布局导入了合成属性,就会出现问题。
例如,假设您有一个具有 activity_main.xml
和 scrap.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();
}
我知道 Kotlin 的 Android 扩展创建合成属性 + 缓存函数来替换任何需要调用 findViewById
:
- https://www.raywenderlich.com/84-kotlin-android-extensions
- https://antonioleiva.com/kotlin-android-extensions/
所有这些示例都表明类似的 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
是什么类型? TextView
或 TextView?
What I don't understand is how this prevents potential NPEs?
没有。如果您尝试引用不存在的小部件,则会崩溃。
只要您的 import
语句仅用于您的 Kotlin 代码的相关布局,您就不应该引用不存在的小部件。如果您不小心从另一个布局导入了合成属性,就会出现问题。
例如,假设您有一个具有 activity_main.xml
和 scrap.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 的
正如@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();
}