Moses Aprico
Moses Aprico

Reputation: 2121

CustomView couldn't be instantiated (error in editor preview and nothing shown after compile)

So, I created a CustomView called ButtonWithCaption.xml,

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    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_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="2dp"
    android:scaleType="centerCrop" />

<TextView
    android:id="@+id/bwc_caption"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/bwc_icon"
    android:textAlignment="center" />

Along with its controller 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();
        }
    }
}

Nothing fancy.

And then, I declare custom attributes in 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>

And this is my 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_width="match_parent"
    android:layout_height="match_parent"
    tools:context="id.foodmap.foodmapcustomviews.MainActivity">

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

...but weirdly, Android Studio's preview gave me render error :

Failed to instantiate one or more classes

with stacktrace :

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

My findViewById() from ButtonWithCaption.java to find TextView and ImageView also returns null.

Anyone know what caused this? Because it doesn't even show up in the editor preview window.

P.S. If I use , The preview show the element correctly. P.S.S. It compiled to my phone with no error, but no ButtonWithCaption shown.

Thanks in advance

Upvotes: 0

Views: 548

Answers (2)

Jonathan Aste
Jonathan Aste

Reputation: 1774

You have to replace the tag <RelativeLayout> with your class, something like this:

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

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

com.yourpackage.app should be the path where your ButtonWithCaptionclass is.

EDIT:

AYour posted layout sould be your activity layout, the ButtonWithCaptionclass should be your xml element, not your xml name, so change the name, replace the RelativeLayout with your custom class and try it.

Upvotes: -1

Code-Apprentice
Code-Apprentice

Reputation: 83517

You are getting a NullPointerException because you have not inflated the views from ButtonWithCaption.xml. One way to fix this is with the static View.inflate() method:

 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();
    }
}

Upvotes: 2

Related Questions