box

box

  • blog
  • classification
  • The label
  • Friends of the chain
  • about
  • RSS
  • search

The article directories

  1. preface
  2. Implementation scheme
    1. Android 5.0 and above implementation
      1. Establish a contract
      2. Call ActivityOptionsCompat
      3. finishAfterTransition
    2. Compatible with full version implementation
      1. The principle of
      2. Gets the parameters associated with the control
        1. Matrix
        2. getLocationOnScreen
        3. Translation offset
      3. Enter the animation
      4. Exit the animation
      5. Interface transition animation
  3. harvest
    1. Introduction of depend on
    2. Implement interface
    3. In response to the interface
  4. Wen treasure

Android shared animation compatible implementation

2017.07.16
idisfkj
animation

3, Cease to struggle and you cease to live

preface

Looking at the previous entries, the most recent entry was written on March 12th, July 16th today. Unconsciously, I have not sat in front of the computer for 4 months to think seriously and calm down to make some conclusions. With all the excitement surrounding King of Glory, here’s what I learned about shared animation on Android.

Implementation scheme

I’m going to assume that you’re all familiar with the effects of shared animations, which are simply panning and zooming from one screen to another. In terms of implementation, there are different approaches for different Versions of Android system. For Android 5.0 (LOLLIPOP API 21) above the system, it is relatively easy to implement a lot, just do some contracts and call the system API. However, there are models below Android 5.0 in the market, and we can’t ignore them. Therefore, in order to better accommodate models of both versions and to make user experience consistent, we must realize the requirement of shared animation by ourselves.

Android 5.0 and above implementation

In order to satisfy part of the Android 5.0 only consider the above implementation of friends, I also here on the system call method for a simple example. I have summed it up, mainly divided into three steps.

Establish a contract

In order to click the control share in the first interface to jump to the control of another interface, it is necessary to bind the two shared controls, that is, to enable the system to find the corresponding effective control. And to do that, the system gives us a way

           
1
           
public final void setTransitionName(String transitionName)

This is a method in the View that takes one argument, which is a contract name of type string. That is, two controls that need to be shared on the two interfaces are set with the same name.

           
1
2
           
public static final String TRANSITION_NAME_SHARE = "share";
imageView.setTransitionName(TRANSITION_NAME_SHARE);

This is set dynamically in code and can be set in an XML file

           
1
           
android:transitionName="share"

The only caveat is that the names must be the same

Call ActivityOptionsCompat

With the contract established above, you can go straight to the subject – start shared animation. To jump to the interface, create the Intent and call the startActivity method as usual. You just pass a Bundle parameter when you call startActivity. Obtain this parameter from ActivityOptionsCompat.

           
1
           
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, imageView, TRANSITION_NAME_SHARE);

Talking about the parameters, the first Activity, the second View to share, and the third contract name. Pass in when the jump is finally turned on.

           
1
           
startActivity(intent, compat.toBundle());

finishAfterTransition

Use the above code to see the jump to enable the shared animation, of course, on Android 5.0 and above mobile phones. The above is only done on, for exit, the implementation is also very simple, just call the following code at exit time

           
1
           
finishAfterTransition();

This is the method of the Activity. So you can call it directly from the exit screen. It is recommended that you override the onBackPressed method to call from there.

Review the code above, which is less than 10 lines. So for those of you who only support a higher version of the system, it’s really cool. No picture no truth, guest officer look at the picture.


Compatible with full version implementation

I believe that the guest officer psychology has been reading here is very happy and relaxed, I need to remind the guests below, should mention a bit of attention to look at the following wonderful content.

The principle of

Based on the above implementation, we look at the above renderings, the so-called picture is worth a thousand words, we come together to analyze the implementation principle of the renderings. First of all, we can see two obvious effects through the renderings:

  • The interface background is transitioned to another interface in a transparent gradient.
  • The control is zoomed in and panned from the first interface to the second interface.

From the point of view of the above, the control of animation implementation is the most important. The specific implementation process is: the second interface transparent start, at the same time the second interface control zoom translation to the first interface control position, and then zoom translation to the second interface original position. This enables a higher version of the shared animation. In order to achieve accurate amplification and translation animation, it is natural to obtain the corresponding control parameter information. So we are in the realization of control magnification animation, here we must get the width and height of the two interface control and the width and height of the picture in the control. And then figure out how much you need to scale.

Please note that here I am on the picture control for shared animation, if it is a simple TextView and other controls just need to obtain the width and height of the control, I believe that the guest officers see the following implementation scheme can also quickly deal with other control types.

