0

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?

1
  • can you post the whole error log Commented Jan 13, 2012 at 9:07

2 Answers 2

7

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>
Sign up to request clarification or add additional context in comments.

4 Comments

What do you mean with the last one, because I haven't done it. Can you edit and give an example? I have made the changes toy sai but I am getting an error, "no resource identifier found for the image" and I suppose is what you said.
Thanks! But i am still getting the exact same error. Question: in my LoaderImageView element in xml, the app: goes only infront of image or in front of every style attribute?
in front of every style attribute. and also... add every style attribute in the resource xml file
Ok,I will try it. can you suggest the format for the scaleType attribute?
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.