0

This question seems to have been asked a lot, but I couldn't find an answer. I welcome any links to a duplicate that solves my problem.

I have two views, one custom and one defined in xml as

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:id="@+id/button_bar_id" >

    <Button
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"
        android:id="@+id/button_undo"></Button>
</LinearLayout>

In my main activity, I get a reference to the two views, and create a FrameLayout to put them in:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    canvasView = new CustomCanvasView(this);

    buttonBar = (LinearLayout) findViewById(R.id.button_bar_id);
    frameLayout = new FrameLayout(this);
    frameLayout.addView(canvasView);
    frameLayout.addView(buttonBar);
    setContentView(frameLayout);
}

and that's it! Everything works if I'm just using the FrameLayout and my custom view, but if I try to add the predefined view buttonBar then I get a nullPointerException from the line where I initialize it.

I looked at this question where the solution seemed to be to call setContentView(frameLayout) first, but that doesn't work for me, and in fact makes the working case (where I don't add buttonBar) crash paradoxically.

Any notion as to what is going on here?

Thanks!

UPDATE

So, as some have suggested I tried modifying my xml file so that it includes a reference to my custom view:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:id="@+id/button_bar_id" >

<com.collin.customcanvasengine.CustomCanvasView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/customCanvasView">
</com.collin.customcanvasengine.CustomCanvasView>
<Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:id="@+id/button_undo"></Button>

</LinearLayout>

as well as modifying my main activity so that it uses this xml file as its content view:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.button_bar);

    canvasView = (CustomCanvasView) findViewById(R.id.customCanvasView);
    canvasView.setDimensions(10,10);
    canvasView.requestFocus();
}

Now the error I am getting is that my custom view cannot be inflated:

android.view.InflateException: Binary XML file line #8: Error inflating class com.collin.customcanvasengine.CustomCanvasView

Any ideas? Thanks.

UPDATE

Here is the logcat output:

06-06 01:55:30.010: E/Trace(19901): error opening trace file: Permission denied (13)
06-06 01:55:30.010: D/ActivityThread(19901): setTargetHeapUtilization:0.25
06-06 01:55:30.010: D/ActivityThread(19901): setTargetHeapIdealFree:8388608
06-06 01:55:30.010: D/ActivityThread(19901): setTargetHeapConcurrentStart:2097152
06-06 01:55:30.050: D/AndroidRuntime(19901): Shutting down VM
06-06 01:55:30.050: W/dalvikvm(19901): threadid=1: thread exiting with uncaught exception     (group=0x410b5438)
06-06 01:55:30.060: I/Process(19901): Sending signal. PID: 19901 SIG: 9
06-06 01:55:30.060: E/AndroidRuntime(19901): FATAL EXCEPTION: main
06-06 01:55:30.060: E/AndroidRuntime(19901): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.collin.customcanvasengine/com.collin.customcanvasengine.MainActivity}: android.view.InflateException: Binary XML file line #8: Error inflating class com.collin.customcanvasengine.CustomCanvasView
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2117)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2155)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread.access$700(ActivityThread.java:139)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.os.Handler.dispatchMessage(Handler.java:99)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.os.Looper.loop(Looper.java:137)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread.main(ActivityThread.java:5062)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at java.lang.reflect.Method.invokeNative(Native Method)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at java.lang.reflect.Method.invoke(Method.java:511)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at dalvik.system.NativeStart.main(Native Method)
06-06 01:55:30.060: E/AndroidRuntime(19901): Caused by: android.view.InflateException: Binary XML file line #8: Error inflating class com.collin.customcanvasengine.CustomCanvasView
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.createView(LayoutInflater.java:596)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:256)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.Activity.setContentView(Activity.java:1893)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at com.collin.customcanvasengine.MainActivity.onCreate(MainActivity.java:23)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.Activity.performCreate(Activity.java:5058)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2081)
06-06 01:55:30.060: E/AndroidRuntime(19901):    ... 11 more
06-06 01:55:30.060: E/AndroidRuntime(19901): Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
06-06 01:55:30.060: E/AndroidRuntime(19901):    at java.lang.Class.getConstructorOrMethod(Class.java:460)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at java.lang.Class.getConstructor(Class.java:431)
06-06 01:55:30.060: E/AndroidRuntime(19901):    at android.view.LayoutInflater.createView(LayoutInflater.java:561)
06-06 01:55:30.060: E/AndroidRuntime(19901):    ... 22 more

Also, the constructor for CustomCanvasView is of the form

public CustomCanvasView(Context context)

FINAL UPDATE

So Christopher Perry's answer worked best for me. As suggested, it is necessary to have a constructor of the form public CustomCanvasView(Context context, AttributeSet attr) and, importantly, to pass the attribute set to the parent in the call to super within this constructor. At the moment I have not used the attribute set in the constructor but I may decide to implement custom attributes in the future. It will work either way.

Thanks again to everyone, and thanks Christopher Perry for being quite helpful.

Community
  • 1
  • 1
