Codementor Events

Android UI Tutorial: Layouts and Animations

Published May 14, 2015Last updated Jun 08, 2017
Android UI Tutorial: Layouts and Animations

This article is based on the Codementor Office Hours hosted by Linton Ye of Mirror for Android Studio. In this session, Linton makes a quick live coding of Android UI creations and shows how to use the different layouts, Views (TextView, ListView, ImageView, GridView, RecyclerView) and Motions (Property Animation, drawable Animation) by live coding. You may find this useful before you start your own project.

The Goal

This tutorial is aimed at the procedure of using layouts to present animations. Instead of going through every detail of doing everything, the goal is to give you an impression of how it feels like to build a UI for Android from start to end.

You can download the demo project on GitHub.


Layouts

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout

The basic building block for Android UI is layout. So we’re going to see how we can actually build those layouts.There are a few options like frame layouts, linear layouts relative layouts, grid layouts. The top three( frame layouts, linear layouts relative layouts) there are probably the most important ones and we’re going to see how we use it in practice.

Frame Layout

Android UI frame layout

So first of all, frame layout is  basically just putting stuff one on top of each other. There’s not a lot of options on this layout.  For this image above, on the top is the ImageView, and there’s a couple of TextViews, and then an ImageView for that Star.

Linear Layout

Android UI linear layout

We can start basically with a linear layout on the top, which is a container for everything. And then we put an ImageView on the top of the LinearLayout, in which it has an option to lay its children horizontally or vertically so you can specify and attribute.Then we can put two text views, one below each other, inside a linear layout with a vertical orientation.

One of the best practices to make a layout is to minimize the number of views it has, especially if it’s something that’s going to appear over and over again. We want it to make the hierarchy as shallow as possible.

Relative Layout

Android UI relative layout

One way to do make the hierarchy shallower is to use a relative layout. In a relative layout,  it’s a pretty easy way to arrange items in a single container without introducing that extra level of hierarchy here.

Custom View

If you handle all the rendering by yourself, you can achieve this just by using one custom view. Depending on your situation, sometimes it’s really necessary to do a custom view if you have a performance critical app, as it’s going to improve things like having to scroll through lists extremely fast.

Android UI custom view

If the system only needs to inflate just a single view for the item, scrolling is going to be very smooth. But of course it comes with its own price in that it’s a bit more difficult to implement.

Android Studio

Android Studio is a lot better than what we used to have on Eclipse, on ADT, where the UI tool was not useful at all. Personally I still go to the actual .xml a lot. For my work, I tend to just throw in some stuff there that we need to build and then fine tune it in the actual .xml format, which is a lot easier to work with.

