Custom header with parallax effect in ListView

We will see how to set a custom header to our ListView and apply a parallax effect to the header image.

You can download the example code and the apk.

Parallax effect

Here is the custom header I use:

Custom header

You can check the layout files:

The main layout is a ListView, and we define the element layout in its own xml.

Each element of the ListView will represent a Model. For the example I use a basic model that has a name and a description.

public class Model {
    private String name;
    private String description;

    public Model(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

We also have to define an adapter for that model.

public class ModelAdapter extends SlothArrayAdapter<Model> {

    @InjectView(R.id.name)          TextView name;
    @InjectView(R.id.description)   TextView description;

    public ModelAdapter(Context mContext, int layoutResourceId, List<Model> data) {
        super(mContext, layoutResourceId, data);
    }

    @Override
    protected void onCreateViewForPosition(View viewCreated, int position, Model data) {
        name.setText(data.getName());
        description.setText(data.getDescription());
    }
}

I’m using a custom array adapter from slothframework. That way I can use view injection and two custom methods onCreateViewForPosition and onCreatingLastView.
The view injection it’s not done unnecessarily, I’ve tried to implement some kind of ViewHolder pattern[performance tips for android’s ListViews].

Of course you can have your own adapter.

Now we will analyze the code of the MainActivity. The first thing we do is initialize the adapter and set it to the view.

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

    initAdapter();
    ...
}

private void initAdapter() {
    // instantiate the adapter and attach it to the listview
    adapter = new ModelAdapter(this, R.layout.element_list_view, modelList);
    listView.setAdapter(adapter);
}

We inflate and set the custom header:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    inflateHeader();
    ...
}
...
private void inflateHeader() {
    // inflate custom header and attach it to the list
    LayoutInflater inflater = getLayoutInflater();
    ViewGroup header = (ViewGroup)inflater.inflate(R.layout.custom_header, listView, false);
    listView.addHeaderView(header, null, false);

    // we take the background image and button reference from the header
    backgroundImage = (ImageView) header.findViewById(R.id.customHeaderBackground);
    postButton  = (Button) header.findViewById(R.id.postsButton);
}

Parallax effect

There are other ways to get the parallax effect, but here we will see a very simple one. By now this parallax effect is for api level 11 and above (Android 3.0).

Our ListView must have its custom ScrollListener, so we add it in the onCreate method of our MainActivity. We only add it to devices with api level 11 and above for compatibility.

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

private void addScrollListenerForSDKsAbove11() {
    if (Integer.valueOf(Build.VERSION.SDK_INT)>11) {
        listView.setOnScrollListener(this);
    }
}

We must override the onScroll method and set the top of the image to its middle value:

// override the OnScrollListener methods: onScrollStateChanged & onScroll
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {}

@Override
public void onScroll(AbsListView absListView, int i, int i2, int i3) {
    parallaxImage(backgroundImage);
}

private void parallaxImage(View view) {
    Rect rect = new Rect();
    view.getLocalVisibleRect(rect);
    if (lastTopValueAssigned != rect.top){
        lastTopValueAssigned = rect.top;
        view.setY((float) (rect.top/2.0));
    }
}

The effect is done in the parallaxImage method, when we call view.setY. Here we are displacing the view down half the size that the view goes up. For each 2 pixels that the view goes up the screen we displace the view 1 pixel down.

To avoid calling view.setY() unnecessarily we had defined a field to check if the value has changed (lastTopValueAssigned)

Here are the code references again:

Background image

Advertisements

Implementing view injection, SlothFramework

So… I tried to make a Framework, I call it SlothFramework. But I consider it more of an experiment.

While I was developing my last app I decided that I wanted to get rid of as much boilerplate code as I could. So I started a package where I was putting all that code. With time that package become a library and now I’m sharing it with you.

It’s in a clearly alpha (pre-alpha?) state so don’t be too hard on me, but I will be pleased to hear suggestions and (constructive) critics.

For now I present you two of it’s main features, but I will only analyse the view injection:

  • Inject views
  • Save the state of a field to keep its value when you rotate your device.

