I don’t know if you’ve noticed, but the Android version of Nuggets has this little animation: Clicking on an author’s avatar takes you to the author’s details page, and the author’s avatar animates from the current screen to the details page.
Lack of knowledge limited my vision, really can not think how to achieve this?
While writing an article on animation recently, I found the answer online: “Transition from Activity to shared elements in animation”.
The original intention of this article is to eliminate illiteracy with everyone. If it is useful to you, “welcome to like”, so that more partners learn more knowledge. Small animation, hidden huge knowledge; No wonder the interview builds the rocket, the job turns the screw, this is the knowledge reserve, although may never use.
【 “Series of good articles recommended” 】
Android property animation, read this article enough
Android vector animation: each person will get a gold digger yellow cart
1. Transition animation for Activity switching
The Activity transition animation includes the “Enter transition” and “Exit transition” and “Shared Element Transition” animations, which also only support Android 5.0+ version.
1) Shared element transition animation
Shared element transitions refer to how views shared by two activities transition between the two activities. For example, in the Gif image above, the shared view is the ImageView.
Shared elements can also have one element and multiple elements.
Define shared element transition effects “steps” as follows:
- In the two
Activity
Define two views of the same type; - Give two
View
Set the sametransitionName
Properties; - through
ActivityOptions.makeSceneTransitionAnimation()
Function to generateBundle
Object; startActivity()
You passbundle
Object.
“Chestnut explanation, clear and easy to understand:“
- Respectively in
activity_first.xml
andactivity_second.xml
Layout file definitionImageView
Component and willtransitionName
Attribute is set toactivityTransform
.
<! --activity_first.xml file contents -->
<? xml version="1.0" encoding="utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="@color/white" android:orientation="vertical"> <ImageView android:id="@+id/ivImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_one" android:transitionName="activityTransform" /> <TextView android:id="@+id/tvText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:gravity="center" android:text="I'm the first Activity" android:textColor="@color/c_333" android:textSize="18sp" /> </LinearLayout> <! -- Activity_second. XML file contents --><? xml version="1.0" encoding="utf-8"? ><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/ivImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:adjustViewBounds="true" android:src="@mipmap/ic_one" android:transitionName="activityTransform" /> <TextView android:id="@+id/tvText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/ivImage" android:layout_marginBottom="10dp" android:gravity="center" android:text="I'm the second Activity" android:textColor="@color/c_333" android:textSize="18sp" /> </RelativeLayout> Copy the code
“preview“ activityTransform
Properties can also be set through code.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ivImage.transitionName="activityTransform"
}
Copy the code
- in
FirstActivity
To give inImageView
Set the click event to jump to the second Activity.
ivImage.setOnClickListener {
if(build.version.sdk_int >= build.version_codes.lollipop) {// Determine the Android VERSION val bundle =
ActivityOptions.makeSceneTransitionAnimation(this, ivImage, "activityTransform")
.toBundle()
startActivity(Intent(this, SecondActivity::class.java), bundle) } else { startActivity(Intent(this, SecondActivity::class.java)) } } Copy the code
In the code, first determine whether the current Android version is greater than or equal to Android 5.0. If it is greater than or equal to Android 5.0, set the shared element animation. If it is less than 5.0, start the second Activity normally.
Through ActivityOptions. MakeSceneTransitionAnimation () to create the Activity transition of some parameters, makeSceneTransitionAnimation () function first parameter as the object of the Activity; The second parameter is the shared element component, which is set to the ImageView view whose ID is ivImage; The third parameter is the value of the transitionName property, in this case activityTransform. Call AcivityOptions toBundle to wrap the Bundle object.
“Effect:“
“Multiple shared element transitions“
Multiple Shared element transition is also very simple, only need to call the makeSceneTransitionAnimation () function of another overloaded functions.
- Based on the previous XML layout, give
TextView
increasetransitionName
Properties:textTransform
.
<! --activity_first.xml file contents --><? xml version="1.0" encoding="utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white" android:orientation="vertical"> <ImageView android:id="@+id/ivImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_one" android:transitionName="activityTransform" /> <TextView android:id="@+id/tvText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:gravity="center" android:transitionName="textTransform" android:text="I'm the first Activity" android:textColor="@color/c_333" android:textSize="18sp" /> </LinearLayout> <! -- Activity_second. XML file contents --><? xml version="1.0" encoding="utf-8"? ><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/ivSecondImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:adjustViewBounds="true" android:src="@mipmap/ic_one" android:transitionName="activityTransform" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:transitionName="textTransform" android:layout_above="@id/ivSecondImage" android:layout_marginBottom="10dp" android:gravity="center" android:text="I'm the second Activity" android:textColor="@color/c_333" android:textSize="18sp" /> </RelativeLayout> Copy the code
- Build multiple
Pair
Object and passed tomakeSceneTransitionAnimation()
Function, startActivity
.
ivImage.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val imagePair=Pair<View,String>(ivImage,"activityTransform")
val textPair=Pair<View,String>(ivImage,"textTransform")
val bundle = ActivityOptions.makeSceneTransitionAnimation(this, imagePair,textPair).toBundle() startActivity(Intent(this, SecondActivity::class.java), bundle) } else { startActivity(Intent(this, SecondActivity::class.java)) } } Copy the code
This is done primarily by wrapping the values of the shared view and transitionName attribute into the Pair, and the other steps are no different from those of a shared element.
“Effect:“
“A deep pit remind“
Sometimes enter the detail page from RecyclerView interface, due to the loading delay of the detail page, there may be no effect. For example, if ImageView loads an image from the network, it might not work from A to B, but from B to A.
Solution steps:
- in
setContentView
After adding the following code, lazily loading the transition animation.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
postponeEnterTransition()
}
Copy the code
- After the shared element view is loaded, or the image is loaded, call the following code to start loading the transition animation.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
startPostponedEnterTransition()
}
Copy the code
For example, I call it after Glide loading:
Glide.with(mContext)
.asBitmap()
.load(value? .avatar ? :"")
.listener(object : RequestListener<Bitmap> {
override fun onResourceReady(resource: Bitmap? , model: Any? , target: Target<Bitmap>? , dataSource: DataSource? , isFirstResource: Boolean): Boolean {animatorCallback? .invoke()// The invoke callback starts loading the transition animation return false } override fun onLoadFailed(e: GlideException? , model: Any? , target: Target<Bitmap>? , isFirstResource: Boolean): Boolean {animatorCallback? .invoke()// The invoke callback starts loading the transition animation return false } }) .apply(RequestOptions.circleCropTransform()) .placeholder(R.mipmap.ic_default) .error(R.mipmap.ic_default) .into(authorBinding!! .ivAvatar)Copy the code
You can also consider the following code:
shareElement.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
shareElement!! .viewTreeObserver.removeOnPreDrawListener(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
animatorCallback? .invoke() } return true } }) Copy the code
Enter and exit the transition animation
In contrast to shared elements, there are transitions between activities entering and exiting, where the two activities switch animations without a shared view. Let’s look at three animation renderings: “Explosion effect” and “fade in and out effect”, “slide effect”.
- “Explosive” : Move the view into or out of the center of the scene
- “Sliding” : Move the view in and out of one edge of the scene;
- “Explosive” : Add or remove views from a scene by changing the view’s opacity;
The first screen uses Fade and the second screen uses Explode.
Both front and back interfaces are usedSlide
Slide in slide out effect.
Using Android’s existing transition framework, it is very simple to implement. The steps are as follows:
- in
Activity
theonCreate()
Method callsetContentView()
Enable window transition properties before setting;
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
Copy the code
- Create a transition effect object
Slide
,Explode
,Fade
;
val slide=Slide()
slide.slideEdge=Gravity.START
Slide. duration=300// Effect duration. Generally, the Activity switching time is very short, so it is not recommended to set it too longCopy the code
For Slide effects, you can set the slideEdge property to specify the Slide direction (Gravity.BOTTOM by default).
- Set the transition effect to the window property, set;
Exit the transition animation for the current interfacewindow.exitTransition = slide
// Enter the transition animation of the current interfacewindow.enterTransition = slide
// Re-enter the interface transition animationwindow.reenterTransition = slide Copy the code
- Call the second one
Activity
Interface, using transition effects.
startActivity(
Intent(this, SecondActivity::class.java),
ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
Copy the code
So the Activity’s OnCreate() method looks something like this.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
window.allowEnterTransitionOverlap=false
Slide().apply { duration = 300 excludeTarget(android.R.id.statusBarBackground, true) excludeTarget(android.R.id.navigationBarBackground, true) }.also { window.exitTransition = it window.enterTransition = it window.reenterTransition = it } } setContentView(R.layout.activity_first) ivContent.setOnClickListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startActivity( Intent(this, SecondActivity::class.java), ActivityOptions.makeSceneTransitionAnimation(this).toBundle() ) } } } Copy the code
The excludeTarget() method is called in the above code to exclude the status bar and navigation bar from the transition animation effect. Otherwise it will follow the animation effect, not very beautiful.
Normal, exit and enter the transition animation will have a short cross process, and the window. The allowEnterTransitionOverlap = false is prohibited cross, only to quit after the transition animation will display into the transition animations.
If you want to transition back to the first Activity after the second Activity finishes, instead of manually calling Finish (), you can call the finishAfterTransition () method.
Iii) Compatible with Android 5.0 before
What if You want toggle animations before Android 5.0?
- in
res/anim
Create the desired effect under the folder:
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@interpolator/decelerate_quad"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="@android:integer/config_longAnimTime" /> Copy the code
- In the start
Activity
After the calloverridePendingTransition()
Methods.
val intent = Intent(this, TestActivity2::class.java)
startActivity(intent)
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
Copy the code
OverridePendingTransition () method first argument for the next interface into the animation, the second parameter for the current interface from the animation.
At this point, the Activity’s transition animation is basically over. Some friends may ask, can only the Activity switch to apply the transition effect?
Second, layout change transition animation
In the last section, you learned one concept: scenarios. The display and hiding of the layout can be understood as a scene respectively, and the transition animation is to solve the stiff visual feeling brought by the scene switch. An Activity transition animation is a transition between two activities, and a layout change animation is a transition animation between views of the same Activity.
Create Scene manually
To create a scene manually, we need to create our own start and end scenes and use the existing transition effects to switch between the two scenes. By default, the current screen is the starting scene.
- For creating the start and end scenarios
xml
Layout. The start scene and the end scene need to have the same root element, as in the following codeid
forflConatent
theFrameLayout
Layout.
<? xml version="1.0" encoding="utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView android:id="@+id/tvText" android:text="Content transition animation" android:gravity="center" android:textSize="18sp" android:layout_width="match_parent" android:layout_height="50dp"/> <FrameLayout android:id="@+id/flContent" android:layout_weight="1" android:layout_width="match_parent" android:layout_height="0dp"> <include layout="@layout/layout_first_scene"/> </FrameLayout> </LinearLayout> Copy the code
Initial view, first scene, layout layout_first_scene.xml:
<? xml version="1.0" encoding="utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView android:id="@+id/tvFirst" android:textSize="18sp" android:layout_marginTop="100dp" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal|top" android:text="Thank you for reading this article." /> </LinearLayout> Copy the code
The second scenario, layout_second_scene.xml:
<? xml version="1.0" encoding="utf-8"? ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView android:textSize="18sp" android:layout_marginTop="100dp" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal|top" android:text="I am the new little dream \ N welcome everyone to like and support." /> </LinearLayout> Copy the code
- Create the start and end scenarios.
val firstScene = Scene.getSceneForLayout(flContent, R.layout.layout_first_scene, this)
val secondScene = Scene.getSceneForLayout(flContent, R.layout.layout_sencod_scene, this)
Copy the code
By default, the transition animation applies to the entire scene. If a View of the scene does not participate, it can be removed using the removeTarget() method of the transition object.
Slide(Gravity.TOP).removeTarget(tvNoJoin)
Copy the code
- When clicked, the scene transitions.
tvText.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (isFirst) {
TransitionManager.go(secondScene, Slide(Gravity.TOP))
}else{
TransitionManager.go(firstScene, Slide(Gravity.TOP)) } isFirst=! isFirst } } Copy the code
The first parameter of transitionManager.go () indicates the end of the scene, and the second parameter indicates the exit of the current scene, which is the initial scene.
“Effect:“
Ii. The system automatically creates the Scene
This kind of situation, we call TransitionManager. BeginDelayedTransition (sceneRoot) function, the system will automatically record the current sceneRoot nodes under all must carry on the view of animation as a starting node, In the next frame, the view of the animation state of all the starting scenes under the sceneRoot child node is recorded again as the end scene. This is typically used to change the properties of the View and then animate transitions, such as the width and height of the View.
Chestnuts:
Define a View with only one square, and see the animation effect by changing the width and height of the square to double.
activity_text.xml
Layout file, definitionid
forsceneRoot
Is the root node of the scene.
<? xml version="1.0" encoding="utf-8"? ><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/sceneRoot"
android:background="@color/colorPrimary"> <View android:id="@+id/vSquare" android:layout_width="100dp" android:layout_height="100dp" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:background="@color/white" /> </RelativeLayout> Copy the code
- in
TestActivity
theOnCreate
Method to set the width and height of the square to 200dp.
vSquare.setOnClickListener {
TransitionManager.beginDelayedTransition(sceneRoot)
vSquare.layoutParams.apply {
width = dp2px(200f, this@TestActivity)
height = dp2px(200f, this@TestActivity)
}.also { vSquare.layoutParams = it } } Copy the code
“Effect:“
Three, transition animation effect
The above animation effects are all built in the system. What are the specific animation effects, or do you support customization?
The Transition effects classes inherit from the Transition class, which holds information about the scene cutting animation. The main purpose of subclasses is to capture property values (such as start and end values) and how to play the animation. It can also be seen from here that transition animation is also an extension and application of attribute animation.
I. Built-in transition animation
The system supports any transition from an extended Visibility class as an entry or exit transition. The built-in classes include Explode, Slide, and Fade. Shared element transitions are supported by:
changeScroll
Adds an animation effect for the target view slidechangeBounds
Animates changes in the layout boundaries of the target viewchangeClipBounds
Animates changes in clipping boundaries for the target viewchangeTransform
Animates the zoom and rotation changes for the target viewchangeImageTransform
Animates changes in the size and scaling of the target image
Code examples:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TransitionSet().apply {
addTransition(ChangeImageTransform())
addTransition(ChangeBounds())
addTransition(Fade(Fade.MODE_IN))
}.also { window.sharedElementEnterTransition=it } } Copy the code
The TransitionSet object is a collection of animations that can organize multiple transition effects.
This can also be done with an XML layout, created in the RES/Transition folder:
<? xml version="1.0" encoding="utf-8"? ><transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:transitionOrdering="together">
<changeImageTransform />
<changeBounds /> <fade /> </transitionSet> Copy the code
TransitionSet and fade… Some of the attributes mentioned in this article are similar and will not be repeated here.
Code call:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TransitionInflater.from(this).inflateTransition(R.transition.transition_set).also {
window.sharedElementEnterTransition=it
}
}
Copy the code
“Effect:“
Inheritance can be used when the existing transition effects do not meet daily requirementsTransition
Customize your own animation effects.
2) Custom transition animation
The subclass inherits the Transition class and overwrites its three methods.
class MyTransition : Transition() {
override fun captureStartValues(transitionValues: TransitionValues?) {}
override fun captureEndValues(transitionValues: TransitionValues?) {}
override fun createAnimator( sceneRoot: ViewGroup? .startValues: TransitionValues? . endValues: TransitionValues? ): Animator { return super.createAnimator(sceneRoot, startValues, endValues) } } Copy the code
The captureStartValues() and captureEndValues() methods must be implemented to capture the start and end values of the animation, while the createAnimator() method is used to create custom animations.
The TransitionValues parameter can be understood to store some property values of the View, and the sceneRoot parameter is the root View.
Custom Transition animation for Android
Ok, so much for transition animation
Reference article:
Official document
Cool Activity switch animation to create a better user experience
My lot
“[code word is not easy, click a like, good view in the future]“
This article is formatted using MDNICE