Android共享元素场景切换动画的实现
安卓5.0系统引入了共享元素,能做出非常炫酷的场景切换效果,这让人非常兴奋同时非常蛋疼,因为低版本没法使用啊,所以今天就跟大家分享一下自己写的一个库,其实只有2个文件而已就可以兼容安卓5.0以下的版本。
重要的工具类
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.ViewTreeObserver; import java.util.ArrayList; /**
* Tool for transition between two activities
* <br/>
* Created by huzn on 2017/5/8.
*/
public class EasyTransition { public static final String EASY_TRANSITION_OPTIONS = "easy_transition_options";
public static final long DEFAULT_TRANSITION_ANIM_DURATION = 1000; /**
* Start Activity with transition options
*
* @param intent The intent to start
* @param options Transition options, using {@link EasyTransitionOptions#makeTransitionOptions(Activity, View...)}
* to build your options
*/
public static void startActivity(Intent intent, EasyTransitionOptions options) {
options.update();
intent.putParcelableArrayListExtra(EASY_TRANSITION_OPTIONS, options.getAttrs());
Activity activity = options.getActivity();
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
} /**
* Start Activity for result, with transition options
*
* @param intent The intent to start
* @param requestCode If >= 0, this code will be returned in onActivityResult() when the activity exits,
* see {@link Activity#startActivityForResult(Intent, int)}
* @param options Transition options, using {@link EasyTransitionOptions#makeTransitionOptions(Activity, View...)}
* to build your options
*/
public static void startActivityForResult(Intent intent, int requestCode, EasyTransitionOptions options) {
options.update();
intent.putParcelableArrayListExtra(EASY_TRANSITION_OPTIONS, options.getAttrs());
Activity activity = options.getActivity();
activity.startActivityForResult(intent, requestCode);
activity.overridePendingTransition(0, 0);
} /**
* Enter the Activity, invoking this method to start enter transition animations
*
* @param activity The Activity entering
* @param duration The duration of enter transition animation
* @param interpolator The TimeInterpolator of enter transition animation
* @param listener Animator listener, normally you can do your initial after animation end
*/
public static void enter(Activity activity, long duration, TimeInterpolator interpolator, Animator.AnimatorListener listener) {
Intent intent = activity.getIntent();
ArrayList<EasyTransitionOptions.ViewAttrs> attrs =
intent.getParcelableArrayListExtra(EASY_TRANSITION_OPTIONS);
runEnterAnimation(activity, attrs, duration, interpolator, listener);
} /**
* The same as {@link EasyTransition#enter(Activity, long, TimeInterpolator, Animator.AnimatorListener)}
* with no interpolator
*/
public static void enter(Activity activity, long duration, Animator.AnimatorListener listener) {
enter(activity, duration, null, listener);
} /**
* The same as {@link EasyTransition#enter(Activity, long, TimeInterpolator, Animator.AnimatorListener)}
* with default duration
*/
public static void enter(Activity activity, TimeInterpolator interpolator, Animator.AnimatorListener listener) {
enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, interpolator, listener);
} /**
* The same as {@link EasyTransition#enter(Activity, long, TimeInterpolator, Animator.AnimatorListener)}
* with default duration and no interpolator
*/
public static void enter(Activity activity, Animator.AnimatorListener listener) {
enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, null, listener);
} /**
* The same as {@link EasyTransition#enter(Activity, long, TimeInterpolator, Animator.AnimatorListener)}
* with default duration, no interpolator and no listener
*/
public static void enter(Activity activity) {
enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, null, null);
} private static void runEnterAnimation(Activity activity,
ArrayList<EasyTransitionOptions.ViewAttrs> attrs,
final long duration,
final TimeInterpolator interpolator,
final Animator.AnimatorListener listener) {
if (null == attrs || attrs.size() == 0)
return; for (final EasyTransitionOptions.ViewAttrs attr : attrs) {
final View view = activity.findViewById(attr.id); if (null == view)
continue; view.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this); int[] location = new int[2];
view.getLocationOnScreen(location);
view.setPivotX(0);
view.setPivotY(0);
view.setScaleX(attr.width / view.getWidth());
view.setScaleY(attr.height / view.getHeight());
view.setTranslationX(attr.startX - location[0]); // xDelta
view.setTranslationY(attr.startY - location[1]); // yDelta view.animate()
.scaleX(1)
.scaleY(1)
.translationX(0)
.translationY(0)
.setDuration(duration)
.setInterpolator(interpolator)
.setListener(listener);
return true;
}
});
}
} /**
* Exit the Activity, invoke this method to start exit transition animation,
* the shared views must have same ids, or it will throws NullPointerException
*
* @param activity The Activity Exiting
* @param interpolator The TimeInterpolator of exit transition animation
* @param duration The duration of exit transition animation
* @throws NullPointerException throws if shared views not found in The Activity Exiting
*/
public static void exit(Activity activity, long duration, TimeInterpolator interpolator) {
Intent intent = activity.getIntent();
ArrayList<EasyTransitionOptions.ViewAttrs> attrs = intent.getParcelableArrayListExtra(EASY_TRANSITION_OPTIONS);
runExitAnimation(activity, attrs, duration, interpolator);
} /**
* The same as {@link EasyTransition#exit(Activity, long, TimeInterpolator)}
* with default duration
*/
public static void exit(Activity activity, TimeInterpolator interpolator) {
exit(activity, DEFAULT_TRANSITION_ANIM_DURATION, interpolator);
} /**
* The same as {@link EasyTransition#exit(Activity, long, TimeInterpolator)}
* with no interpolator
*/
public static void exit(Activity activity, long duration) {
exit(activity, duration, null);
} /**
* The same as {@link EasyTransition#exit(Activity, long, TimeInterpolator)}
* with default duration and no interpolator
*/
public static void exit(Activity activity) {
exit(activity, DEFAULT_TRANSITION_ANIM_DURATION, null);
} private static void runExitAnimation(final Activity activity,
ArrayList<EasyTransitionOptions.ViewAttrs> attrs,
long duration,
TimeInterpolator interpolator) {
if (null == attrs || attrs.size() == 0)
return; for (final EasyTransitionOptions.ViewAttrs attr : attrs) {
View view = activity.findViewById(attr.id);
int[] location = new int[2];
view.getLocationOnScreen(location);
view.setPivotX(0);
view.setPivotY(0); view.animate()
.scaleX(attr.width / view.getWidth())
.scaleY(attr.height / view.getHeight())
.translationX(attr.startX - location[0])
.translationY(attr.startY - location[1])
.setInterpolator(interpolator)
.setDuration(duration);
} activity.findViewById(attrs.get(0).id).postDelayed(new Runnable() {
@Override
public void run() {
activity.finish();
activity.overridePendingTransition(0, 0);
}
}, duration);
}
}
import android.app.Activity;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View; import java.util.ArrayList; /**
* Transition options, using {@link EasyTransitionOptions#makeTransitionOptions(Activity, View...)}
* to build your options
* <br/>
* Created by huzn on 2017/5/8.
*/
public class EasyTransitionOptions { private Activity activity;
private View[] views;
private ArrayList<ViewAttrs> attrs; public EasyTransitionOptions(Activity activity, View[] views) {
this.activity = activity;
this.views = views;
} /**
* Make options for transition
*
* @param activity The activity who contains shared views
* @param views Shared views, which must contains same id between two activities
* @return A new transition options that will be used to build our transition animations
*/
public static EasyTransitionOptions makeTransitionOptions(Activity activity, View... views) {
return new EasyTransitionOptions(activity, views);
} public void update() {
if (null == views)
return; attrs = new ArrayList<>();
for (View v : views) {
int[] location = new int[2];
v.getLocationOnScreen(location);
attrs.add(new ViewAttrs(
v.getId(),
location[0],
location[1],
v.getWidth(),
v.getHeight()
));
}
} public Activity getActivity() {
return activity;
} public ArrayList<ViewAttrs> getAttrs() {
return attrs;
} public static class ViewAttrs implements Parcelable {
public int id;
public float startX;
public float startY;
public float width;
public float height; public ViewAttrs(int id, float startX, float startY, float width, float height) {
this.id = id;
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
} // Parcelable
@Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeFloat(this.startX);
dest.writeFloat(this.startY);
dest.writeFloat(this.width);
dest.writeFloat(this.height);
} protected ViewAttrs(Parcel in) {
this.id = in.readInt();
this.startX = in.readFloat();
this.startY = in.readFloat();
this.width = in.readFloat();
this.height = in.readFloat();
} public static final Parcelable.Creator<ViewAttrs> CREATOR = new Parcelable.Creator<ViewAttrs>() {
@Override
public ViewAttrs createFromParcel(Parcel source) {
return new ViewAttrs(source);
} @Override
public ViewAttrs[] newArray(int size) {
return new ViewAttrs[size];
}
};
}
}
场景使用:
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private ArrayList<String> list; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add("name" + i);
}
ListView listView = (ListView) findViewById(R.id.lv);
listView.setAdapter(new MyAdapter());
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// ready for intent
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("name", list.get(position)); // ready for transition options
EasyTransitionOptions options =
EasyTransitionOptions.makeTransitionOptions(
MainActivity.this,
view.findViewById(R.id.iv_icon),
view.findViewById(R.id.tv_name),
findViewById(R.id.v_top_card)); // start transition
EasyTransition.startActivity(intent, options);
}
});
} private class MyAdapter extends BaseAdapter { @Override
public int getCount() {
int count = 0;
if (null != list)
count = list.size();
return count;
} @Override
public Object getItem(int position) {
return list.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (null != convertView) {
view = convertView;
} else {
view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_main_list, null, false);
}
TextView tvName = (TextView) view.findViewById(R.id.tv_name);
tvName.setText(list.get(position));
return view;
}
}
}
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView; public class DetailActivity extends AppCompatActivity { private LinearLayout layoutAbout;
private ImageView ivAdd;
private boolean finishEnter; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail); // pre init some views and data
initViews(); // if re-initialized, do not play any anim
long transitionDuration = 800;
if (null != savedInstanceState)
transitionDuration = 0; // transition enter
finishEnter = false;
EasyTransition.enter(
this,
transitionDuration,
new DecelerateInterpolator(),
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// init other views after transition anim
finishEnter = true;
initOtherViews();
}
});
} private void initViews() {
TextView tvName = (TextView) findViewById(R.id.tv_name);
tvName.setText(getIntent().getStringExtra("name"));
} private void initOtherViews() {
layoutAbout = (LinearLayout) findViewById(R.id.layout_about);
layoutAbout.setVisibility(View.VISIBLE);
layoutAbout.setAlpha(0);
layoutAbout.setTranslationY(-30);
layoutAbout.animate()
.setDuration(300)
.alpha(1)
.translationY(0); ivAdd = (ImageView) findViewById(R.id.iv_add);
ivAdd.setVisibility(View.VISIBLE);
ivAdd.setScaleX(0);
ivAdd.setScaleY(0);
ivAdd.animate()
.setDuration(200)
.scaleX(1)
.scaleY(1);
} @Override
public void onBackPressed() {
if (finishEnter) {
finishEnter = false;
startBackAnim();
}
} private void startBackAnim() {
// forbidden scrolling
ScrollView sv = (ScrollView) findViewById(R.id.sv);
sv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
}); // start our anim
ivAdd.animate()
.setDuration(200)
.scaleX(0)
.scaleY(0); layoutAbout.animate()
.setDuration(300)
.alpha(0)
.translationY(-30)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// transition exit after our anim
EasyTransition.exit(DetailActivity.this, 800, new DecelerateInterpolator());
}
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hzn.easytransition.MainActivity"
> <TextView
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@color/colorPrimary"
android:gravity="center_vertical"
android:paddingLeft="16sp"
android:text="TITLE"
android:textColor="#ffffff"
android:textSize="25sp"
/> <View
android:id="@+id/v_top_card"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_below="@id/title_bar"
android:background="@color/colorPrimary"
/> <ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/title_bar"
/>
</RelativeLayout>
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hzn.easytransition.MainActivity"
> <TextView
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@color/colorPrimary"
android:gravity="center_vertical"
android:paddingLeft="16sp"
android:text="TITLE"
android:textColor="#ffffff"
android:textSize="25sp"
/> <View
android:id="@+id/v_top_card"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_below="@+id/title_bar"
android:background="@color/colorPrimary"
/> <ImageView
android:id="@+id/iv_icon"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:src="@mipmap/avatar_male"
/> <TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_icon"
android:layout_centerHorizontal="true"
android:layout_marginLeft="10dp"
android:textColor="#5b5b5b"
android:textSize="50sp"
/> <ScrollView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tv_name"
android:overScrollMode="never"
android:scrollbars="none"
> <LinearLayout
android:id="@+id/layout_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical"
android:paddingBottom="40dp"
android:paddingTop="20dp"
android:visibility="invisible"
> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@mipmap/icon_phone"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:text="151-2121-2121"
android:textColor="#446880"
android:textSize="16sp"
/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:drawableLeft="@mipmap/icon_email"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:text="243666666@gmail.com"
android:textColor="#446880"
android:textSize="16sp"
/> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:drawableLeft="@mipmap/icon_location"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:text="China, Guangdong, Shenzhen"
android:textColor="#446880"
android:textSize="16sp"
/>
</LinearLayout>
</ScrollView> <ImageView
android:id="@+id/iv_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:padding="16dp"
android:src="@mipmap/icon_add"
android:visibility="gone"
/>
</RelativeLayout>
item_main_list.xml
<?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"
android:padding="16dp"
> <ImageView
android:id="@+id/iv_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/avatar_male"
/> <TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/iv_icon"
android:text="name"
android:textColor="#5b5b5b"
android:textSize="20sp"
/> </RelativeLayout>
效果图:

