Android共享元素场景切换动画的实现
安卓5.0系统引入了共享元素,能做出非常炫酷的场景切换效果,这让人非常兴奋同时非常蛋疼,因为低版本没法使用啊,所以今天就跟大家分享一下自己写的一个库,其实只有2个文件而已就可以兼容安卓5.0以下的版本。
重要的工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | 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); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 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]; } }; } } |
场景使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | 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; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?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
最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步