Some visitors may have questions, why to obtain the width and height of the picture, the width and height of the picture is not equal to the width and height of the control? Yes, this is true in most cases, but sometimes the width and height of a control does not necessarily equal the width and height of the image, such as images in large view mode. If you use the width and height of the control to calculate the scale, naturally not the desired effect, the effect of picture scaling is bound to be inaccurate. In fact, the essence is that we want to leave the control, focus on the essence – picture effect.

After saying so much, the guest officers may get a little impatient and start to show me the code.

Gets the parameters associated with the control

The width and height of the control is not covered here. Let’s focus on the actual width and height of the image displayed in the control. Look at the following code:

           
1
2
3
4
5
6
7
8
9
10
11
12
13
           
public void convertOriginalInfo(ImageView oriView) {
if (oriView == null || oriView.getDrawable() == null) {
throw new NullPointerException("original ImageView or ImageView drawable must not null");
}
//get original ImageView info
oriView.getImageMatrix().getValues(mOriginalValues);
Rect oriRect = oriView.getDrawable().getBounds();
mOriginalWidth = (int) (oriRect.width() * mOriginalValues[Matrix.MSCALE_X]);
mOriginalHeight = (int) (oriRect.height() * mOriginalValues[Matrix.MSCALE_Y]);
mOriginalViewWidth = oriView.getWidth();
mOriginalViewHeight = oriView.getHeight();
oriView.getLocationOnScreen(mOriginalLocation);
}

Matrix

There is a knowledge point here, each picture has a corresponding Matrix, which represents a 3*3 Matrix, which contains the relevant information of the picture, such as zooming and translation.

This is the ImageMatrix in the ImageVIew, not the Matrix in the View, and you can Google the Matrix information

The Matrix getValues method converts the 3*3 rectangle value into a float array of mOriginalValues of size 9. In this way, we use matrix. MSCALE_X and matrix. MSCALE_Y to obtain the x and Y scale of the image respectively. The ImageView’s getDrawable. GetBounds method retrieves the original image information. Finally, multiply by the scaling factor to get what we want.

           
1
2
3
           
//calculator scale
mScaleX = (float) mOriginalWidth / mTargetWidth;
mScaleY = (float) mOriginalHeight / mTargetHeight;

Since Matrix is mentioned, its two values, matrix. MTRANS_X and matrix. MTRANS_Y respectively represent the translation size of the picture. Similar to the slide, pan, zoom and exit effect of large picture browsing in wechat moments, these two values can be used to obtain the translation amount of the picture in the zoom process.

getLocationOnScreen

This method can directly obtain the control of the upper left corner of the screen coordinate position. Returns an array of size 2. With this method we can easily obtain the control of the center coordinates.

           
1
2
3
           
//calculator pivot position
mPivotX = mTargetLocation[0] + mTargetValues[Matrix.MTRANS_X] + mTargetWidth / 2;
mPivotY = mTargetLocation[1] + mTargetValues[Matrix.MTRANS_Y] + mTargetHeight / 2;

Where mTargetLocation[0] represents the control’s x coordinate position on the screen, and mTargetLocation[1] represents the control’s Y coordinate position on the screen.

Subsequent zooming and panning animation needs to determine the center position, because to achieve the effect of zooming and panning on the picture, so to get the exact center position of the picture, the default is the center of the control

Translation offset

           
1
2
3
4
           
mCenterOffsetX = (int) (mOriginalLocation[0] + mOriginalValues[Matrix.MTRANS_X] + mOriginalViewWidth / 2
- mTargetLocation[0] - mTargetValues[Matrix.MTRANS_X] - mTargetViewWidth / 2);
mCenterOffsetY = (int) (mOriginalLocation[1] + mOriginalValues[Matrix.MTRANS_Y] + mOriginalViewHeight / 2
- CommonUtils.getStatusBarHeight(context) - mTargetLocation[1] - mTargetValues[Matrix.MTRANS_Y] - mTargetViewHeight / 2);

After the above explanation, the guest officers of the translation of the calculation should not be difficult to understand. The core is to calculate the offset of the center position.

Enter the animation

First of all to confirm the call time of the control animation, must be called when the control is drawn, only in this way can the earliest access to control related information, for animation preparation. We can register addOnPreDrawListener to listen to the drawing of the control.

           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
           