Implementing view injection

The view injection it’s done through a custom annotation @InjectView, that has for parameter the id of the view. It’s done in a similar way to roboguice.

View injection example:

    @InjectView(R.id.breadCrum)   ListView breadCrumList;
    @InjectView(R.id.list_layout) ImageButton changeToList;
    @InjectView(R.id.context_bar) RelativeLayout contextBar;
    @InjectView(R.id.edit)        ImageButton edit;
    @InjectView(R.id.searchText)  EditText searchText;

It’s quite few code the necessary to implement view injection. Basically there are 3 parts:

  • Annotation: To mark the fields to process and pass the view id as parameter.
  • Base classes: To process the annotations in the activities and the fragments.
  • View injector: the one who injects the view. Implementing an injector helps to avoid code replication on base classes.

Annotation

First we need to define the annotation:

package es.slothdevelopers.slothframework.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    int value() default Integer.MIN_VALUE;
}

Base classes

In order to process this annotations and inject the views our classes must extend an Sloth Class (depending if it’s an activity or a fragment).

Here you can check the available classes: available classes.

The view injection is made in different points depending of the base class. This is because the views are consolidated in different forms depending if it’s an activity or a fragment.

Activities

Here we have the code related to view injection for an Activity (SlothActivity)class:

public class SlothActivity extends Activity {
    ViewInjector viewInjector = new ViewInjector(this);

    public SlothActivity(){
        parseFields();
    }

    // HERE WE PROCESS THE ANNOTATION
    private void parseFields() {
        for (Field field : this.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            viewInjector.checkIfViewToInject(field);
        }
    }
...
    // AND WE OVERRIDE SETCONTENTVIEW TO INJECT THE VIEWS
    @Override
    public void setContentView(int layoutResID) {
        super.setContentView(layoutResID);
        viewInjector.injectViewList(this);
    }
}

In the constructor we parse fields to check the annotations, saving the fields we would inject later.

Then we override the setContentView method (in the Sloth class) to inject the views at this point. It’s made this way because is in this method where we have our view for the first time in the activity.

Fragments

For the fragments (bases on SlothFragment) we inject the views in the onViewCreated method:

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    viewInjector.injectViewList(view);
}

In resume: depending the class you are extending you would have access to the injected views on different points.

  • If you are extending an Activity class you could use the injected views after calling the setContentView method.
  • If you are extending a Fragment class you could use the injected views after calling the super method on onViewCreated.

View Injector

And here we have the code for the injector:

public class ViewInjector {
    List<Field> viewsToInject = new LinkedList<Field>();
    SlothLog log;
    Object caller;

    public ViewInjector(Object caller){
        this.log = new SlothLog(caller);
        this.caller = caller;
    }

    public void checkIfViewToInject(Field field) {
        if (field.isAnnotationPresent(InjectView.class)){
            viewsToInject.add(field);
        }
    }

    public void injectViewList(Activity activity) {
        for (Field field: viewsToInject){
            injectView(activity, field);
        }
    }

    private void injectView(Activity activity, Field field) {
        int id = field.getAnnotation(InjectView.class).value();
        if (id == Integer.MIN_VALUE){
            log.debug(errorObtainingViewIdToInject(field));
        }
        try {
            field.set(caller, activity.findViewById(id));
        } catch (IllegalAccessException e) {
            log.error(errorInjectingView(field, activity.getResources()));
        } catch (IllegalArgumentException ex){
            log.error(errorInjectingView(field, activity.getResources()));
        }
    }
}

As we can see it basically save the fields to be injected in a List and inject the views on demand. The view injection is done in this line:

field.set(caller, activity.findViewById(id));

Notifying errors

I tried to advise some typical errors injecting view. One of them is try to inject a different type that the one you declared.

In this example I’m trying to inject a ListView in a FrameLayout.

***********************************************************************
Error injecting view in: MainActivity.
It's possible that the type you are trying to inject differs from the one declared in the layout.
Check code.
@InjectView(R.id.list)
FrameLayout listView;
***********************************************************************

