简体   繁体   English

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

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

I know that Kotlin's Android Extensions creates synthetic properties + caching function to replace any need to call findViewById :我知道 Kotlin 的 Android 扩展创建了合成属性 + 缓存函数来替换调用findViewById任何需要:

All of these examples show that the similar java code would look like所有这些例子都表明类似的 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();
   }
}

What I don't understand is how this prevents potential NPEs?我不明白的是这如何防止潜在的 NPE? var2 = this.findViewById(var1); may still return null.可能仍然返回 null。

Using the example from that last 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!"
}

What type is welcomeMessage ? welcomeMessage是什么类型? TextView or TextView? TextView还是TextView?

What I don't understand is how this prevents potential NPEs?我不明白的是这如何防止潜在的 NPE?

It doesn't.它没有。 If you try referencing a widget that does not exist, you crash.如果您尝试引用不存在的小部件,则会崩溃。

So long as your import statements are only for the relevant layout for your Kotlin code, you should not wind up referencing a widget that does not exist.只要您的import语句仅用于 Kotlin 代码的相关布局,您就不应该引用不存在的小部件。 Where the problem comes in is if you accidentally import the synthetic properties from another layout.问题出现的地方是,如果您不小心从另一个布局导入了合成属性。

For example, suppose you have a project with activity_main.xml and scrap.xml layouts, and your activity is:例如,假设您有一个具有activity_main.xmlscrap.xml布局的项目,并且您的活动是:

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
  }
}

Here, we are referencing a scrapView view in the scrap layout.在这里,我们在scrap布局中引用了一个scrapView视图。 We have not inflated that layout, and so this crashes with an IllegalStateException :我们没有夸大该布局,因此会因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?什么类型是welcomeMessage? TextView or TextView?文本视图还是文本视图?

Technically, it is TextView!从技术上讲,它是TextView! , where ! ,哪里! means "it's a platform type, so we don't know if it can be null or not".意思是“它是一种平台类型,所以我们不知道它是否可以为null ”。 Practically, TextView!实际上, TextView! is used the same as TextView , which is why you crash if it winds up being null .TextView用法相同,这就是为什么如果它最终为null就会崩溃的原因。

While @CommonsWare's answer is correct, I also wanted to spare my 2 cents on this subject.虽然@CommonsWare 的回答是正确的,但我也想在这个主题上节省 2 美分。

As @CommonsWare pointed out, you have to import relevant layout in order to be able to use Kotlin Extensions .正如@CommonsWare 指出的那样,您必须导入相关布局才能使用Kotlin Extensions The tricky part here is, it's not only about importing relevant layout but also inflating the layout before you call it with Kotlin Extensions.这里的棘手部分是,它不仅涉及导入相关布局,而且还涉及在使用 Kotlin 扩展调用布局之前对其进行扩充。

So, if you have something like below所以,如果你有类似下面的东西

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)

  }
}

You will still get你仍然会得到

java.lang.IllegalStateException: welcomeMessage must not be null java.lang.IllegalStateException:welcomeMessage 不能为空

And your app will crash.你的应用程序会崩溃。

I got crash with NPE also in listener.我也在侦听器中与 NPE 崩溃了。 I made ZoomRecyclerView, which call onZoom listener method onZoom when zoomed.我制作了 ZoomRecyclerView,它在缩放时调用 onZoom 侦听器方法 onZoom。 This event is then propagated to activity, where i call synthetic imagebuttonview method, to set image resource to set zoom button image if item is zoomed or not.然后将此事件传播到活动,在那里我调用合成 imagebuttonview 方法,以设置图像资源以设置缩放按钮图像(如果项目已缩放)。 It simply crash on synthetic call.它只是在合成调用时崩溃。

Example :例子 :

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM