Adding physics-based animation to Android applications. Creating transition animations between Activity in Android

In this article we will look at how to animate interface elements in Android. Under the interface elements in in this case this means all descendants of the View class (a complete list of descendants can be found in the documentation of the View class). Animation is an easy way to make an application more lively :)

1. Let's start by creating a test site. Let's make a simple application with a button and a picture in the middle of the screen. I won’t give the code, it’s simple, if anything, look at the sources (they’re at the end of the article).

2. In the /res/anim directory, create a file anim.xml and write there
< set xmlns:android=" http : // schemas.android.com /apk/res/android " android:shareInterpolator=" false " > < alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " />
This is the animation description that we will apply to our image. We’ll look at what’s happening here in more detail below, but for now we’ll just copy it to a file.

3. To load an animation from xml file used static method class AnimationUtils
loadAnimation(Context context, int id), Where context is the current context, and id- identifier of the resource with animation. The method returns an instance of the Animation class.
Animation - an abstract class for representing animation in an application.
To apply it, the resulting instance of the Animation class is passed to the method
startAnimation(Animation animation) class View (and all its descendants).

4. Let's write to the AnimationTestActivity.java file:
public class AnimationTestActivity extends Activity ( ImageView image; Button button; Animation anim; @Override protected void onCreate(Bundle savedInstanceState) ( super .onCreate(savedInstanceState); setContentView(R.layout.main); image = (ImageView)findViewById(R. id.image); button = (Button )findViewById(R.id.button); anim = AnimationUtils.loadAnimation(this , R.anim.anim); // 1 button.setOnClickListener(new OnClickListener() ( @Override public void onClick(View v) ( image.startAnimation(anim); //2 ) )); ) )
1) Read the file with the identifier R.anim.anim (which corresponds to the file /res/anim/anim.xml) and get an instance of the Animation class.
2) By clicking the button, we apply animation to the image.

5. You can run our application. When you press the button, the picture will disappear and then slowly begin to appear back.

6. Now let's take a closer look at how animation is created in an xml file.
There are 4 types of animation:


  • alpha(transparency, visibility)

  • scale(scaling)

  • rotate(turn)

  • translate(move)

To create animation, we must describe the initial and final states of the object, and the system itself will decide how to move from one state to another. In our example
< alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " />
we describe the alpha animation, that is, we change the visibility of the object. Set the initial state fromAlpha="0.0"(completely invisible) and finite toAlpha="1.0"(fully visible). Specify the duration of the animation duration="1000"(in milliseconds). And everything else, that is, how to change the visibility of an object in order to turn it from invisible to visible in a second, the system does itself. This is calculated using interpolation- in computational mathematics, a method of finding intermediate values ​​of a quantity from an existing discrete set of values. For each animation you can set an interpolator
-AccelerateDecelerateInterpolator(@android:anim/accelerate_decelerate_int erpolator) - the rate of change is low at the beginning and end, and accelerates in the middle

-AccelerateInterpolator(@android:anim/accelerate_interpolator) - rate of change starts low and then accelerates

-AnticipateInterpolator(@android:anim/anticipate_interpolator) - changes start at reverse side and then quickly move forward

-AnticipateOvershootInterpolator(@android:anim/anticipate_overshoot_inte rpolator) - changes start in the opposite direction, then quickly move forward and fly above the final value, and then return to the final value

-BounceInterpolator(@android:anim/bounce_interpolator) - the rate of change increases at the end

-CycleInterpolator(@android:anim/cycle_interpolator) - repeat the animation a specified number of times. The rate of change follows a sine wave

-DecelerateInterpolator(@android:anim/decelerate_interpolator) - the rate of change decreases at the end

-LinearInterpolator(@android:anim/linear_interpolator) - the rate of change is constant

-OvershootInterpolator(@android:anim/overshoot_interpolator) - changes jump forward and fly above the final value, and then return to the final value

The interpolator is specified using the android:interpolator attribute. For example
android:interpolator="@android:anim/cycl e_interpolator". The default is LinearInterpolator.

7. Description of initial and final states
1) alpha (transparency, visibility)
- android:fromAlpha- initial transparency value. 0.0 - completely transparent (invisible), 1.0 - completely opaque (visible)
- android:toAlpha - final value transparency

2) scale
- android:fromXScale- initial scale value along the X axis (where the current size corresponds to the value 1.0)
- android:toXScale- final scale value along the X axis
- android:fromYScale- initial scale value along the Y axis (where the current size corresponds to the value 1.0)
- android:toYScale- final scale value along the Y axis
- android:pivotX- x coordinate of the point, which will remain unchanged after scaling
- android:pivotY- y coordinate of the point, which will remain unchanged after scaling

Possible values ​​for pivotX and pivotY:
in pixels relative to the left (or top for Y coordinate) edge of the element (for example, "5")
as a percentage relative to the left (top) edge (for example, "5%")
as a percentage relative to the left (top) edge parent element(for example "5%p")

For example, if pivotX=0, pivotY=0 (which corresponds to the top left corner of the element), then scaling will resize the element down and to the right. If pivotX=50%, pivotY=50%, then the point is in the center of the element and the size changes in all directions, while the center will remain at one point.

3) rotate (turn)
- android:fromDegrees - Initial value rotation angle (in degrees, negative value possible)
- android:toDegrees- final value of the rotation angle
- android:pivotX- x coordinates of the center of rotation.
- android:pivotY- y coordinate of the center of rotation.
Possible values ​​of pivotX and pivotY as in the scale animation

4) translate (move)
- android:fromXDelta- x coordinate of the starting point of movement. Possible values:
in pixels relative to the original position (for example “5”)
as a percentage relative to the width of the element (for example, “5%”)
as a percentage relative to the width of the parent element (for example "5%p")
- android:toXDelta- x coordinate of the end point of the move
- android:fromYDelta- y coordinate of the starting point of movement
- android:toYDelta- y coordinate of the end point of the movement

8. Additional options
There are also attributes common to all four types of animation, the most useful of which are:
- android:duration- animation duration (in milliseconds)
- android:interpolator- defines the interpolator for animation
- android:repeatCount- number of additional animation repetitions. Exactly additional ones, that is, the animation will be executed once anyway. The default value is "0" - this means the animation will only be executed once. A value of "1" means that the animation will be executed twice (once the main one and once the secondary one). The value “-1” or “infinite” means endless repetition.
- android:repeatMode- determines the behavior of the animation when it has reached the end, and the repeatCount parameter is not equal to 0. There are two values: “restart” - the animation starts again and “reverse” - the animation will go in the reverse order.
- android:startOffset- delay before the animation starts (in milliseconds)

9. Combining Multiple Animations
You can apply multiple types of animations to an element at the same time. For example, if we write:
< set xmlns:android=" http : // schemas.android.com /apk/res/android " > < alpha android:fromAlpha=" 0.0 " android:toAlpha=" 1.0 " android:duration=" 1000 " /> < rotate android:fromDegrees=" 0 " android:toDegrees=" 360 " android:pivotX=" 50% " android:pivotY=" 50% " android:duration=" 1000 " />
The image will change transparency in 1 second (from completely transparent to opaque) and at the same time rotate 360 ​​degrees.

Animations can be set to different durations, for example, let's set duration=5000 for the animation rotate. Now the picture will rotate much more slowly, and the transparency will still change in a second.

By using startOffset, you can make the animations sequential. Add the rotate attribute startOffset="1000"(that is, we will make a delay equal to the duration of the first animation). Now the picture will first become visible in 1 second, and then only rotate 360 ​​degrees.

Several animations can be combined into sets using the tag. One such tag will always be in the file and is the root tag. You can set the following attributes for a set:
- duration(duration), repeatMode(repeat mode) - these attributes will be applied to each animation in the set
- interpolator- defines the animation interpolator and shareInterpolator- whether this interpolator will be applied for each animation in the set (possible values ​​are “true” and “false”)
- startOffset(delay) - delay for the entire set of animations.
Unfortunately, the attribute cannot be applied to the set repeatCount, that is, repeating a set of animations several times will not work.
Sets can be of any nesting.

