CustomView 无法实例化(编辑器预览错误,编译后没有显示)

Posted

技术标签:

【中文标题】CustomView 无法实例化(编辑器预览错误,编译后没有显示)【英文标题】:CustomView couldn't be instantiated (error in editor preview and nothing shown after compile) 【发布时间】:2017-09-24 05:35:35 【问题描述】:

所以,我创建了一个名为ButtonWithCaption.xmlCustomView

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:layout_weight="1"
    android:background="?attr/selectableItemBackground"
    android:paddingBottom="@dimen/spacing_normal"
    android:paddingLeft="@dimen/spacing_small"
    android:paddingRight="@dimen/spacing_small"
    android:paddingTop="@dimen/spacing_normal">

<ImageView
    android:id="@+id/bwc_icon"
    android:layout_
    android:layout_
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="2dp"
    android:scaleType="centerCrop" />

<TextView
    android:id="@+id/bwc_caption"
    android:layout_
    android:layout_
    android:layout_below="@id/bwc_icon"
    android:textAlignment="center" />

连同它的控制器ButtonWithCaption.java

public class ButtonWithCaption extends RelativeLayout 
    private ImageView mImageView;
    private TextView mTextView;
    private int mIconSrc;
    private String mCaptionText;
    private boolean mShowIcon;
    private boolean mShowText;

    public ButtonWithCaption(Context context) 
        super(context);
        init(context, null, 0);
    

    public ButtonWithCaption(Context context, AttributeSet attrs) 
        super(context, attrs);
        init(context, attrs, 0);
    

    public ButtonWithCaption(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    

    private void init(Context context, AttributeSet attrs, int defStyleAttr) 
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ButtonWithCaption, 0, 0);

        try 
            mIconSrc = a.getResourceId(R.styleable.ButtonWithCaption_iconSrc, 0);
            mCaptionText = a.getString(R.styleable.ButtonWithCaption_captionText);
            mShowIcon = a.getBoolean(R.styleable.ButtonWithCaption_showIcon, mIconSrc != 0);
            mShowText = a.getBoolean(R.styleable.ButtonWithCaption_showText, !mCaptionText.isEmpty());

            mImageView = (ImageView) findViewById(R.id.bwc_icon);
            mTextView = (TextView) findViewById(R.id.bwc_caption);

            mImageView.setImageResource(mIconSrc);
            mTextView.setText(mCaptionText);
         finally 
            a.recycle();
        
    

没什么特别的。

然后,我在attrs.xml 中声明自定义属性

<resources>
    <declare-styleable name="ButtonWithCaption">
        <attr name="iconSrc" format="reference" />
        <attr name="captionText" format="string" />
        <attr name="showIcon" format="boolean" />
        <attr name="showText" format="boolean" />
    </declare-styleable>
</resources>

这是我的activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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_
    android:layout_
    tools:context="id.foodmap.foodmapcustomviews.MainActivity">

    <id.foodmap.foodmapcustomviews.ButtonWithCaption
        android:id="@+id/button_with_caption"
        android:layout_
        android:layout_
        app:captionText="Test"
        app:iconSrc="@drawable/ic_email_gray_24dp" />
</RelativeLayout>

...但奇怪的是,Android Studio 的预览给了我渲染错误:

无法实例化一个或多个类

使用堆栈跟踪:

java.lang.NullPointerException 在 id.foodmap.foodmapcustomviews.ButtonWithCaption.init(ButtonWithCaption.java:54) 在 id.foodmap.foodmapcustomviews.ButtonWithCaption.(ButtonWithCaption.java:29) 在 sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 在 sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 在 sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 在 java.lang.reflect.Constructor.newInstance(Constructor.java:423) 在 org.jetbrains.android.uipreview.ViewLoader.createNewInstance(ViewLoader.java:475) 在 org.jetbrains.android.uipreview.ViewLoader.loadClass(ViewLoader.java:262) 在 org.jetbrains.android.uipreview.ViewLoader.loadView(ViewLoader.java:220) 在 com.android.tools.idea.rendering.LayoutlibCallbackImpl.loadView(LayoutlibCallbackImpl.java:186) 在 android.view.BridgeInflater.loadCustomView(BridgeInflater.java:334) 在 android.view.BridgeInflater.loadCustomView(BridgeInflater.java:345) 在 android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:245) 在 android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727) 在 android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:858) 在 android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:70) 在 android.view.LayoutInflater.rInflate(LayoutInflater.java:834) 在 android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821) 在 android.view.LayoutInflater.inflate(LayoutInflater.java:518) 在 android.view.LayoutInflater.inflate(LayoutInflater.java:397) 在 com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:324) 在 com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:429) 在 com.android.ide.common.rendering.LayoutLibrary.createSession(LayoutLibrary.java:368) 在 com.android.tools.idea.rendering.RenderTask$2.compute(RenderTask.java:567) 在 com.android.tools.idea.rendering.RenderTask$2.compute(RenderTask.java:549) 在 com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:863) 在 com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:549) 在 com.android.tools.idea.rendering.RenderTask.lambda$inflate$1(RenderTask.java:680) 在 java.util.concurrent.FutureTask.run(FutureTask.java:266) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 在 java.lang.Thread.run(Thread.java:745)

我的findViewById()ButtonWithCaption.java 找到TextViewImageView 也返回null。

有人知道是什么原因造成的吗?因为它甚至没有出现在编辑器预览窗口中。

