安卓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