10. Creating animation without xml
Animation can be created without using xml, directly in the program code. For this, the Animation descendant classes are used:
1) AlphaAnimation to create alpha animation. The class constructor looks like
AlphaAnimation(float fromAlpha, float toAlpha) where fromAlpha and toAlpha are the initial and final transparency values, respectively (from 0.0 to 1.0)

11. Let's create an animation in the code that, when you press a button, will rotate the picture by a random angle (from 0 to 360) and enlarge it to a random size (no more than twice). For this purpose I added another button randomButton
randomButton.setOnClickListener(new OnClickListener() ( @Override public void onClick(View v) ( Random random = new Random (); //1 RotateAnimation rotate = new RotateAnimation (0, (float )random.nextInt(360), Animation. RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //2 rotate.setDuration(1000); //3 rotate.setRepeatMode(Animation.REVERSE); //4 rotate.setRepeatCount(1); //5 long duration = rotate.computeDurationHint(); //6 float size = random.nextFloat() + 1.0; //7 ScaleAnimation scale = new ScaleAnimation(1.0f, size, 1.0f, size, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF , 0.5f); //8 scale.setDuration(1000); scale.setStartOffset(duration); //9 AnimationSet set = new AnimationSet (false); //10 set.addAnimation(rotate); //11 set.addAnimation (scale); image.startAnimation(set); //12 ) ));
1) Create a Random object to generate random numbers. You can read more about Random in the documentation; now we are interested in the int nextInt(int n) methods - generating an integer in the range from 0 to n. And the float nextFloat() method is generating real number from 0 to 1.
2) Create a rotation animation. Start angle = 0, end angle = random number from 0 to 360. Animation.RELATIVE_TO_SELF means that we will indicate the rotation center point as a percentage relative to the width of the element. Don't forget that the value 1.0 corresponds to 100%, which means 0.5f is 50%. This means the center of rotation point will be in the middle of the picture.
3) Set the animation duration to 1000 milliseconds (this is 1 second)
4) We define the repetition mode as Animation.REVERSE, that is, when repeating the animation, we will go in the reverse order.
5) Set the number of additional repetitions = 1. This means that the animation will be repeated twice, once in forward order and once in reverse.
6) The long computeDurationHint() method calculates how long the animation will last in total. There is a getDuration() method, but it simply returns the duration value that we set with the setDuration() method. In our case, we set the duration value to 1000 and the getDuration() method will return 1000 and will not take into account that the animation will repeat twice, which means it will actually last 2000 milliseconds. The computeDurationHint() method will calculate the duration taking into account retries and delays.
7) We calculate new size Pictures. The value 1.0 is the current image scale, so the value 2.0 means the image is doubled. We generate a number from 0.0 to 1.0 and add 1, which means we get a number from 1.0 to 2.0
8) Create a scaling animation from the current picture size to a randomly generated number from 1.0 to 2.0
9) Set a delay equal to the total duration of the rotation animation. So that the second animation starts immediately after the end of the first
10) Create a set of animations.
11) Add two created animations to the set
12) Apply a set of animations to the picture

12. Another interesting method of the Animation class
setAnimationListener(Animation.AnimationListener listener)- sets a listener for animation state changes. The Animation.AnimationListener interface defines the following methods:
onAnimationStart (Animation animation)- called when the animation starts
onAnimationRestart (Animation animation)- called when the animation repeats
onAnimationEnd(Animation animation)- called at the end of the animation

For example:
anim = AnimationUtils.loadAnimation(this, R.anim.anim); anim.setAnimationListener(new AnimationListener () ( @Override public void onAnimationEnd(Animation animation) ( Log.d("MY" , "animation end" ); ) @Override public void onAnimationRepeat(Animation animation) ( Log.d("MY " , "animation repeat" ); ) @Override public void onAnimationStart(Animation animation) ( Log.d("MY" , "animation start" ); ) ));
We don’t do anything useful when changing the animation state, we just write it to the log.

That's all. I’ve told you the basics, it’s better to learn the rest through experiments :)

Sources can be downloaded here

At ribot, we care about creating beautiful and meaningful experiences for people, in which movement plays an important role.