附:如果我使用 ,预览会正确显示元素。 P.S.S.它编译到我的手机没有错误,但没有显示ButtonWithCaption

提前致谢

【问题讨论】:

你在声明的 xml 中的自定义视图在哪里? ButtonWithCaption.java 的第 54 行是什么? 另外,显示使用 ButtonWithCaption 的代码?您是在activity_main.xml 或其他布局文件中使用它吗?或者您是否尝试从 Java 代码动态创建 ButtonWithCaption? @Code-Apprentice 第 54 行:mImageView.setImageResource(mIconSrc); 是的。我忘了粘贴activity_main.xml 文件。刚刚添加。 【参考方案1】:

您获得了NullPointerException,因为您没有夸大来自ButtonWithCaption.xml 的视图。解决此问题的一种方法是使用静态 View.inflate() 方法:

 private void init(Context context, AttributeSet attrs, int defStyleAttr) 
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ButtonWithCaption, 0, 0);

    try 
        mIconSrc = a.getResourceId(R.styleable.ButtonWithCaption_iconSrc, 0);
        mCaptionText = a.getString(R.styleable.ButtonWithCaption_captionText);
        mShowIcon = a.getBoolean(R.styleable.ButtonWithCaption_showIcon, mIconSrc != 0);
        mShowText = a.getBoolean(R.styleable.ButtonWithCaption_showText, !mCaptionText.isEmpty());

        View v = View.inflate(context, R.layout.ButtonWithCaption, this);   // add this line
        mImageView = (ImageView) findViewById(R.id.bwc_icon);
        mTextView = (TextView) findViewById(R.id.bwc_caption);

        mImageView.setImageResource(mIconSrc);
        mTextView.setText(mCaptionText);
     finally 
        a.recycle();
    

【讨论】:

好的,漂亮。我以为我以前试过? Idk,但这有点奇怪,我想,如果这个类已经被调用,那是不是意味着我已经在 xml 中声明了它?为什么我们需要再次充气呢?虽然没有意义。 (我认为,通货膨胀应该自动发生,因为我已经在 xml 中声明了它吗?) @MosesAprico 不,XML 布局永远不会自动膨胀。甚至活动的布局也必须通过调用 setContentView() 来扩展。 好吧,你知道这种行为背后的原因吗?因为我觉得这很奇怪。因为,为什么我什至在我的 XML 上放了一些我不想被夸大的东西?顺便说一句,我是一名 C# 和 XAML 程序员,我放入 XAML(C# 的 Android 布局 XML 版本)中的元素会在我的代码隐藏中自动初始化。 @MosesAprico 当您调用 View.inflate() 时,XML 会膨胀。另一种方法是直接在 Java 中创建所有视图,这将需要大量代码。就个人而言,我认为使用 XML 更简单、更干净。 我的意思是,当有人在 XML 中声明一个视图时,是否意味着“某人”希望它被夸大?我的意思是,为什么谷歌将其设计为不自动充气?有没有其他人想要的情况?【参考方案2】:

您必须将标签 &lt;RelativeLayout&gt; 替换为您的班级,如下所示:

<?xml version="1.0" encoding="utf-8"?>

<com.yourpackage.app.ButtonWithCaption xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    ...
    >
        ...
</com.yourpackage.app.ButtonWithCaption>

com.yourpackage.app 应该是 ButtonWithCaptionclass 所在的路径。

编辑:

A您发布的布局应该是您的活动布局,ButtonWithCaptionclass 应该是您的 xml 元素,而不是您的 xml 名称,因此请更改名称,将 RelativeLayout 替换为您的自定义类并尝试一下。

【讨论】:

我假设您指的是 ButtonWithCaption.xml 文件中的 RelativeLayout。这个建议是不正确的。 属于activity_main.xml 或任何其他使用 ButtonWithCaption 的布局文件。它不属于 ButtonWithCaption 布局文件本身。 他试图夸大一个没有在任何地方声明的视图。他创建了一个扩展RelativeLayout 的ButtonWithCaption 类,如果他不将该类用作任何布局中的标记,他将无法使用他修改的功能。如果我错了,请再解释一下,我没明白你的意思。 有两种选择:1. 在布局文件中使用&lt;id.foodmap.foodmapcustomviews.ButtonWithCaption&gt;,例如activity_main.xml 或2. 在Java 代码中以编程方式创建ButtonWithCaption 实例。在这两种情况下,您都应该在ButtonWithCaption.xml 中使用&lt;id.foodmap.foodmapcustomviews.ButtonWithCaption&gt;。当ButtionWithCaption 类尝试膨胀布局时,这将导致无限递归。 "正如 Code-Apprentice 所说,您发布的布局应该是您的活动布局" 这不是我说的。发布的布局与ButtonWithCaption.xml 一样好。我已经发布了一个答案,解释了真正的问题是什么以及如何解决它。 现在我明白了。但是,现在我问是因为我只想知道,您在 RelativeLayout 中有一个 RelativeLayout,这需要吗?我的意思是,您的自定义布局扩展了RelativeLayout,并在您的xml 中声明了另一个Relative。

以上是关于CustomView 无法实例化(编辑器预览错误,编译后没有显示)的主要内容,如果未能解决你的问题,请参考以下文章

无法解决:customview-1.1.0

Android错误,无法实例化服务...接口无法实例化

Unity 自定义编辑器 Raycast 在实例化对象后无法正常工作

带有 CustomView 和边框的 UIBarButton

无法实例化邮件功能。为啥会出现这个错误

无法实例化活动组件信息错误