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
最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!
posted on 2017-06-07 14:50 LoaderMan 阅读(1150) 评论(0) 编辑 收藏 举报