After witnessing an inspiring talk at Droidcon London, I decided to dig deeper into the motion elements of Android. With that in mind, I've compiled all my findings to help developers and designers know how easy it is to add beautiful motion to Android apps.

If you'd like to try making these animations yourself, each of these examples is packaged in an Android app on Github.

I love movement, not only does it increase interaction, but it immediately turns heads. Think about the apps you use and the motion graphics, especially how nice, easy, free and natural they look.

Falcon Pro: Even subtle movements can make a huge difference in user experience

Now compare them to apps you love that don't evoke the same feelings.

Medium: As much as I love the Medium app, it really lacks movement in the areas that it deserves to have it.

It's all in the details

We can use these motion effects in different ways:

  • Transfer users through navigation context
  • Strengthen the elementary hierarchy
  • Explain changes between components displayed on screen

The purpose of this article is to show you how easy it is to implement movement in your applications where it can bring significant benefits - so let's get started.

Touch feedback

Security feedback, when the user touches the screen, helps communicate in a visual manner so that interaction occurs. These animations should not distract the user, but should provide fun, clarity, and encourage further exploration.

The Android framework provides a ripple state for this feedback layer, which can be used to set the animation's background to one of the following:

Android:attr/selectableItemBackground - Shows a ripple effect within the image's boundaries.

The pulsation begins at the point of contact, filling the background of the presented image

?android:attr/selectableItemBackgroundBorderless- Shows a pulsating effect that goes beyond the boundaries of the presented image.

A circular pulsating effect begins at the point of contact, filling a radius beyond the presented image

View Property Animator

View Property Animator was introduced at API level 12, which allows you to simply and efficiently perform animation operations (in parallel) on several image properties using a single Animator instance.

Here I am making animations on all the properties presented below.

alpha() - Sets the alpha value to make the animation
ScaleX() & ScaleY() - Balances the view on its X and/or Y axis
translationZ() - Translates the view on its Z axis
setDuration() - Sets the duration of the animation
setStartDelay() - Sets the animation delay
setInterpolator() - Sets animation interpolation
setListener() - Sets a listener to know when the animation starts, ends, repeats, or is canceled.
Note: When the listener has been set to this image, and if you perform other animations at the same point and do not want to use this callback function, then you must set the listener to NULL.

This is also simple and neat to implement programmatically:

mButton.animate():

TranslationZ(10f)

SetInterpolator(new FastOutSlowInInterpolator())

SetStartDelay(200)

