android.view.InflateException: Binary XML file line #15: Error inflating class

19.1k views Asked by At

i have implemented an new ImageLoaderView that extends view, with a code that works if i set the xml programatically. But as this seems to be difficult I am trying to make it work for an xml in my res layout folder, but i am getting an error. Below are my codes.

LoaderImageView:

package com.anon.android.test;

import java.io.IOException;
import java.net.MalformedURLException;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

/**
 * Free for anyone to use, just say thanks and share :-)
 * @author Blundell
 *
 */
public class LoaderImageView extends LinearLayout{

    private static final int COMPLETE = 0;
    private static final int FAILED = 1;

    private Context mContext;
    private Drawable mDrawable;
    private ProgressBar mSpinner;
    private ImageView mImage;

    /**
     * This is used when creating the view in XML
     * To have an image load in XML use the tag 'image="http://developer.android.com/images/dialog_buttons.png"'
     * Replacing the url with your desired image
     * Once you have instantiated the XML view you can call
     * setImageDrawable(url) to change the image
     * @param context
     * @param attrSet
     */
    public LoaderImageView(final Context context, final AttributeSet attrSet) {
        super(context, attrSet);
        final String url = attrSet.getAttributeValue(null, "image");
        if(url != null){
            instantiate(context, url);
        } else {
            instantiate(context, null);
        }
    }

    /**
     * This is used when creating the view programatically
     * Once you have instantiated the view you can call
     * setImageDrawable(url) to change the image
     * @param context the Activity context
     * @param imageUrl the Image URL you wish to load
     */
    public LoaderImageView(final Context context, final String imageUrl) {
        super(context);
        instantiate(context, imageUrl);     
    }

    /**
     *  First time loading of the LoaderImageView
     *  Sets up the LayoutParams of the view, you can change these to
     *  get the required effects you want
     */
    private void instantiate(final Context context, final String imageUrl) {
        mContext = context;

        mImage = new ImageView(mContext);
        mImage.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

        mSpinner = new ProgressBar(mContext);
        mSpinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

        mSpinner.setIndeterminate(true);

        addView(mSpinner);
        addView(mImage);

        if(imageUrl != null){
            setImageDrawable(imageUrl);
        }
    }

    /**
     * Set's the view's drawable, this uses the internet to retrieve the image
     * don't forget to add the correct permissions to your manifest
     * @param imageUrl the url of the image you wish to load
     */
    public void setImageDrawable(final String imageUrl) {
        mDrawable = null;
        mSpinner.setVisibility(View.VISIBLE);
        mImage.setVisibility(View.GONE);
        new Thread(){
            public void run() {
                try {
                    mDrawable = getDrawableFromUrl(imageUrl);
                    imageLoadedHandler.sendEmptyMessage(COMPLETE);
                } catch (MalformedURLException e) {
                    imageLoadedHandler.sendEmptyMessage(FAILED);
                } catch (IOException e) {
                    imageLoadedHandler.sendEmptyMessage(FAILED);
                }
            };
        }.start();
    }

    /**
     * Callback that is received once the image has been downloaded
     */
    private final Handler imageLoadedHandler = new Handler(new Callback() {
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
            case COMPLETE:
                mImage.setImageDrawable(mDrawable);
                mImage.setVisibility(View.VISIBLE);
                mSpinner.setVisibility(View.GONE);
                break;
            case FAILED:
            default:
                // Could change image here to a 'failed' image
                // otherwise will just keep on spinning
                break;
            }
            return true;
        }       
    });

    /**
     * Pass in an image url to get a drawable object
     * @return a drawable object
     * @throws IOException
     * @throws MalformedURLException
     */
    private static Drawable getDrawableFromUrl(final String url) throws IOException, MalformedURLException {
        return Drawable.createFromStream(((java.io.InputStream)new java.net.URL(url).getContent()), "name");
    }

}