Saving the state of a field, @SaveState annotation

This annotation can be used in simple fields (primitive data types, POJOs, and list) to save the state of the field when we pause our activity and restore it when we resume it.

To use the annotation you must simply add the @SaveState annotation, no parameters needed.

Example:

    @SaveState    boolean waitingForRestartGame = false;
    @SaveState    int punctuation = 0;

This annotation can be used in Fragments and Activities.

I will post about this annotation with more time in future posts, but you can check the repository if you want to see how it’s done.

The process it’s similar to the inject view, it involves an annotation, the base classes and the save state injector.

The type support its not complete(I’ll try to make a list), there is still some work to do, and how you can see, it involves Reflection.

Example project

To show the framework in action I made a very simple project.

It consist of a button and a counter. Each time you press the button the counter grows.

Example application

The views are injected automatically. In this example we are injecting the Button and the TextView of the counter with the @InjectView annotation.

We are also saving the state of the counter with the @SaveState annotation. Without the annotation your counter will go down to 0 each time you rotate your device. You could write the code to restore the state, but the main point of the framework is not to do so.

Here is the resulting code of the main activity:

public class MainActivity extends SlothActionBarActivity implements View.OnClickListener {
    @SaveState
    int counter = 0;

    @InjectView(R.id.counter)       TextView counterCount;
    @InjectView(R.id.increaseCount) Button increaseCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        increaseCount.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        counter++;
        updateCounterCount();
    }

    @Override
    protected void onResume() {
        super.onResume();
        updateCounterCount();
    }

    private void updateCounterCount(){
        counterCount.setText(String.valueOf(counter));
    }
}

You can check the rest of the code in the repository:

That’s all by now. I’ll present more features in future posts.

Thanks for reading.

Sloth logo 3

Shadow effect with custom shapes

It’s easy to create a shadow for your views using custom shapes.

The idea it’s that you create the shadow layer as background first and the content layers on top of that.
But you also need to displace the layers in order to create the shadow effect.

For example:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <layer-list>
            <item android:right="4dp" android:bottom="4dp">

                <!-- SHADOW LAYER -->
            </item>
            <item android:left="4dp" android:top="4dp">
                <!-- CONTENT LAYER-->
            </item>
        </layer-list>
    </item>
</selector>

You can control the shadow direction and size through the top, bottom, right and left parameters.

Shadow shapes

Here we have an example of our app WhoSaid:

Custom shadow shape

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <layer-list>
            <item android:left="4dp" android:top="4dp">
                <shape>
                    <solid android:color="#ff58bb52" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
        </layer-list>
    </item>
    <item>
        <layer-list>
            <!-- SHADOW LAYER -->
            <item android:left="4dp" android:top="4dp">
                <shape>
                    <solid android:color="#66000000" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
            <!-- CONTENT LAYER -->
            <item android:bottom="4dp" android:right="4dp">
                <shape>
                    <solid android:color="#ff58bb52" />
                    <corners android:radius="30dip"/>
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

In our case we take off the shadow when the button is pressed to give improve the feedback.

Don’t forget that you can play with distance and colors to get new effects. For example you can use a gradient for the shadow layer.

Underlining views with custom shapes, overdraw performance

After the post of underlining views and custom drawables in android some people ask me about performance. So let’s talk about it.

So after a little research about performance I found this post http://www.curious-creature.org/2012/12/01/android-performance-case-study/ talking about performance and overdraw. I recommend read the post, but in resume we refer to overdraw as the number of times the GPU has to draw the screen for each component. You can active it in the Developer options as Show GPU overdraw.

It will color your screen this way:

Wordpress overdraw

  • No color means there is no overdraw. The pixel was painted only once. In this example, you can see that the background is intact.
  • Blue indicates an overdraw of 1x. The pixel was painted twice. Large blue areas are acceptable (if the entire window is blue, you can get rid of one layer.)
  • Green indicates an overdraw of 2x. The pixel was painted three times. Medium-sized green areas are acceptable but you should try to optimize them away.
  • Light red indicates an overdraw of 3x. The pixel was painted four times. Small light red areas are acceptable.
  • Dark red indicates an overdraw of 4x or more. The pixel was painted 5 times or more. This is wrong. Fix it.