SetListener(new Animator.AnimatorListener() (

public void onAnimationStart(Animator animation) ( )

public void onAnimationEnd(Animator animation) ( )

public void onAnimationCancel(Animator animation) ( )

public void onAnimationRepeat(Animator animation) ( )

Note: We don't actually have to call start() on our animation builder, since the animation starts automatically as soon as we stop declaring the animation at the same time. If this is the case, then the animation will not start until the next update from the interface instrumentation event queue.

Note: For backwards compatibility, you can use the ViewCompat class to implement ViewPropertyAnimator from Android API version 4 and above.

Like the View Property Animator, the Object Animator allows us to animate various properties of the target image (both in code and XML file resources). However, there are a few differences:

Object Animator only allows an animation to exist in a single state per instance, for example an X scale followed by a Y scale
However, it allows the animation to exist in its normal state, such as the foreground color of the image.
Using custom Properties or States to animate the image to scale and change the foreground color, we can achieve the following:

Using Custom Property, we can create one instance of Object Animator by calling ObjectAnimator.ofInt() where we state:

View - View to apply animation
Property - Property for animation
Initial Color - The color that the animation view starts with.
Target color - The color with which this image must come to life
private void animateForegroundColor(@ColorInt final int targetColor) (
ObjectAnimator animator =
ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor);
animator.setEvaluator(new ArgbEvaluator());
animator.setStartDelay(DELAY_COLOR_CHANGE);
animator.start();
}
Then we set the evaluator (we use ArgbEvaluator since we are doing the animation between the color values), set the delay and start() the animation.

We instantiate the ObjectAnimator using ofFloat() because we don't animate integer values ​​when working with image sizes
Instead of custom property, we use image properties - both View.SCALE_X and View. SCALE_Y
private void resizeView() (
final float widthHeightRatio = (float) getHeight() / (float) getWidth();
resizeViewProperty(View.SCALE_X, .5f, 200);
resizeViewProperty(View.SCALE_Y, .5f / widthHeightRatio, 250);
}
private void resizeViewProperty(Property property
float targetScale,
int durationOffset) (
ObjectAnimator animator = ObjectAnimator.ofFloat(this, property, 1f, targetScale);
animator.setInterpolator(new LinearOutSlowInInterpolator());
animator.setStartDelay(DELAY_COLOR_CHANGE + durationOffset);
animator.start();
}
Finally, we need to animate our off-screen resized image. In this case, we're using the AdapterViewFlipper to accommodate our images that we're animating off-screen. Using this means we can call showNext() on the ViewFlipper instance and it will animate off-screen images using the animation we've defined. Then, the next image will automatically come to life on the screen, also using the incoming animation that we also defined.

Interpolators

An interpolator can be used to determine the rate of change for an animation, meaning the speed, acceleration, and behavior of the animation can be changed. There are several different types of interpolators available and the differences between some of them are subtle, so I suggest trying them out on this device.

  • Without Interpolator - The view comes to life without variations in the rate of change
  • Fast - Outside the line - Inside

The image begins the animation and ends with linear motion

  • Fast - Slow - Inside

The image starts the animation quickly and slows down towards the end

  • Linear - Slow - Inside

The image begins with linear movements and slows down towards the end

  • Acceleration - Deceleration

The image starts out speeding up at the beginning of the animation, and gradually slows down as it comes to an end.

  • Accelerate - The image gradually accelerates until the animation ends
  • Braking - The image gradually slows down until the animation ends
  • Lead - The image begins with a slight rotation of the specified animation before it moves in a standard manner
  • Anticipate - Skip - Same as Lead, but the "pulling back" motion that occurs during the animation is a little more exaggerated
  • Jumping Interpolator - The image comes to life with a "bounce" effect before it reaches the finish line
  • Linear Interpolator - An image comes to life from start to finish with linear and smooth movement
  • Skipping Interpolator - The image “revitalizes” the exaggeration given value, retracting back to the required value

Circular animation

CircularReveal animation uses a clipped circle to either reveal or hide a group of elements user interface. Besides helping to provide visual continuity, it is also a pleasant interaction to help enhance it with the user.

As shown above, we start by using the View Property Animator to hide the Floating Action button before we begin to animate the animation before our eyes. Setting up our revive circle requires defining just a few attributes:

  • startView - the view that CircularReveal will start from (i.e. the compressed view)
  • centerX - Center coordinates for the X-axis when pressed
  • centerY- Center coordinates for the Y-axis when pressed
  • targetView - The view to create
  • finalRadius - The cutoff radius of the circle, equal to the hypotenuse of our values ​​- centerX and centerY

int centerX = (startView.getLeft() + startView.getRight()) / 2;
int centerY = (startView.getTop() + startView.getBottom()) / 2;
float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(
targetView, centerX, centerY, 0, finalRadius);

Window Transitions

Customizing the transitions used to navigate between transactions allows for stronger visual connections between application states. By default we can configure the following transitions:

  • input - Defines how transaction images enter the scene
  • exit - Determines how transaction images exit the scene
  • enter again - Defines how a transaction enters again after previously exiting
  • shared elements - Defines how transition images are exchanged between transactions

As with API Level 21, several new transitions have appeared and been introduced:

Explosive transition allows images to come out from all sides of the screen, creating an explosive effect when pressed.

The burst effect works very well on grid based layouts.

This effect is easy to implement - to start with, you need to create the next transition in the RES res/transition directory.

android:duration=“300“/>

All we have done here:

  • Explosive transition announced
  • Set the duration to 300 milliseconds

Or programmatically:

Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
getWindow().setEnterTransition(explode);

Slide

Slide transition allows you to slide in or out of a transaction with either right side, or from the bottom of the screen. While you may have arrived at this before, this new transition is much more flexible.

Slide transition allows you to sequentially slide into child images

This transition is likely to be common when switching transactions, I particularly liked the right slide due to its liquid-like state. Again, this is easy to do:

android:interpolator=“@android:interpolator/decelerate_cubic“
android:slideEdge=“end“/>

Here we:

  • Announcing the slide transition
  • Set the transition slideEdge to end there (on the right), so the slides go to the right - the bottom slide should be set to the bottom

Fading

A fading transition allows you to transition to a transaction internally or externally using the fading effect.

The fading transition is simple, although the fading transition is pleasing to the eye.

Creating it is even easier than previous transitions:

android:duration=“300“/>

Here we:

  • We announce the fading transition
  • Set the duration to 300 milliseconds

Transition optimization

Despite experimentation, I have found a couple of approaches that can help improve the transition effects mentioned above.

Allow window content transitions- you must enable the following attribute in themes that inherit from the material theme:

true

Enable/disable matching transitions- When transitioning, there may be a delay where one action waits for another to complete its transition before it can begin its own. Depending on the use case, transitions will generally look more fluid and natural if you enable these attributes:

true

Excluding Images from Transitions- Sometimes we may not want to create transitions for all our transaction images. I found that in most cases, the status bar and toolbar caused transition glitches. Luckily, we can exclude certain species that were included in our transitions:

android:duration=“200“>




Toolbar and Action Bar- When transitioning between actions using the Action Bar to using the Toolbar (and vice versa), I sometimes found that the transition was not always smooth. To fix this, I made sure that the two activities involved in the transition were using the same component.

Transition duration- You don't want the user to wait too long, but you also don't want to create components that appear at the speed of light. This depends on the transition you're using, so it's best to experiment, but I've found that a duration of 200-500ms works in most cases.

Common transition elements

Shared transition elements allow you to animate transitions between shared images across a transaction, creating more enjoyable transitions and giving the user a better sense of their journey.

Here, the image from our first action is scaled and translated into the header image in our second action

In our layouts, we must associate any common images using the transitionName attribute - this establishes transition relationships between images. Below are general images from the animation above:

These are shared images, which means they will come to life with each other during action transitions

To transition between these two, we start by declaring the name of the common transition, done using the transitionName attribute in the XML layouts.




android:transitionName=“@string/transition_view“/>



android:id=“@+id/view_shared_transition“
android:transitionName=“@string/transition_view“/>
android:id=“@+id/view_separator“/>
android:id=“@+id/text_detail“/>
android:id=“@+id/text_close“/>

Once this is done, we create a Pair object in step 1) containing our transition image, and its transitionName. We then pass it to example transaction options like (ActivityOptionsCompat) so that both activities are aware of the common components. From there we will start our transaction, through the example option:

Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView));
ActivityOptionsCompat transitionActivityOptions =
ActivityOptionsCompat.makeSceneTransitionAnimation(
SharedTransitionsActivity.this, participants);
ActivityCompat.startActivity(SharedTransitionsActivity.this,
intent, transitionActivityOptions.toBundle());