![Android studio](https://s3.amazonaws.com/codementor_content/2015-Apr-week3/ALC/standard+xml.png =>width= 65%)

Above is a standard .xml file. The system is going to create a lot of  things for you. If you look at the image above, notice the tag name is the type of view or layout you want to put into your UI. Typically everything is going to start with the android:. If you’re not familiar with .xml and its name space, just follow this structure. When something goes wrong, when you type something that’s not acceptable, Android Studio’s going to tell you, so don’t worry about that.

Relative layout

As mentioned before, this is what we want to get in a relative layout:

Android UI relative layout

In which the respective code would appear like:

Android UI relative layout

However, the code above would end up giving you this instead:

Android UI relative layout

Android Studio has this fantastic way where you can type any part of a string, it'll show you a list of suggestions to complete the string. So, without further ado, let's start fixing this code.

First of all, let’s just use some hard-coded value for the size of the image view so that we can see it clearly in this session. Using wrapped content is good, but using hard-coded body here can lay out things a bit correctly.

<ImageView
  android:layout_width=“160dp”
  android:layout_height=“180dp” 

Inside a relative layout, you can specify the position of your view relative to its parent or its siblings:</span>

…
<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_alignParentLeft="true"
  android:layout_alignParentTop="true"
  android:text="Perfect world"
  android:id="@+id/textView"/>

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_below_="@id/name"
  tools:text="Kodaline"
  android:id="@+id/artist"/>
…

We wanted the album name to be on the top left position, align parent left “true.” And align parent top “true.”

The artist name is supposed to be below the ID/name. The format “@+id/name”, it’s kind of an interesting format here, but this is the type of the resources. Everything is generalized as a resource. When we have this cross sign “+”, it means that if we don’t already have that ID created, I’m going to create it. That’s the typical way of specifying ID in a layout.

As for the star, it’s going to be aligned to the right of its parent:

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_alignParentRight="true">

…

Views

Types of views:

  • TextView, ImageView, Button...
  • ListView, GridView
  • Recycler View
  • ViewPager

Before we put all these things inside a grid, let’s have an overview of what we have here:

You can have your custom views written in Java already. A ListView shows things in a vertical list, which is one of the most used views in Android. Furthermore, GridView is kind of outdated.

Since last summer, Google actually introduced the RecyclerView. If you’re writing new projects, use RecyclerView instead of a ListView because it’s a lot more flexible and it gives you a lot of other options. For one thing, ListView will only show you vertical lists. To create a horizontally scrolling list, you’re going to have to find a third party library or just code it all by yourself. However, if you use a RecyclerView, you can do pretty much anything.

For our grid here, we’re going to use a RecyclerView as well. But the idea is that you can actually populate these RecyclerViews and then see it on the real device using this tool.

Here's the layout component activity_album_list.xml that I had written.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".AlbumListActivity">

  <android.support.v7.widget.Toolbar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:is="@+id/toolbar"
    android:background="?attr/colorPrimary"
    style="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    />
  <android.support.v7.widget.RecyclerView
    android:id="@_id/album_list"
    android:layout_below="@id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</RelativeLayout>

So, the code example above uses the RecyclerView.

In our Java code (AlbumListActivity.java), we have two activities, which is the one of the basic units for Android’s UI so it handles all the user interactions. Here is how we can populate the views:

public class AlbumListActivity extends ActionBarActivity {
  public static Object sHook;
  @InjectView(R.id.album_list)
  RecyclerView mAlbumList;

  @InjectView(R.id.toolbar)
  Toolbar mToolbar;

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

    ButterKnife.inject(this);
    mToolbar.inflateMenu(R.menu.menu_album_list);
    populate();
  }

  interface OnVHClickedListener {
    void onVHClicked(AlbumVH vh);
  }

  static class AlbumVH extends RecyclerView.ViewHolder implement…
    private final OnVHClickedListener mListener;
    @InjectView(R.id.album_art)
    ImageView albumArt;
    @InjectView(R.id.name)…

I’m not going to go into detail about how to write all of the stuff up there, but setContentView(R.layout.activity_album_list) is a key part, as it’s going to set the content view of this activity to the layout that we were just building.

Afterwards, we can let this code populate the view:

AlbumListActivity.java

private void populate() {
  final int[] albumArts = {R.drawable.christina,
    	R.drawable.ellie,
    	R.drawable.foster,
        R.drawable.keane,
        R.drawable.kodaline,
        R.drawable.pinkrobots,};
  RecyclerView.Adapter adapter = new RecyclerView.Adapter<AlbumVH>() {
    	@Override
        public AlbumVH onCreateViewHolder(ViewGroup parent, int viewType) {
      View albumView = getLayoutInflater().inflate(R.layout.album_grid_item, parent, false);
      return new AlbumVH(albumView, new OnVHClickedListener() {
        @Override
        public void onVHClicked(AlbumVH vh) {
          int albumArtResId = albumArts[vh.getPosition() % albumArts.length];
          Intent intent = new Intent(AlbumListActivity.this, AlbumDetailActivity.class);
          intent.putExtra(AlbumDetailActivity.EXTRA_ALBUM_ART_RESID, albumArtResId);
                    
          View sharedView = vh.albumArt;
          ActivityOptions ops = ActivityOptions.makeSceneTransitionAnimation(AlbumListActivity.this, sharedView, "albumArt");
          startActivity(intent, ops.toBundle());
        }
      });
    }
        
    @Override
    public void onBindViewHolder(AlbumVH holder, int position) {
      holder.albumArt.setImageResource(albumArts[position % albumArts.length]);
      MockData md = new MockData();
      holder.name.setText(md.phrase());
      holder.artist.setText(md.personName());
    }

Basically one thing to remember is that in order to populate this RecyclerView, we need to create an adapter, and in this adapter we kind of create some data. For each of the views there, you want to create one thing which is called ViewHolder. You want to extend from RecyclerView holder and you’re going to populate each of the views, you’re going to handle it on click event. That’s about this RecyclerViews.

Again, this project is on GitJub so that you can try it out yourself.


Motion

Here are the different types of motion in app design:

  • Property animation
  • Reveal effect
  • Activity transitions
  • Share view transitions
  • View state change animation
  • Vector drawable animation

Motion is getting a lot more prominent than before. The basic building block is the Property animation, which I’m going to show you what that means.

The goal is to implement this part of the animation:

Android UI Live Coding Example 1

Not necessarily going back yet, but this coordination of making the button bigger or smaller and having those views come in and out.

If we check out our activity_album_detail.xml in our layout folder, you'll see what elements we have for the images and views, and as you see in our AlbumDetailBox.java, we're going to animate these elements through Mirror.

The benefit of using Mirror is that you don’t have to recompile every time you change the code, so it’s going to push all the changes over to the device. It’s going to push the layout changes and also it’s going to push the Java code as well.

The two following java codes are our component of our AlbumDetailBox.java:

public class AlbumDetailBox extends MirrorSandboxBase {
    @InjectView(R.id.fab)
    View fab;
    @InjectView(R.id.title_container)
    View cyanPanel;
    @InjectView(R.id.info_container)
    View whitePanel;
    @InjectView(R.id.album_art)
    ImageView albumArt;

    public AlbumDetailBox(View root) {
        super(root);
        ButterKnife.inject(this, root);
    }

    @OnClick(R.id.fab)
    public void onFabClick() {
        Animator anim = ViewAnimationUtils.createCircularReveal(albumArt, 100, 100, 1000, 000);
        anim.start();
    }

For example, over here, if you’re familiar with ButterKnife, it's just an easy way to bind your views to variables instead of using "findViewById".

The fab is a little icon, which is a floating action button. You can do animations on the view as well, but it's easier to inject the ButterKnife.

If you want to make the fab icon shrink, you can do this:

@Override
public void $onLayoutDone(View rootView) {
    enterAnimation().start();
    exitAnimation().start();
    ValueAnimator a = ObjectAnimator.ofFloat(fab, "scaleX", 0, 1).setDuration(1000);
    a.start();
}

You can give the ObjectAnimator a target that you want to animate. You can give it the property that you want to animate, and then you give it a few values. When I save it, mirror's going to compile it. If you want to actually see the animation, we can make it slower by setting the duration to 2000.

Android UI live coding example 2

Afterwards, you create this animator. That’s how you create animator using the property animator. You can also sequence all the animators, set them to play together.

Motion Kit

In order to make animating easier, we’ve actually written an Open Source library which is called Motion Kit.

@Override
  public AlbumDetailBox(View root) {
    wrap(fab).scale(0, 1).start();
  }

  private MirrorView wrap(View view) { return new MirrorView<>(view); } 

It’s just a bit of simple wrapper code to make it easier. We can do this wrap around a view that we want to animate with a scale of zero to one like wrap(fab).scale(0, 1).start().

For the rest of the animation, we want to kind of animate the bottom - basically unfold the two containers in the bottom.

@Override
public void $onLayoutDone(View rootView) {
  wrap(fab).scale(0, 1);
  unfold(cyanPanel).start();
}

private MirrorView wrap(View view) { return new MirrorView<>(view); }

I have created some helper functions at the bottom of AlbumDetailBox.java, and unfold(cyanPanel).start() compiles only the chained files and its dependent.

We can sequence a bunch of animations there and play them one by one:

@Override
public void $onLayoutDone(View rootView) {
  MotionKit.setGlobaSpeed(0.3);
  MotionKit.sequence(unfold(cyanPanel),
    unfold(whitePanel),
    wrap(fab).scale(0, 1)
  ).start();
}

private MirrorView wrap(View view) { return new MirrorView<>(view); }

So, as you can see we're unfolding the cyan panel first and then unfold the white panel and then the fab icon. I set the play speed to 0.3, which is going to be a lot slower so we can actually see it:

Android UI live coding example 3

However, don't actually use this in your final code if you want your animation to be swift.

I've also extracted the animations as its own methods. For example, I have an exit animation method:

public MirrorAnimator exitAnimation() {
    return MotionKit.sequence(wrap(fab).scale(1, 0).duration(200),
        fold(whitePanel).duration(100),
        fold(cyanPanel).duration(100)
    );
}

Which is going to make the panels shrink and fold all the way up. The point of using these methods is to experiment to see if your animations are right or not, and this way you don't have to compile the full app.

In our AlbumDetailActivity.java, we want to play this animation, so I'm going to create a mirror sandbox (mBox):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_album_detail);
    ButterKnife.inject(this);
    populate();
    initTransitions();
    mBox = new AlbumDetailBox(getWindow().getDecorView());
}

So, as you can see I created the object with mBox = new AlbumDetailBox(getWindow().getDecorView()) and basically I will use it as a helper object.

I won't go into detail about this, but if you want to play the animation nicely in the live product, there is some more work to do. Feel free to read my blog post on transitions for more details.

Anyhow, this is the code for our enter animation:

@Override
public void onTransitionEnd(Transition transition) {
  mBox.enterAnimation().start();
}

This method is going to return your animator plots, which is just a simple wrapper of the standard property animation. We can call start, which means that after the system transitions, it’s going to play the enter animation.

To prevent the animation from flickering, we also need to add this bit of code (I'm not going to go into detail about it):

@Override
public void onGlobalLayout() {
  mBox.enterAnimation().setupStage();
}

The other part of the animation is that we want to show those two panels getting folded up and also show the fab icon shrinking and returning to its original size. We do this with override (this transition is exclusive to Android Lollipop):

@Override
public void finishAfterTransition() {
    MirrorAnimator mirrorAnimator = mBox.exitAnimation();
    mirrorAnimator.getAnimator().addListener(new Animator.AnimatorListener() {
    	@Override
    	public void onAnimationStart(Animator animation) {
        
        }
        
        @Override
        public void onAnimationEnd(Animator animation) {
            superFinishAfterTransition();
        }
        
        @Override
        public void onAnimationCancel(Animator animation) {
        
        }
        
        @Override
        public void onAnimationRepeat(Animator animation) {
        
        }
  });
  mirrorAnimator.start();
}

With the code above, we'll make sure our animation plays first before playing systems animation. We'll also add a listener that will call superFinishAfterTransition() after the activity playing the systems animation.

Final Thoughts

Of course, you can do these animations without Mirror, but you will have to run the full app. You will need to rebuild it, you will need to deploy it, and you will have to launch it. With Mirror you don’t have to do those if you use Mirror.

Android Weekly and Reddit Android are extremely useful in keeping up with what’s going on with Android, as Android is changing very rapidly so we wanted to keep an eye on everyday development.

Again, the whole base of my demo project is on GitHub.

Part 2: Q&A - How beginners can start with Android UI design, the best way to plan layouts, and more

Discover and read more posts from Linton Ye
get started
post commentsBe the first to share your opinion
Juliano João Bazzo
6 years ago

Thanks a lot! Excellent!!

Tutorial see
7 years ago

Hello Ravi thanks for this Example
Please try my All Layout tutorail Example
http://www.tutorialsee.com/tags/layout

Show more replies