How to add attributes to your custom View

Jack just asked the following questions on the tutorial for animated GIFs I once wrote:

“I want to be able to place the GifMovieView in an existing layout (like any regular control).
Please give me a step-by-step of how to bind xml layout attributes to this custom view.”

A good question. Here’s the answer.

1 Use your own View in an XML

To place your own View in an layout XML file, just use it like a normal view, only that you have to append the whole package name up front. Something like:

< eu.andlabs.tutorial.animatedgifs.views.GifMovieView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/my_id"
/>

In this XML, you can already use the standard Android attributes.

2 Define your custom attributes

If you want to define your own, custom attributes, go to the res/values-folder and create a file called attrs.xml.

You can define your custom attributes here. In the end, it should look like this:

< ?xml version="1.0" encoding="utf-8"?>
< resources>
< declare-styleable name = "eu.andlabs.tutorial.animatedgifs.view.GifMovieView">
< attr name="url" format="string" />
< attr name="fetchAutomatically" format="boolean" />
< /declare-styleable>
< /resources>

You can now address these attributes in your XML layout. To do so, add something like

xmlns:gif="http://schemas.android.com/apk/res-auto"

To your root layout element. Please notice that this only works with ADT 17 and older. Now you can access your attributes in your layout like this:

< eu.andlabs.tutorial.animatedgifs.views.GifMovieView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/my_id"
gif:url="http://mygif.com/mygif.gif"
gif:fetchAutomatically="true" 
/>

3 Handle the attributes in your View

Since the XML is done, it’s time to address the Java part, which means the custom View. You should at least overwrite the constructor that is receiving a Context and an Attributes-object. Like so:

public GifMovieView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(attrs);
}

You can now access your custom attributes in using the AttributeSet:

private void init(final AttributeSet attrs) {
if (attrs != null) {
String packageName = "http://schemas.android.com/apk/res-auto";
mUrl = attrs.getAttributeValue(packageName, "url");
mFetchAutomatically = attrs.getAttributeBooleanValue(packageName, "fetchAutomatically", false);
}
}

Do whatever is needed with it. That’s already it.

 

I hope you enjoyed this post. Please feel free to ask anything you want in the comments.

And remember: Sharing is caring!