Rookatu
  • 1,487
  • 3
  • 21
  • 50
  • 5
    you will need to call `setContentView(R.layout.your_xml_layout_name)` before getting views from current window – ρяσѕρєя K Jun 06 '13 at 05:05
  • Move your setContentView below super.onCreate(savedInstanceState); and then run it. If you get a error, post that error in your question. – the-ginger-geek Jun 06 '13 at 05:10
  • I did try that. I got a NullPointerException originating from the call to setContentView. I am attempting to implement some of these other suggestions. Thanks! – Rookatu Jun 06 '13 at 05:19

5 Answers5

2

try it as using LayoutInflater.inflate :

canvasView = new CustomCanvasView(this);
LayoutInflater inflater= (LayoutInflater)this.getSystemService(
                                               Context.LAYOUT_INFLATER_SERVICE);
View buttonBar=inflater.inflate(R.layout.your_layout_name,null);
frameLayout = new FrameLayout(this);
frameLayout.addView(canvasView);
frameLayout.addView(buttonBar);
setContentView(frameLayout);
ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
1

You are asking the Activity to find your view before you even put it in the view hierarchy of the Activity. You need to call setContentView with your layout resource id before you can find views inside of the Activity.

What you should do is change your xml to something like this (i.e. your xml file is called some_layout.xml in this example, since I don't know your file name):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"
        android:id="@+id/button_undo" />

    <com.mypackage.CustomCanvasView
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />

</LinearLayout>

Then your onCreate is simply:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.some_layout);
    }

Since you are putting a custom View in the Activity view hierarchy, just declare it in the layout xml file. There's no reason to clutter up your Activity code with things Android already does for you.

EDIT:

Make sure you define a constructor in your CustomCanvasView class that takes an AttributeSet or you'll get an InflateException. Like so:

public CustomCanvasView(Context context, AttributeSet attributes) {
  super(context, attributes);
  // code goes here
}
Christopher Perry
  • 38,891
  • 43
  • 145
  • 187
  • this would work if his buttonBar is inside canvasView. if it was, then there was no need for him to add both to framelayout. hope my understanding is correct. – SKK Jun 06 '13 at 05:07
  • @Santhosh Yeah, I saw that after I posted. I edited my answer. – Christopher Perry Jun 06 '13 at 05:08
  • I don't quite understand what you are suggesting. Are you saying I should permute the order of some lines? Thanks. Also, buttonBar is not inside canvasView. One is an xml file and the other id defined in a class extending View. – Rookatu Jun 06 '13 at 05:09
  • @Rookatu `setContentView` is what builds the View hierarchy of your Activity. Calling `findViewById` on it before you actually put something in it will of course give you nothing. See my edit. – Christopher Perry Jun 06 '13 at 05:14
  • One problem is that I need to create my CustomCanvasView programmatically because I need to initialize some values in the constructor, such as the number of rows and the number of columns, etc. EDIT: Actually, I think I can just access it if I add an id. I will try that – Rookatu Jun 06 '13 at 05:25
  • @Rookatu, I suggest rethinking the class design then. Use [custom attributes](http://developer.android.com/training/custom-views/create-view.html#customattr) – Christopher Perry Jun 06 '13 at 05:27
  • I tried to declare my view in the xml. Now the logcat says it has an error infating my view: android.view.InflateException: Binary xml file line #8 Error inflating class com.collin.customcanvasengine.CustomCanvasView – Rookatu Jun 06 '13 at 05:38
  • Can you post the entire stack trace. Most likely you don't have the right constructor defined in your custom View. You need to at least define the one that takes a Context and an AttributeSet, since you are passing in attributes. – Christopher Perry Jun 06 '13 at 05:42
  • I've posted the logcat and described the constructor. – Rookatu Jun 06 '13 at 06:03
  • It's as I suspected. You need the constructor that takes Context and an AttributeSet defined in CustomCanvasView. – Christopher Perry Jun 06 '13 at 06:06
  • I updated my constructor. It takes in the AttributeSet but does nothing with it. I can create the activity with the view, but as soon as I try to refer to customCanvasView after finding its view by id, I get a null pointer exception. So, for instance, I cannot initialize the number of rows or columns and just get a black screen with no gridlines. Any ideas? Thanks btw – Rookatu Jun 06 '13 at 06:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31307/discussion-between-christopher-perry-and-rookatu) – Christopher Perry Jun 06 '13 at 06:20
1

You need to set the content of your layout to the activity first

  setContentView(R.layout.mylayout);

Then you can findviewbyid (initialize views);

   buttonBar = (LinearLayout) findViewById(R.id.button_bar_id); 

You can findViewById of the current view hierarchy set to the activity. If view is not initialized you get NullPointerException

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
0

Use buttonBar = (LinearLayout)canvasView. findViewById(R.id.button_bar_id);

Arun C
  • 9,035
  • 2
  • 28
  • 42
0

You will have to inflate your layout first and then add it into your frameLayout

CRUSADER
  • 5,486
  • 3
  • 28
  • 64