前言
很早的时候算是看过一些关于Android 5.0的新特性,但是当时也就是记录一下用法,也没后续去看,前段时间看到一个动画切换效果,我还傻乎乎的去写,后来发现居然是Android自带的,找了些资料算是补习了!
本篇包括Transition
、Shared Element
、Circular Reveal
的内容。
Transition
Transition是Android 5.0新加入的过渡动画效果,包括Explode
、Slide
和Fade
Explode | Slide | Fade |
---|---|---|
从中心移入或移出 | 从边缘移入或移出 | 调整透明度产生渐变 |
在讲解这些功能之前我们需要知道几个方法
setEnterTransition(Transition transition)
设置当前页面进入的过渡动画setReturnTransition(Transition transition)
设置当前页面返回的过渡动画setExitTransition(Transition transition)
设置当前页面退出的过渡动画setReenterTransition(Transition transition)
设置当前页面重启的过渡动画
这几个方法是设置过渡动画的时候需要注意的。
Explode
我们这边需要做一个A -> B 然后B进行Explode过渡动画的效果设置。
逻辑如下:
A页面进入B页面 B页面需要有过渡动画 所以只要对B页面设置setEnterTransition
方法和setReturnTransition
方法即
可。
调用方法也要传入对应的代码才有效
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Explode");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
当直接使用ActivityOptions.makeSceneTransitionAnimation(activity)
的时候在低于5.0以下的时候软件会直接崩溃,所以可以使用ActivityOptionsCompat
来兼容低版本,但是这样设置只是让低版本不会崩溃,并不会有过渡动画效果,后面我会用ActivityOptionsCompat
做演示。
在接受到的页面加入以下判断
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
case "Explode":
Explode explode = new Explode();
setDuration(explode);
getWindow().setEnterTransition(explode);
break;
}
}
至此就实现了功能,不设置setReturnTransition
系统会自动调用进入的反向动画来实现!
Slide
和上面的Explode一样我们只要加入以下代码
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
在需要展示的页面加入判断,不过由于Slide是需要设置到底从哪个方向的所以可以加入方向
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Slide":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
setDuration(slide);
getWindow().setEnterTransition(slide);
break;
}
}
Fade
这个过渡也没有特别的直接调用以下代码就可以了
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Fade");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
设置展示
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Fade":
Fade fade = new Fade();
setDuration(fade);
getWindow().setEnterTransition(fade);
break;
}
}
补充setReturnTransition
至此可以发现使用特别简单。下面我们设置setReturnTransition
方法来实现进入和退出不一样的效果
可以看到进入是一个有回弹效果的,并且退出的方向也和进入的相同了!
打开页面的代码也没有特别的变化
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide2");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
展示的代码
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
...
case "Slide2":
Slide slide2 = new Slide();
slide2.setSlideEdge(Gravity.RIGHT);
setDuration(slide2);
//回弹效果
slide2.setInterpolator(new BounceInterpolator());
getWindow().setEnterTransition(slide2);
break;
}
}
前面的代码都没有特别的变化!
只是在最后返回的时候对setReturnTransition
进行了修改。
@Override
public void onBackPressed() {
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 如果没有 return transition 被定义,将使用反进入的动画
switch (transition) {
case "Slide2":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
setDuration(slide);
getWindow().setReturnTransition(slide);
break;
}
//一定要调用finishAfterTransition
finishAfterTransition();
} else {
super.onBackPressed();
}
}
这边需要注意的就是调用的关闭页面方法一定要是finishAfterTransition
才会执行过渡动画。
Shared Element
上面讲完了Transition
现在来说下Shared Element
就是共享元素。
首先我们看下效果
这就是元素共享的界面过渡动画效果,下面我们看下如何实现!
单独元素并且不传递图片
这次我们使用ActivityCompat
和 ActivityOptionsCompat
来实现 这是为了兼容低版本,让低版本不崩溃而已!效果是没办法实现!
首先布局页面代码为
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="点击单独放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
我们发现布局上面加入了一段android:transitionName="Open"
这是为了让元素进行共享的起点。
打开页面的代码如下
tv1 = findViewById(R.id.tv_1_ase);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, tv1,
ViewCompat.getTransitionName(tv1));
ActivityCompat.startActivity(activity, intent, compat.toBundle());
我们对比上面的代码会发现也是调用makeSceneTransitionAnimation
方法,不过传递的不在是一个Explode
这类对象了,而是传递了一个view 并且还传递了TransitionName
,这个可以直接写或者取!
之后是我们需要展示效果的页面
需要进行过渡动画的View布局代码
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
这样我们两边就都有了一个android:transitionName="Open"
这样进行跳转之后系统就会帮我们绑定两个View
对于展示页面我们可以不需要写任何代码!
多个元素并且不传递图片
多个元素其实也是一样的,只不过传递的view多了而已!
首先还是布局,我们写入了两个transitionName
进入前的布局
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="点击单独放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_2_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:text="点击放大2个"
android:transitionName="Open2"
app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />
进入后的布局
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:transitionName="Open2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@mipmap/homepage_banner1" />
这边各自对应了各自的android:transitionName
跳转代码
Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
我们创建多个Pair
对象在传入就可以了
元素传递图片
上面都是内部本身就有了图片,外部只是一个位置提供而已,那么能够在外部传入图片进去么。是可以的,其实效果只是告诉第二个页面进入之后你的图片需要设置什么而已,实际其实是一样的!
我们新增两个按钮布局
<TextView
android:id="@+id/tv_3_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定义图片1"
android:transitionName="Open3"
app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_4_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定义图片2"
android:transitionName="Open3"
app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />
并且在跳转页面也加入一个布局用于展示
<ImageView
android:id="@+id/imageView3"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:transitionName="Open3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
这是一个没有设置图片的View,当进入之后才设置图片
跳转代码如下
tv3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher_round);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
这次我们就需要设置图片了,所以展示页面代码如下
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
这样设置之后我们进入之后就是传递的图片了!
注:当然也可以是网络图片,不过由于网络的原因,所以建议是直接先传在跳转前的图片,进入之后等网络自己刷新新的图片就好了!
补充1 监听两个跳转页面直接的关联
有可能会做一个轮播的点击跳转的,然后内部切换之后返回回来就会发现位置明显不对或者外部的轮播图片并未改变,这个时候我们只要加入监听就好了!
逻辑如下:
A 在a图片的时候 进入 B 并且打开了a图片,这个时候B 切换了下图片变成了b图片,这个时候返回需要告诉A图片换了。
首先在A的onCreate中加入监听
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle != null) {
int id = bundle.getInt("imgId");
String url = bundle.getString("imgUrl");
Log.e("-s-", "id = " + id);
Log.e("-s-", "url = " + url);
bundle = null;
}
}
});
然后实现Activity的onActivityReenter
方法,即页面跳转回来之后判断传回来的data
@Override
public void onActivityReenter(int resultCode, Intent data) {
Log.e("-s-", "resultCode = " + resultCode);
if (resultCode == 101) {
bundle = new Bundle(data.getExtras());
}
}
至此A页面的监听写完了,再来看下B页面的传递信息
将传递信息的代码写在finishAfterTransition
方法中
@Override
public void finishAfterTransition() {
Intent data = new Intent();
data.putExtra("imgId", imgId);
data.putExtra("imgUrl", imgUrl);
setResult(101, data);
super.finishAfterTransition();
}
这样就实现了两个Activity之间的传递信息功能!
补充2 将Transition
和Shared Element
结合下
实现多种结合的过渡动画,效果如下
跳转代码
tv9.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("transition", "Slide+");
intent.putExtra("imgurl", imgs[0]);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
和上面比就是加入了一些参数而已。
实现页面代码
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
imgUrl = getIntent().getStringExtra("imgurl");
transition = getIntent().getStringExtra("transition");
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch (transition) {
case "Slide+":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
getWindow().setEnterTransition(slide);
break;
}
}
if (imgUrl != null) {
Glide.with(this).load(imgUrl).into(img3);
} else if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
就是将Transition和SharedElement简单结合而已。
Circular Reveal
这个效果是在当前页面进行一个扩散过渡。但是可以做到加入到跳转动画中!
效果如下
基本说明
先说下最基本的使用说明
其实就是调用ViewAnimationUtils.createCircularReveal
创建一个Animator
,说白了就是一个Animator
,只不过官方提供了一个扩散效果动画给你而已_(:з」∠)_
createCircularReveal
提供了5个参数View view, int centerX, int centerY, float startRadius, float endRadius
,分别代表,需要执行的动画是哪个View,起始位置的X,Y, 开始圆的半径, 结束圆的半径!
这边扩散整个布局的代码
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
效果的逻辑很简单
A 以SharedElement打开 B ,之后立马执行CircularReveal扩散整个根布局, 退出B的时候必须先执行完CircularReveal动画在关闭页面。
代码如下
跳转代码
tv10.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, CircularRevealActivity.class);
intent.putExtra("CircularReveal", "green");
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
执行效果页面代码
CircularRevealActivity.java
package com.gjn.testproject;
import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {
View root;
View v1, v2, v3;
boolean in = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circular_reveal);
in = false;
root = findViewById(R.id.root_acr);
v1 = findViewById(R.id.v1);
v2 = findViewById(R.id.v2);
v3 = findViewById(R.id.v3);
String circularReveal = getIntent().getStringExtra("CircularReveal");
if (circularReveal != null) {
switch (circularReveal) {
case "green":
root.setBackgroundResource(android.R.color.holo_green_light);
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
if (!in) {
in = true;
show(v1);
}
}
});
}
}
v1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_green_light);
show(v1);
}
});
v2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_red_dark);
show(v2);
}
});
v3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_blue_light);
show(v3);
}
});
}
@Override
public void onBackPressed() {
if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
hide(v1);
} else {
super.onBackPressed();
}
}
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
private void hide(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
root.setBackgroundColor(Color.TRANSPARENT);
finishAfterTransition();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
具体就看代码吧!其实就是做了两个动画而已!
全部代码
activity_shared_element.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.SharedElementActivity">
<TextView
android:id="@+id/tv_1_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="@android:color/holo_blue_bright"
android:gravity="center"
android:text="点击单独放大"
android:transitionName="Open"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_2_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:text="点击放大2个"
android:transitionName="Open2"
app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_3_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定义图片1"
android:transitionName="Open3"
app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />
<TextView
android:id="@+id/tv_4_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:text="自定义图片2"
android:transitionName="Open3"
app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_ase"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_3_ase" />
<TextView
android:id="@+id/tv_5_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:gravity="center"
android:text="Explode"
app:layout_constraintStart_toEndOf="@+id/tv_2_ase"
app:layout_constraintTop_toTopOf="@+id/tv_2_ase" />
<TextView
android:id="@+id/tv_6_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:gravity="center"
android:text="Slide1"
app:layout_constraintStart_toEndOf="@+id/tv_5_ase"
app:layout_constraintTop_toTopOf="@+id/tv_5_ase" />
<TextView
android:id="@+id/tv_7_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:gravity="center"
android:text="Fade"
app:layout_constraintStart_toStartOf="@+id/tv_5_ase"
app:layout_constraintTop_toTopOf="@+id/tv_4_ase" />
<TextView
android:id="@+id/tv_8_ase"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
android:gravity="center"
android:text="Slide2"
app:layout_constraintStart_toStartOf="@+id/tv_6_ase"
app:layout_constraintTop_toBottomOf="@+id/tv_6_ase" />
<TextView
android:id="@+id/tv_9_ase"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:transitionName="Open3"
android:text="Slide+SharedElements"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rv_ase" />
<TextView
android:id="@+id/tv_10_ase"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="@android:color/holo_green_light"
android:gravity="center"
android:text="CircularReveal1"
android:transitionName="Open4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_9_ase" />
</android.support.constraint.ConstraintLayout>
activity_shared.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.SharedActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="200dp"
android:transitionName="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/homepage_banner2" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:transitionName="Open2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@mipmap/homepage_banner1" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:transitionName="Open3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
activity_circular_reveal.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_acr"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gjn.testproject.CircularRevealActivity">
<TextView
android:id="@+id/v1"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="150dp"
android:transitionName="Open4"
android:background="@android:color/holo_green_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/v2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="32dp"
android:layout_marginEnd="32dp"
android:background="@android:color/holo_red_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/v3"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginEnd="24dp"
android:background="@android:color/holo_blue_light"
app:layout_constraintBottom_toBottomOf="@+id/v2"
app:layout_constraintEnd_toStartOf="@+id/v2" />
</android.support.constraint.ConstraintLayout>
SharedElementActivity.java
package com.gjn.testproject;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.gjn.baserecycleradapterlibrary.BaseRecyclerAdapter;
import com.gjn.baserecycleradapterlibrary.RecyclerViewHolder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class SharedElementActivity extends AppCompatActivity {
TextView tv1, tv2, tv3, tv4, tv5, tv6, tv7, tv8, tv9, tv10;
Activity activity;
String[] imgs = {"https://ws1.sinaimg.cn/large/0065oQSqgy1fxd7vcz86nj30qo0ybqc1.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqgy1fwyf0wr8hhj30ie0nhq6p.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fuh5fsvlqcj30sg10onjk.jpg",
"https://ws1.sinaimg.cn/large/0065oQSqly1fubd0blrbuj30ia0qp0yi.jpg"};
Bundle bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// //设置允许通过ActivityOptions.makeSceneTransitionAnimation发送或者接收Bundle
// getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
// //设置使用TransistionManager进行动画,不设置的话系统会使用一个默认的TransitionManager
// getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_shared_element);
activity = this;
tv1 = findViewById(R.id.tv_1_ase);
tv2 = findViewById(R.id.tv_2_ase);
tv3 = findViewById(R.id.tv_3_ase);
tv4 = findViewById(R.id.tv_4_ase);
tv5 = findViewById(R.id.tv_5_ase);
tv6 = findViewById(R.id.tv_6_ase);
tv7 = findViewById(R.id.tv_7_ase);
tv8 = findViewById(R.id.tv_8_ase);
tv9 = findViewById(R.id.tv_9_ase);
tv10 = findViewById(R.id.tv_10_ase);
RecyclerView RV = findViewById(R.id.rv_ase);
List<String> list = new ArrayList<>();
list.add("图片1");
list.add("图片2");
list.add("图片3");
list.add("图片4");
list.add("图片5");
list.add("图片6");
RV.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
BaseRecyclerAdapter<String> adapter = new BaseRecyclerAdapter<String>(activity, R.layout.item_ase, list) {
@Override
public void bindData(RecyclerViewHolder holder, String s, int i) {
holder.setTextViewText(R.id.tv_ia, s);
}
};
RV.setAdapter(adapter);
adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int i) {
TextView textView = view.findViewById(R.id.tv_ia);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(textView, ViewCompat.getTransitionName(textView))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgurl", imgs[i]);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
setClick();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle != null) {
int id = bundle.getInt("imgId");
String url = bundle.getString("imgUrl");
Log.e("-s-", "id = " + id);
Log.e("-s-", "url = " + url);
bundle = null;
}
}
});
}
}
@Override
public void onActivityReenter(int resultCode, Intent data) {
Log.e("-s-", "resultCode = " + resultCode);
if (resultCode == 101) {
bundle = new Bundle(data.getExtras());
}
}
private void setClick() {
tv1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity, tv1,
ViewCompat.getTransitionName(tv1)).toBundle());
}
});
tv2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher_round);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
);
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("imgid", R.mipmap.ic_launcher);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Explode");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Fade");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv8.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide2");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
}
});
tv9.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, SharedActivity.class);
intent.putExtra("transition", "Slide+");
intent.putExtra("imgurl", imgs[0]);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
tv10.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, CircularRevealActivity.class);
intent.putExtra("CircularReveal", "green");
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
);
ActivityCompat.startActivity(activity, intent, compat.toBundle());
}
});
}
}
TransitionActivity.java
package com.gjn.testproject;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.transition.Explode;
import android.transition.Fade;
import android.transition.Slide;
import android.transition.Transition;
import android.view.Gravity;
import android.view.animation.BounceInterpolator;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class TransitionActivity extends AppCompatActivity {
private String transition;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
transition = getIntent().getStringExtra("transition");
if (transition != null) {
switch (transition) {
case "Explode":
Explode explode = new Explode();
setDuration(explode);
getWindow().setEnterTransition(explode);
break;
case "Slide":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
setDuration(slide);
getWindow().setEnterTransition(slide);
break;
case "Slide2":
Slide slide2 = new Slide();
slide2.setSlideEdge(Gravity.RIGHT);
setDuration(slide2);
//回弹效果
slide2.setInterpolator(new BounceInterpolator());
getWindow().setEnterTransition(slide2);
break;
case "Fade":
Fade fade = new Fade();
setDuration(fade);
getWindow().setEnterTransition(fade);
break;
}
}
}
private void setDuration(Transition transition) {
transition.setDuration(800);
}
@Override
public void onBackPressed() {
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 如果没有 return transition 被定义,将使用反进入的动画
switch (transition) {
// case "Explode":
// Explode explode = new Explode();
// getWindow().setReturnTransition(explode);
// break;
// case "Slide":
// Slide slide = new Slide();
// slide.setSlideEdge(Gravity.RIGHT);
// getWindow().setReturnTransition(slide);
// break;
case "Slide2":
// Slide slide2 = new Slide();
// slide2.setSlideEdge(Gravity.RIGHT);
// //回弹效果
// slide2.setInterpolator(new BounceInterpolator());
// getWindow().setReturnTransition(slide2);
Slide slide = new Slide();
slide.setSlideEdge(Gravity.LEFT);
setDuration(slide);
getWindow().setReturnTransition(slide);
break;
// case "Fade":
// Fade fade = new Fade();
// getWindow().setReturnTransition(fade);
// break;
}
//一定要调用finishAfterTransition
finishAfterTransition();
} else {
super.onBackPressed();
}
}
}
SharedActivity.java
package com.gjn.testproject;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.transition.Slide;
import android.view.Gravity;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
public class SharedActivity extends AppCompatActivity {
int imgId;
String imgUrl;
String transition;
ImageView img3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared);
img3 = findViewById(R.id.imageView3);
imgId = getIntent().getIntExtra("imgid", -1);
imgUrl = getIntent().getStringExtra("imgurl");
transition = getIntent().getStringExtra("transition");
if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch (transition) {
case "Slide+":
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
getWindow().setEnterTransition(slide);
break;
}
}
if (imgUrl != null) {
Glide.with(this).load(imgUrl).into(img3);
} else if (imgId != -1) {
Glide.with(this).load(imgId).into(img3);
}
}
@Override
public void finishAfterTransition() {
Intent data = new Intent();
data.putExtra("imgId", imgId);
data.putExtra("imgUrl", imgUrl);
setResult(101, data);
super.finishAfterTransition();
}
}
CircularRevealActivity.java
package com.gjn.testproject;
import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {
View root;
View v1, v2, v3;
boolean in = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circular_reveal);
in = false;
root = findViewById(R.id.root_acr);
v1 = findViewById(R.id.v1);
v2 = findViewById(R.id.v2);
v3 = findViewById(R.id.v3);
String circularReveal = getIntent().getStringExtra("CircularReveal");
if (circularReveal != null) {
switch (circularReveal) {
case "green":
root.setBackgroundResource(android.R.color.holo_green_light);
break;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
if (!in) {
in = true;
show(v1);
}
}
});
}
}
v1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_green_light);
show(v1);
}
});
v2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_red_dark);
show(v2);
}
});
v3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
root.setBackgroundResource(android.R.color.holo_blue_light);
show(v3);
}
});
}
@Override
public void onBackPressed() {
if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
hide(v1);
} else {
super.onBackPressed();
}
}
private void show(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
private void hide(View v) {
int w = getResources().getDisplayMetrics().widthPixels;
int h = getResources().getDisplayMetrics().heightPixels;
int cx = (v.getLeft() + v.getRight()) / 2;
int cy = (v.getTop() + v.getBottom()) / 2;
float r = Math.max(w, h);
Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
root.setBackgroundColor(Color.TRANSPARENT);
finishAfterTransition();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
资料
【Transition】Android炫酷的Activity切换效果,共享元素
Activity间的跳转动画—Transition
使用Circular Reveal为你的应用添加揭露动画效果