public FKJShareElement convert(final ImageView tarView) {
if (mInfo == null) {
throw new NullPointerException("ShareElementInfo must not null");
}
tarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
tarView.getViewTreeObserver().removeOnPreDrawListener(this);
mInfo.convertTargetInfo(tarView, mContext);
//init
if (mEnter) {
tarView.setPivotX(mInfo.getPivotX());
tarView.setPivotY(mInfo.getPivotY());
tarView.setTranslationX(mInfo.getCenterOffsetX());
tarView.setTranslationY(mInfo.getCenterOffsetY());
tarView.setScaleX(mInfo.getScaleX());
tarView.setScaleY(mInfo.getScaleY());
mAnimator = tarView.animate();
start();
startBackgroundAlphaAnimation(mBgView, new ColorDrawable(ContextCompat.getColor(mContext, R.color.fkj_white)));
}
return true;
}
});
return this;
}

For entering the animation, it was pointed out in the previous principle analysis that the control of the second interface should be scaled to the position of the first interface. So we will directly scale and pan the control first, using the View’s setTranslationX method and so on. Method mInfo saves information about the image retrieved above. The actual animation execution is called in start. The purpose is to perform a restore animation of the control.

           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
           
private void start() {
mAnimator.setDuration(mDuration)
. ScaleX (1.0 f)
ScaleY (1.0 f)
.translationX(0)
.translationY(0);
if (mListener ! = null) {
mAnimator.setListener(mListener);
}
if (mInterpolator ! = null) {
mAnimator.setInterpolator(mInterpolator);
}
mAnimator.start();
}

Exit the animation

           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
           
public void startExitAnimator() {
mEnter = false;
mAnimator.setDuration(mDuration)
.scaleX(mInfo.getScaleX())
.scaleY(mInfo.getScaleY())
.translationX(mInfo.getCenterOffsetX())
.translationY(mInfo.getCenterOffsetY());
if (mListener ! = null) {
mAnimator.setListener(mListener);
}
if (mInterpolator ! = null) {
mAnimator.setInterpolator(mInterpolator);
}
mAnimator.start();
startBackgroundAlphaAnimation(mBgView, new ColorDrawable(ContextCompat.getColor(mContext, R.color.fkj_white)), 255, 0);
}

Exiting the animation is relatively easy by zooming and panning the second screen control to the position of the first screen control.

Interface transition animation

Call in to enter and exit the animation startBackgroundAlphaAnimation method, the effect of this method is to transparent gradient interface. The principle is also simple, we only need to the second interface background View background gradient, the specific implementation is as follows:

           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
           
private void startBackgroundAlphaAnimation(final View bgView, final ColorDrawable colorDrawable, int... value) {
if (bgView == null)
return;
if (value == null || value.length == 0) {
value = new int[]{0, 255};
}
ObjectAnimator animator = ObjectAnimator.ofInt(colorDrawable, "alpha", value);
animator.setDuration(mDuration);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
bgView.setBackground(colorDrawable);
}
});
animator.start();
}

This is just a simple call to a transparent animation, but you will find it wrong to call it directly because you will also need to set the Activity theme to a transparent effect. Just set Android :windowBackgroun to transparent

harvest

I do not know how many visitors to see here, thank you for your support. Finally, the two implementation methods together with flexible call, in Android 5.0 above call system method, Android 5.0 below call encapsulated method. The steps are as follows:

Introduction of depend on

           
1
2
3
           
dependencies {
The compile 'com. Idisfkj. Share: sharelibrary: 1.0.0'
}

Implement interface

           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
           
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ShareElementActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
imageView.setTransitionName(TRANSITION_NAME_SHARE);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, imageView, TRANSITION_NAME_SHARE);
startActivity(intent, compat.toBundle());
} else {
ShareElementInfo info = new ShareElementInfo();
info.convertOriginalInfo(imageView);
intent.putExtra(EXTRA_SHARE_ELEMENT_INFO, info);
startActivity(intent);
overridePendingTransition(0, 0);
}
}
});

In response to the interface

           
1
2
3
4
5
6
7
8
9
10
           
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mImageView.setTransitionName(MainActivity.TRANSITION_NAME_SHARE);
} else {
ShareElementInfo info = getIntent().getExtras().getParcelable(MainActivity.EXTRA_SHARE_ELEMENT_INFO);
mShareElement = new FKJShareElement(info, this, mImageView.getRootView());
mShareElement.convert(mImageView)
.setDuration(ANIMATOR_DURATION)
.setInterpolator(new LinearInterpolator())
.startEnterAnimator();
}
           
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
           
@Override
public void onBackPressed() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAfterTransition();
} else {
mShareElement.convert(mImageView)
.setDuration(ANIMATOR_DURATION)
.setInterpolator(new LinearInterpolator())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finish();
overridePendingTransition(0, 0);
}
})
.startExitAnimator();
}
}

Wen treasure



The demo address

There will be continuous updates in the future. If guests are still interested in this, you can follow my blog or Github. Thank you for your support.