12 Comments

  1. Thank you very much for your prompt response Johannes! I looked over this code and it begins to make sense to me however:

    1. in the layout XML you use eu.andlabs.tutorial.animatedgifs.views.GifMovieView
    while in the attrs.xml you use eu.andlabs.tutorial.animatetgifs.view I would have expected both
    to be like the former. please explain.

    2. the line xmlns:gif=”http://schemas.android.com/apk/res-auto” is added in addition to xmlns:android=”http://schemas.android.com/apk/res/android” – correct?

    3. one piece of info I’m missing: the activity. When and how is GifMovieView’s constructor called. I tried to instantiate
    using GifMovieView mV = (GifMovieView)findViewById(R.id.gifMovieView1); mV comes up null

    4. In the Eclipse IDE, in my layout XML, in the Graphical Layout, I see my GifMovieView in the ‘Custom and Library View’ palette: When I drag the control onto the layout (onto a Relative Layout):
    :: the xml code HAS BEEN updated
    :: no graphical control appears
    :: no control properties appear
    :: no entry appears in the Outline window e.g. RelativeLayout1
    Ab textView1 – “Large Text”
    SHOULD BE HERE

    Please advise

    Thank you for your patience.
    Jack

    • Hello Jack,

      1. You are totally right, I corrected that.
      2. Correct. You define a different namespace for accessing your XML-attributes. Instead of xmlns:android=… you could also write xmlns:a=”… to save some keystrokes when defining your layouts (but it’s non-best practice).
      3. It’s called once the View gets inflated by setContentView(); When calling findViewById() after that, it should be non-null.
      4. I’m not quite familiar with the WYSIWYG functionality of the editor (except with dragging and dropping) since when I started developing for Android, this was basically not there. Unfortunately I don’t know what to do to make it the controls/properties appear. It would be great if you could let me know the solution once you found it.

      Best regards
      Johannes

  2. Once again Johannes thank so much.
    Yes indeed, I called findViewById before setContentView. Seems to work now though there is still the IDE mystery – I’ll let you know when I have a solution,
    Best regards,
    Jack

    PS Quite a few years ago I trained under Chiba Sensei in San Diego for 5 years.

  3. Hey,
    ich hoffe es macht nichts, dass ich den Post in deutsch schreibe, da mein Englisch nicht gut genug ist um das ganze in Englisch wiederzugeben.
    Zu Beginn: Bin ein Android Neuling, also bitte ich um etwas Verständnis ;)
    Ich habe das Tutorial soweit gemacht mit den animierten Gifs. Hat auch Super geklappt. Nun wollte ich hierdas mit dem Layout probieren um verschiedene Gifs an verschieden stellen im Layout zu platzieren. Verstehe soweit auch alles. Nur habe ich ein Problem: Der Teil mit: >>>mUrl = attrs.getAttributeValue(packageName, “url”);
    mFetchAutomatically = attrs.getAttributeBooleanValue(packageName, “fetchAutomatically”, false);<<< Da meckert Eclipse, dass ich es nicht als Variable angegeben habe. Egal ob ich es nun als Parameter oder als lokale Variable deklariere funktioniert das Programm nicht und stürzt beim start ab. Könntest du mir sagen wo ich den Denkfehler habe oder vielleicht einfach mal als Beispiel den Code von der geänderten GifMovieView und der Main schicken oder bereitstellen?
    Wäre echt nett =)
    Vielen Dank im Voraus,
    Jonas

    • Hi Jonas,
      vielen Dank für Deinen Kommentar und die Mail.
      Variablen, die mit einem extra ‘m’ anfangen, sind in der Regel Membervariablen. Wenn Du sie also als fields erstellst, sollte es gehen. Da Deine die App aber abstürzt, liegt es aber wahrscheinlich an etwas Anderem. Vielleicht kannst Du ja mal den Stacktrace posten?

      Beste Grüße
      Johannes

      P.S.: Immer, wenn ein User zum ersten Mal einen Kommentar erstellt, muss dieser zuerst von mir bestätigt werden, darum hat Du Deinen zunächst nicht gesehen.

  4. Thanks a lot, but how to address that with ADB > 17 ???

  5. applied according to discussion but still getting errors

    attrs.xml (in library project)

    actual class (in library project)

    public CircleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    String namespace = “http://schemas.android.com/apk/res-auto”;
    radius = attrs.getAttributeFloatValue(namespace, “radius”, 4);
    }

    main.xml (another project that uses the library)

    getting the following error:
    android.view.InflateException: Binary XML file line #35: Error inflating class
    while reaching the line
    “radius = attrs.getAttributeFloatValue(namespace, “radius”, 4)”

  6. Hi – great tutorial – helped me out a lot – think I spotted a typo in the init it should be:
    http://schemas.android.com/apk/res-auto
    rather than:
    http://schemas.android.com/apk/res/res-auto

    Cheers,
    David

  7. Um anderen Leuten ein paar Probleme zu ersparen die mich gerade eine Stunde aufgehalten haben:
    1. Ihr bastelt euch den gifMovieViewer in eure xml und modifiziert die Javaklasse so wie Johannes es oben beschrieben hat
    2. erstellt in der Klasse noch diese Variablen global:
    private boolean mFetchAutomatically;
    private String mUrl;
    3. vergesst nicht die
    void playGif(InputStream stream)
    auf public zu stellen!
    4. Nun braucht ihr nur noch in eurer MainActivity den Stream aus Tuturial2 mittels :
    movieView1.playGif(stream);
    abspielen zu lassen!

    Aber genial gelöst Johannes, selbst meine gif mit 50fps läuft wunderbar flüssig!

  8. Hello, I hope you can help me:
    I seem to be missing something with regards to the attributes of the custom view.

    Say my view contains an ImageView and a TextView,

    How do I define it so that the src is passed to the ImageView, and text to the TextView? I used watches and breakpoints, but it didn’t seem like any attributes were being passed at all. What am I missing?

    • Hi Andre,

      thank you for your question. Can you share some code?
      Also, please be aware that the code in this post is not state of the art anymore. For more infos on passing attributes to custom views, please click here.
      Best regards,
      Johannes

Leave a Reply

Your email address will not be published.

*

© 2024 Droid-Blog

Theme by Anders NorenUp ↑