学习来源:http://blog.csdn.net/u012199331/article/details/72137112
Android共享元素场景切换动画的实现的更多相关文章
- Android的Activity屏幕切换动画(一)-左右滑动切换
(国内知名Android开发论坛eoe开发者社区推荐:http://www.eoeandroid.com/) Android的Activity屏幕切换动画(一)-左右滑动切换 在Android开发过程 ...
- IOS自定义场景切换动画。
IOS中我们可以通过Storyborad以及segue来实现我们自己的场景切换动画,新建项目使用Single View Application模板并取名为MyCustomSegue. 使用storyb ...
- cocos2d-x场景切换动画
void StartScene::beginGame() { CCLog("beginGame"); //CCTransitionScene *trans ...
- Android的Activity屏幕切换动画-左右滑动切换
. --> 在Android开发过程中,经常会碰到Activity之间的切换效果的问题,下面介绍一下如何实现左右滑动的切换效果,首先了解一下Activity切换的实现,从Android2.0开始 ...
- Android为ViewPager增加切换动画——使用属性动画.
ViewPager作为Android最常用的的组件之一,相信大家在项目中会频繁的使用到的,例如利用ViewPager制作引导页.轮播图,甚至做整个app的表现层的框架等等. Android3.0以下不 ...
- Android开发中activity切换动画的实现
(1)我们在MainAcitvity中定义两个textview,用于点击触发切换Activity事件,下面是布局文件代码. <LinearLayout android:layout_width= ...
- Android至ViewPager添加切换动画——使用属性动画
转载请注明出处:http://blog.csdn.net/allen315410/article/details/44200623 ViewPager作为Android最经常使用的的组件之中的一个.相 ...
- Android为ViewPager添加切换动画——自己定义ViewPager
转载请注明出处:http://blog.csdn.net/allen315410/article/details/44224517 在上篇博客中,我写了一个使用属性动画为ViewPager加入切换动画 ...
- cocos2dx常见场景切换动画(转)
本文转载自:http://www.cnblogs.com/linux-ios/archive/2013/04/09/3010779.html bool HelloWorld::init() { /// ...
随机推荐
- centos 7 安装 redis-5.0.5
[root@localhost ~]# yum -y install gcc make [root@localhost ~]# wget http://download.redis.io/releas ...
- filepath:处理文件路径的一把好手
1.ToSlash(path string) string 将相关平台的路径分隔符转为/ package main import ( "fmt" "os" &q ...
- c++ 初学者的画图库EasyX
EasyX 什么是easyx? EasyX 是针对 C++ 的图形库,可以帮助 C++语言初学者快速上手图形和游戏编程.其实就是c++的一个图形库让初学者不用只在控制台输出代码,而是在图形界面进行开发 ...
- awk处理实记
经grep日志后得到的数据格式如下: } . [debug][-- ::] SendDataStyled:{ , "innings" : "6189269620_0007 ...
- python爬虫练习之批量下载zabbix文档
# -*- coding: UTF-8 -*- import requests,re,time url = 'https://www.zabbix.com/documentation/3.4/zh/m ...
- Js 实现 多个tr 点击变色,再点击还原
我用的是渲染页面,将自定义的值作为一个表示符判断当前状态 <!DOCTYPE html> <html> <head> <meta charset=" ...
- js 多个三目运算符优先级
读JS代码遇到一段看不懂运算优先级的代码,如下 var BrowserSys = {}; var ua = navigator.userAgent.toLowerCase(); var s; (s = ...
- chomre 控制台断点调试
在上图蓝色圆圈中数字,它们分别代表: 1.停止断点调试 2.不跳入函数中去,继续执行下一行代码(F10) 3.跳入函数中去(F11) 4.从执行的函数中跳出 5.禁用所有的断点,不做任何调试 6.程序 ...
- cogs2039树的统计 x
2039. 树的统计 ★★ 输入文件:counttree.in 输出文件:counttree.out 简单对比 时间限制:1 s 内存限制:128 MB [题目描述] 关于树的统计问题 ...
- Map循环/迭代/遍历效率、性能问题。
项目开发完毕,为了找点事打发一下时间,于是用findBugs插件对当前完工的项目进行扫描,发现了很多问题.其中有个关于性能的问题,在这里记录一下. 提示信息为:Inefficient use of k ...