Separating these images while the transition is happening really helps complete the transition.

Here's the transition between these two images, but what about the images in the second act that slide from below?

(Those on the left)

I'm glad you asked! This is also easy to achieve, as shown below:

Slide slide = new Slide(Gravity.BOTTOM);
slide.addTarget(R.id.view_separator);
slide.addTarget(R.id.text_detail);
slide.addTarget(R.id.text_close);
getWindow().setEnterTransition(slide);
As you can see, we create a new Transition Slide template by adding target views for the transition and setting the slide as the transaction entry transition.

Custom Transitions

We also have the ability to create our own transitions using any of the animations from the API we've covered so far. For example, we can take Shared Element transitions one step further to become a transition image - this can come in handy when we want to display dialogs (or similar pop-up images), as shown below:

This movement helps direct the user's attention between component states

Let's take a quick look at what's going on here:

  • We start by creating a SharedTransition, passing in the pressed state along with the transition name to reference the shared component
  • Next we create an ArcMotion instance, this allows us to create curved effect movement when transitioning between two images
  • We then extend ChangeBounds to create a custom transition and transform the two forms (we have a separate class for the button and FAB). Here we override various methods from the class so that we can animate the required properties. We'll use a ViewPropertyAnimator to animate the transparency of the dialog images, an ObjectAnimator to animate the images between the two colors, and a sample AnimatorSet so we can animate both of these effects together.