This is my xml:

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

    <Button
        android:id="@+id/button_weather9"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Back" />

    <com.anon.android.test.LoaderImageview
        android:id="@+id/weather_icon"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center_horizontal"
        android:scaleType="fitXY"
        image="http://developer.android.com/images/dialog_buttons.png"
        />

</LinearLayout> 

and this is how I call it: (in my ImageLoading Activity )

LoaderImageView image = (LoaderImageView) findViewById(R.id.weather_icon);

But I am getting this error:

01-13 09:16:45.516: E/AndroidRuntime(216): Uncaught handler: thread main exiting due to uncaught exception
01-13 09:16:45.526: E/AndroidRuntime(216): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.anon.android.test/com.anon.android.test.ImageLoading}: android.view.InflateException: Binary XML file line #15: Error inflating class com.anon.android.test.LoaderImageview
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2496)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread.access$2200(ActivityThread.java:119)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.os.Handler.dispatchMessage(Handler.java:99)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.os.Looper.loop(Looper.java:123)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread.main(ActivityThread.java:4363)
01-13 09:16:45.526: E/AndroidRuntime(216):  at java.lang.reflect.Method.invokeNative(Native Method)
01-13 09:16:45.526: E/AndroidRuntime(216):  at java.lang.reflect.Method.invoke(Method.java:521)
01-13 09:16:45.526: E/AndroidRuntime(216):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
01-13 09:16:45.526: E/AndroidRuntime(216):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
01-13 09:16:45.526: E/AndroidRuntime(216):  at dalvik.system.NativeStart.main(Native Method)
01-13 09:16:45.526: E/AndroidRuntime(216): Caused by: android.view.InflateException: Binary XML file line #15: Error inflating class com.anon.android.test.LoaderImageview
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:576)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
01-13 09:16:45.526: E/AndroidRuntime(216):  at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:198)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.Activity.setContentView(Activity.java:1622)
01-13 09:16:45.526: E/AndroidRuntime(216):  at com.anon.android.test.ImageLoading.onCreate(ImageLoading.java:22)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
01-13 09:16:45.526: E/AndroidRuntime(216):  ... 11 more
01-13 09:16:45.526: E/AndroidRuntime(216): Caused by: java.lang.ClassNotFoundException: com.anon.android.test.LoaderImageview in loader dalvik.system.PathClassLoader@44e8cad8
01-13 09:16:45.526: E/AndroidRuntime(216):  at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:243)
01-13 09:16:45.526: E/AndroidRuntime(216):  at java.lang.ClassLoader.loadClass(ClassLoader.java:573)
01-13 09:16:45.526: E/AndroidRuntime(216):  at java.lang.ClassLoader.loadClass(ClassLoader.java:532)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.createView(LayoutInflater.java:466)
01-13 09:16:45.526: E/AndroidRuntime(216):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:565)
01-13 09:16:45.526: E/AndroidRuntime(216):  ... 20 more

I think that what gets me the error is the way I declare my LoaderImageView in my xml. Can you help me?

2

There are 2 answers

4
Ovidiu Latcu On

You need to add the package of you app inside the root element of your layout like this :

<LinearLayout 
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res/com.anon.android.test"
   .../>

and then when you inflate your custom view pass the custom attributes like this :

 <com.anon.android.test.LoaderImageview
      ...
      app:image="http://developer....png"/>

Also I suppose that you have declared your styleable attributes inside your resources.

EDIT:

To create a styleable attribute for your View, create an attrs.xml file inside the res\values folder of your project and the content should be as follows :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LoaderImageView">
        <attr name="image" format="string"/>
        //other attributes..
    </declare-styleable>
</resources>
0
Rudolf Real On

So, related to this question but not the same case: I was running into the same issue and the adb logcat output wasn't helping that much.

After reviewing my code I realize I was writing in the layout 'view' insted of 'View'. Apparently if the element that Android si trying to inflate doesn't exists or has an invalid name, it'll crash without a very useful error output.