In the other post we put this drawable:

Layers 2

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

And we can see it has a 2x overdraw (green color) because of the 2 layers:

2 x overdraw

But we can fix that by changing the drawable for not to overlap between the layers.

layers_no_overlap_1

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
            <item android:top="29dp">
                <shape >
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

Now we have a 1x (blue) overdraw:

1 Overdraw

Because now the layers do not overlap it doesn’t matter which layer goes on top of another.

The main drawback of this method is that you only can use if you know beforehand the height of the drawable.

If you don’t know the height you will have to use a 9patch button or keep it like we put in the previous post paying the overdraw cost.

In our case we would use the drawable method in development to be able to change the drawables fast, and change to 9patch button if needed.

Example with 3 layer

This drawable is compound of 3 layers (see this post if you want to see the xml).

3 Layers example

The overdraw of this view is 3x:

3xoverdraw

Changing the drawable we can reduce it to a 1x factor:

Layers no overlap 2

<item android:state_selected="true">
    <layer-list>
        <item android:top="29dp">
            <shape>
                <solid android:color="#222222" />
            </shape>

        </item>
        <item android:top="26dp" android:bottom="3dp">
            <shape >

                <solid android:color="#ffff7a00" />
            </shape>
        </item>
        <item android:bottom="6dp">
            <shape>
                <solid android:color="#222222" />
            </shape>
        </item>
    </layer-list>
</item>

With the new xml the overdraw remains 1x (blue).

New overdraw for 3 layers

Underlining views, custom drawables in android

Edit:
In this post we talk about the performance of this method.

Sometimes we want to underline a view (a TextView or a Button), just to show the user that it’s active, or any other reason.

To achieve this we use custom drawables. In this case we use a layer-list.

The first element of the layer list is the base, and every item we put it’s a layer that goes on top, let’s try with an image, and it corresponding xml:

Layers

And here is the drawable. We can see that the first element is the base layer:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <layer-list>
            <item>
                <!-- layer 1-->
            </item>
            <item>
                <!-- layer 2-->
            </item>
            <item>
                <!-- layer 3-->
            </item>
            <item>
                <!-- layer 4-->
            </item>
        </layer-list>
    </item>
</selector>

And now to achieve the effect we must know the bottom, top, right and left properties of item. They are similar to the margin property, but the are relative to the view.

So if we set the bottom of the second layer to 3dp (for example) we will be able to see 3dp of the layer below in the bottom. Here is an example:

Layers 2

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

Now we only need to assign the drawable to the background property of our view (ImageView, Button, etc).

Here is how it came out in my app:

Bar

Other effects

We can put lines in top and bottom at the same time:

Top and bottom

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_selected="true">
        <layer-list>
            <item>
                <shape>
                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:top="3dp" android:bottom="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

We can also put a line in one (or both) sides. Here we have a line in the left.

Left

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_selected="true">
        <layer-list>
            <item>
                <shape >

                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:left="3dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

We can use more that 2 layers to achieve new effects. Here we use 3 layers to get this effect.

Explaining 3 layers
3 Layers example

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_selected="true">
        <layer-list>
            <item >
                <shape>
                    <solid android:color="#222222" />
                </shape>

            </item>
            <item android:bottom="3dp">
                <shape >

                    <solid android:color="#ffff7a00" />
                </shape>
            </item>
            <item android:bottom="6dp">
                <shape>
                    <solid android:color="#222222" />
                </shape>
            </item>
        </layer-list>
    </item>
</selector>

Using “animation layers” to build complex bitmaps animations in Android

For the android game WhoSaid we wanted to animate a character reading (we call it will). But we wanted it to appear completely random.

will

We wanted the character to move the eyebrows, the eyes and the mouth separately. But the number of combinations were too high (120 frames), making almost impossible not to crash the app, and complicating the xml for the animation.

