Reputation: 2121
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
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
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