Animated vector of the input coefficient

As of API version 21 (Lollipop), AnimatedVectorDrawable can be used to animate VectorDrawable properties to obtain an animation of the drawable.

Now it's easy to make several various types animation on the input coefficient

But how do we do this? Well, let's take a look at this:

It consists of several different files, and we start by creating our two separate vector files, each with several properties:

  • Height and Width - Actual Size vector image
  • Viewport height and width - Declares the size of the virtual canvas on which the vector tracks are drawn
  • Group Name - Declare the group the track belongs to
  • Pivot X & Y - Declare the pivot used for group scale and rotation
  • Color trajectory Fill-Color vector path fill
  • Path Data - Declare the data of the vector path used to draw the vector

Note: All link properties are stored in a common line file, which helps keep elements organized and neat.

android:height=“56dp“
android:width=“56dp“

android:viewportWidth=“24.0“>

android:pivotX=“12“
android:pivotY=“12“>

android:pathData=“@string/path_add“/>

The vector is generated from our ic_add.xml file (below)

android:height=“56dp“
android:width=“56dp“
android:viewportHeight=“24.0“
android:viewportWidth=“24.0“>
android:name=“@string/groupAddRemove“
android:pivotX=“12“
android:pivotY=“12“>
Android:fillColor=“@color/stroke_color“
android:pathData=“@string/path_remove“/>

The vector is generated from our ic_remove.xml file (below)

Next we declare Animated Vector Drawable files, which set both the Vector Drawable and the animations used for each "stretch" state (Add or Remove). By looking at the added or removed animation vector, we declare the target:

Animation from one state to another
Animation of rotation of the entered coefficient

android:drawable=“@drawable/ic_add“>
android:name=“@string/add“
android:animation=“@animator/add_to_remove“ />
android:name=“@string/groupAddRemove“
android:animation=“@animator/rotate_add_to_remove“ />

Then we must create each of the files mentioned for these purposes.

Changing the state of the entered coefficient

In add_to_remove.xml we use ObjectAnimator to transform between shapes using the following properties:

  • PropertyName - Animation property
  • valueFrom- Initial value for the vector path
  • valueTo- Target value for vector path
  • Duration - Duration of animation
  • interpolator - Interpolator used for animation
  • ValueType - The value type we are animating

xmlns:android=“//schemas.android.com/apk/res/android“
android:propertyName=“pathData“
android:valueFrom=“@string/path_add“
android:valueTo=“@string/path_remove“

android:interpolator=“@android:interpolator/fast_out_slow_in“
android:valueType=“pathType“ />

Rotate the form

We use a similar approach to rotate the shape using the rotation and magnitude property:

xmlns:android=“//schemas.android.com/apk/res/android“
android:propertyName=“rotation“
android:valueFrom=“-180“
android:valueTo=“0“
android:duration=“@integer/duration“
android:interpolator=“@android:interpolator/fast_out_slow_in“ />
The reverse animation (from Remove to Add) works the same, only with reverse animation values.

Our completed Animated Input Coefficient Vector looks cool, doesn't it!

And in conclusion…

Although it just scratches the surface, I hope this article has given some insight into how you can create meaningful movement in your apps. I'm looking forward to learning how I can push them further and improve the way my designs look and feel.

If you liked this article, please click “Recommend”!

I'd love to hear your thoughts on this and where you use these animations - please leave a review or tweet me!

Starting with Android 4.4, developers have an additional tool for creating animations - Transitions Framework. It was originally intended to create animations of changing the state of an application by manipulating multiple Views. With the release of Android 5.0, the set of animations available for use was expanded to correspond to the Material Design concept introduced at the same time.

Transitions Framework allows you to quickly and painlessly create various animations. Therefore, in the process of working on iFunny, it was impossible to ignore this toolkit. Readers are invited to a special case of using the Transitions API - creating animation of the transition between Activity with a “seamless” effect.

