Android Studio自定义视图没有办法预览
我想大家应该都和我一样,如果看到布局的编码的时候如果右边能够非常直观地显示出对应的视图,心里会非常舒心,像官方提供的tools命名空间就是为了这个目的而存在的。而且如果视图能够预览,其实无形中能够提高我们的开发效率。
但是,是不是总有一些时候,你会发现右边的预览界面出了问题,怎么也显示不出来?这个时候,在以前我,我也会吐嘈一句,谷歌开发的什么垃圾,连预览这种简单的功能都做不好吗?然而,其实大多数情况下,垃圾的是我自己。
尝试来重现这个情况,首先我定一个一个布局文件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.xmlpreviewdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
可以看到这个xml文件中使用了我们自定义的视图MyTextView
,相信大家也能感觉到,只有自定义的视图才会出现无法预览的问题,但是官方的视图就从来不会出现对应的问题。OK,我们来看一下自定义视图的实现,
class MyTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
override fun onFinishInflate() {
if(MyApplication.application == null){
throw NullPointerException("two param constructor cannot invoke application!")
}
super.onFinishInflate()
}
}
这段代码演示了在视图流程相关方法中引用到Application的context的情况,其实我们的项目中像获取Bitmap这些资源的方法,默认不传context的情况就是直接将Application充当context进而获取到相关的资源。然后在预览的过程中就出现问题了,我们都知道预览功能说到底只是对于xml的解析,然后根据其中的资源生成对应的图像显示出来,所以没有必要单纯为了预览这么一个简单的插件功能去专门启动一个应用,所以当然就不会设置Application,所以在生成对应布局的时候当然会出现问题。解决这个问题也很简单,将视图中将Application充当context的地方改为通过getContext()
方法获取就行了。
另外,其实Android Studio也是给足了提示,看见预览窗口右上角这个红色的感叹号了吗?这说明了界面的绘制出现了问题,点击就能看到出现问题的原因,所以为了我们工作能更加快乐,大家还是多留意一下这个问题吧
这是IDE给出的Exception Stack,你看,我们就能看到这里无法预览的原因就在将Application充当Context。
java.lang.NullPointerException: two param constructor cannot invoke application!
at com.example.xmlpreviewdemo.MyTextView.onFinishInflate_Original(MyTextView.kt:13)
at com.example.xmlpreviewdemo.MyTextView.onFinishInflate(MyTextView.kt)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:876)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:837)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:866)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:837)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:394)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:323)
at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:394)
at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:200)
at com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:572)
at com.android.tools.idea.rendering.RenderTask.lambda$inflate$5(RenderTask.java:698)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)