So we decided to divide the character into three “animation layers”: body + eyebrows (3 frames), eyes (5 frames) and mouth (8 frames). 16 frames in total, but this way we have 120 combinations.

Layout configuration

So we figured out who to do it minimizing the number of images. We used a FrameLayout as container of the layers. And each animation layer is an ImageView.

In the layout file we must include a FrameLayout where we are going to put the ImageViews. In this case we have three ImageViews, one for the eyebrows animation , other for the eyes animation and the last for the mouth animation.

The order of the ImageViews is important. The first ImageView is the base, and the later views are in top of that in descending order. In this case the base goes first, and later the eyes view.

Here is the layout file:

<?xml version="1.0" encoding="utf-8"?>

<!--Container of the three layers-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:padding="10dp">

    <!--First layer -> Base layer-->
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/willBase"
        android:src="@drawable/will_transparent" />

    <!--Second layer-->
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/willEyes"
        android:src="@drawable/will_transparent" />

    <!--Third layer -> Top layer-->
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/willMouth"
        android:src="@drawable/will_transparent" />
</FrameLayout>

Don’t forget to set the src property of your ImageViews to a transparent bitmap of the same size of your frames. If not the images of the animation will stretch to occupy all the view without maintaining proportions.

Drawable configuration

First of all, all the frames must be the same size.

Frame sizes must be equals.

Frame sizes must be equals.

And now the configuration for the three layers.

First layer (base layer)

Here we have the tree images that compound our base animation layer.

Eyebrows animation.

Base image with the three frames that compose the eyebrows animation.

With this three images we create an animation drawable:

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item android:drawable="@drawable/base_eyebrows_no_mouth_001" android:duration="4350"/>
    <item android:drawable="@drawable/base_eyebrows_no_mouth_002" android:duration="4375"/>
    <item android:drawable="@drawable/base_eyebrows_no_mouth_003" android:duration="4250"/>

</animation-list>

Second layer, eyes animation

And in top of that we will set our eyes animation, composed by five frames.

Frames that compose the eyes animation.

Frames that compose the eyes animation.

And the xml for this animation:

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/will_eyes_001" android:duration="810"/>
    <item android:drawable="@drawable/will_eyes_002" android:duration="810"/>
    <item android:drawable="@drawable/will_eyes_003" android:duration="810"/>
    <item android:drawable="@drawable/will_eyes_001" android:duration="910"/>
    <item android:drawable="@drawable/will_eyes_002" android:duration="910"/>
    <item android:drawable="@drawable/will_eyes_003" android:duration="810"/>
    <item android:drawable="@drawable/will_eyes_004" android:duration="50"/>
    <item android:drawable="@drawable/will_eyes_005" android:duration="50"/>

</animation-list>

Third layer, mouth

Frames that compound the mouth animation. I show you as a gif because there are 8 frames.

Mouth animation

And here we have the animation xml:

<?xml version="1.0" encoding="utf-8"?>

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">

    <item android:drawable="@drawable/will_mouth_001" android:duration="225"/>
    <item android:drawable="@drawable/will_mouth_008" android:duration="275"/>
    <item android:drawable="@drawable/will_mouth_004" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_006" android:duration="225"/>
    <item android:drawable="@drawable/will_mouth_002" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_006" android:duration="275"/>
    <item android:drawable="@drawable/will_mouth_007" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_003" android:duration="300"/>
    <item android:drawable="@drawable/will_mouth_004" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_008" android:duration="350"/>
    <item android:drawable="@drawable/will_mouth_004" android:duration="225"/>
    <item android:drawable="@drawable/will_mouth_003" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_006" android:duration="280"/>
    <item android:drawable="@drawable/will_mouth_007" android:duration="300"/>
    <item android:drawable="@drawable/will_mouth_003" android:duration="275"/>
    <item android:drawable="@drawable/will_mouth_008" android:duration="375"/>
    <item android:drawable="@drawable/will_mouth_002" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_007" android:duration="350"/>
    <item android:drawable="@drawable/will_mouth_006" android:duration="250"/>
    <item android:drawable="@drawable/will_mouth_003" android:duration="240"/>
    <item android:drawable="@drawable/will_mouth_001" android:duration="225"/>

</animation-list>

You will notice that there are repeated drawables. That not increase the memory that the animation occupy, but it gives a more variety animation.

Code configuration

And last but not least we have to code our animation.

private void animateWill() {
    ImageView willBase = (ImageView) getView().findViewById(R.id.willBase);
    willBase.setAdjustViewBounds(true);
    willBase.setBackgroundResource(R.drawable.will_base_eyebrows_animation);
    AnimationDrawable eyebrowsAnimation =(AnimationDrawable) willBase.getBackground();
    eyebrowsAnimation.start();

    ImageView willEyes = (ImageView) getView().findViewById(R.id.willEyes);
    willEyes.setAdjustViewBounds(true);
    willEyes.setBackgroundResource(R.drawable.will_eyes_animation);
    AnimationDrawable readingAnimation =(AnimationDrawable) willEyes.getBackground();
    readingAnimation.start();

    ImageView willMouth = (ImageView) getView().findViewById(R.id.willMouth);
    willMouth.setAdjustViewBounds(true);
    willMouth.setBackgroundResource(R.drawable.will_mouth_animation);
    AnimationDrawable mouthAnimation =(AnimationDrawable) willMouth.getBackground();
    mouthAnimation.start();
}

Basically you have to find the view, set the background resource, prepare the animation and start it.

Memory problems

If you are careless it’s easy to run out of memory using this method, cause you are working with lots of bitmaps. It’s strongly recommend to use custom drawables for each pixel density. Each pixel counts especially on low-level devices.

Be careful with Roboguice and creating and destroying Activities/Fragments that has animations.  When finishing an Activity/Fragment that extends a RoboActivity/RoboFragment it’s not destroyed immediately, it has to wait for a Finalizer to be garbage collected. It will occur but It will not fast, even if you call the garbage collector. So if you create a lot of views you will face a problem.

For this game we only used Roboguice to inject views, so we decided to implement the view injection by ourselves in some kind of framework we are building for future applications (that function it’s very few code indeed). We will release it soon, when it is a little bit more mature.

Managing ImageButtons

Sometimes when designing our application we limit the touch area of a ImageButton to its image, making the buttons difficult to use and our users angry.

Or for the contrary, we make the ImageButton bigger distorting the image in the process.

This is because we are managing bad the src and background properties of ImageButton.
The key is to assign the image to the src property, not the background.
If you set the image to the background it will stretch until occupy all the button.
Don’t forget to set the background to null (@null) or transparent (@android:color/transparent) or the background will get a light gray touch.

Bad
The image is set to the background property, and the image get stretched:

Stretched button

<ImageButton
    android:id="@+id/brush"
    android:layout_height="fill_parent"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="fill_parent"
    android:background="@drawable/ic_action_edit"/>

Bad
There background is not set to transparent and we get a gray background.

Light background

<ImageButton
    android:id="@+id/brush"
    android:layout_height="fill_parent"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:layout_height="fill_parent"
    android:src="@drawable/ic_action_edit"/>

Good

Good

<ImageButton
    android:id="@+id/brush"
    android:layout_height="fill_parent"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:src="@drawable/ic_action_edit"
    android:background="@null"/>

It has the layout_width to 0dp and the weight to 1 because it is in a LinearLayout occupying all the width. Here is the result.

Bar

Better
Finally, to give the user a feedback I create a drawable (called background_button)that highlights the button when is pressed.

Pressed button

<ImageButton
    android:id="@+id/brush"
    android:layout_height="fill_parent"
    android:layout_width="0dp"
    android:layout_weight="1"
    android:src="@drawable/ic_action_edit"
    android:background="@drawable/background_button" />

Here is the drawable:

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#44ffffff" />
            <corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp" android:topLeftRadius="6dp" android:topRightRadius="7dp" />
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#00ffffff" />
            <corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp" android:topLeftRadius="6dp" android:topRightRadius="6dp" />
        </shape>
    </item>
</selector>