From a visual point of view, the transition animations between Activities presented in the Transitions Framework can be divided into two types: regular animations and animations with a common element. The concept of animation with a common element is demonstrated in a figure honestly stolen from developer.android.com. 1. The common elements on it are the avatar and the contact’s name.

Rice. 1. Animation of transition between Activity with common elements

But no one likes long introductions, so let’s immediately move on to the story of how animations of this type were created in the iFunny application. As a first example, consider the animation shown in Fig. 2. To use it we need Android version 5.0 and higher.


Rice. 2. Animation of the transition between Activity on the user authentication screen

From the user's point of view, there is nothing unusual here: one screen, simple animation. But, as you might have guessed, “under the hood” is the transition between two screens with one common element.

The first step to creating such a transition is, oddly enough, selecting this very element and determining its location in the layout of both Activities. After this, you need to add the android:transitionName attribute to the description of each View that displays the selected element, and also assign them an android:id if it is missing.

In our case, these are ordinary ImageViews of the following form:

There are two important points worth noting here. Firstly, both ImageViews need to be set to the same transitionName, which is logical. Secondly, since we are using ImageView, their contents must be the same, since using two different resources can lead to unexpected consequences (at least to the blinking of the animated View at the beginning and end of the animation).

In the second step, you need to add options for the launched (second) Activity, indicating that an animation should be launched when it starts.

Note. By “second” we mean the launched Activity, the transition to which must be carried out, and by “first” we mean the launching Activity.

This is done as follows:

Bundle bundle = null; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) ( View v = activity.findViewById(R.id.auth_logo); if (v != null) ( ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity , v, activity.getString(R.string.email_auth_transition)); bundle = options.toBundle(); ) ) Intent intent = new Intent(activity, SecondActivity.class); if (bundle == null) ( activity.startActivity(intent); ) else ( activity.startActivity(intent, bundle); )

In the following listing:

  • R.id.auth_logo - ImageView from the first Activity, used in animation;
  • activity - first Activity;
  • R.string.email_auth_transition - a label previously left in the layout of both ImageViews;
  • SecondActivity.class - second Activity.

And now the attentive reader may be perplexed: in the introduction we talked about using API level 19, in the example there was API level 21, and in the listing above there is a restriction on API level 22. Unfortunately, when writing the code it turned out that transition animations with a common element may behave incorrectly on phones with API level 21. This manifests itself in the form of animation slowdowns in general and artifacts on the animated View in particular. If you are already familiar with the topic, know the reasons for this behavior and/or ways to solve the problem described, tell us about it in the comments.

At the third step, it is necessary to describe the transition animation, i.e. indicate the path traversed by the animated View and the transformation of the View itself. For this we will create separate file projectName/src/main/res/transitions/email_auth_transition.xml with the following content:

A little theory. The transitionSet tag is intended to describe several transformations applied to the animated View at once. The transitionOrdering parameter controls the order in which these transformations are applied. In our case, they are applied simultaneously. There are several types of pre-built transformations provided in the Transitions Framework. WITH full list can be found on this page. We will focus on two specific ones: changeBounds and changeImageTransform.

The first one is for transforming the size of the View. The second works only with ImageView and, in conjunction with the first, allows you to change not only the size, but also the shape of the ImageView. Using the transformation data, we obtain the output animation of changing the image size, shown in Fig. 2. If you do not specify the type of movement of the animated View, then it will move along the shortest path. More interesting way We will consider movement in the second example.

The last step in creating an animation is to declare it in the themes of both Activities. To do this, edit the description of the themes as follows (or create new ones in the projectName/src/main/res/values-v22/theme.xml folder):

  • android:windowActivityTransitions enables transition animations;
  • android:windowSharedElementEnterTransition points to a file describing the animation of the transition from the first Activity to the second;
  • android:windowSharedElementExitTransition points to a file describing the transition animation when returning from the second Activity to the first.

It should be noted that for OS versions below 5.1 it is necessary to create themes with identical styles in order to avoid the quite expected consequences of the application crashing. For example, let's put them in the file projectName/src/main/res/values/theme.xml: