Android--列表页跳转详情页,动画效果需求之共享元素场景切换动画的实现

Posted 彬彬杨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android--列表页跳转详情页,动画效果需求之共享元素场景切换动画的实现相关的知识,希望对你有一定的参考价值。

上周给了一个列表页,跳详情页的动画需求,由于ios端这个需求已经做完,我就直接仿照这他们实现的样子来做的,做的时候发现跟自己最开始设想的还不一样

刚好周末有时间,来做个总结

说几个不一样的点,先放图,在看下面的demo例子,更好理解一些:

1.列表页item  里面的布局是 ImagView ,但是右图详情页的图片却不是Imageview,而是一个Xbanner轮播图,切有滑动功能

   

(2)下图这种大图的情况

左图列表的item布局也是个xbanner 右图同样是xbanner 

 

下面先来说下

简单版本的,思路,先上路

这个就是一个简单的共享动画的跳转

public class BinTransition 

    public static final String EASY_TRANSITION_OPTIONS = "easy_transition_options";
    public static final long DEFAULT_TRANSITION_ANIM_DURATION = 1000;

    /**
     * 开始的时候
     */
    public static void startActivity(Intent intent, BinTransitionOptions options) 
        options.update();
        intent.putParcelableArrayListExtra(EASY_TRANSITION_OPTIONS, options.getAttrs());
        Activity activity = options.getActivity();
        activity.startActivity(intent);
        activity.overridePendingTransition(0, 0);
    

    /**
     * 带结果返回的开始
     */
    public static void startActivityForResult(Intent intent, int requestCode, BinTransitionOptions options) 
        options.update();
        intent.putParcelableArrayListExtra(EASY_TRANSITION_OPTIONS, options.getAttrs());
        Activity activity = options.getActivity();
        activity.startActivityForResult(intent, requestCode);
        activity.overridePendingTransition(0, 0);
    

    /**
     * 调用此方法开始输入转换动画
     *
     * @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<BinTransitionOptions.ViewAttrs> attrs =
                intent.getParcelableArrayListExtra(EASY_TRANSITION_OPTIONS);
        runEnterAnimation(activity, attrs, duration, interpolator, listener);
    


    public static void enter(Activity activity, long duration, Animator.AnimatorListener listener) 
        enter(activity, duration, null, listener);
    


    public static void enter(Activity activity, TimeInterpolator interpolator, Animator.AnimatorListener listener) 
        enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, interpolator, listener);
    


    public static void enter(Activity activity, Animator.AnimatorListener listener) 
        enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, null, listener);
    


    public static void enter(Activity activity) 
        enter(activity, DEFAULT_TRANSITION_ANIM_DURATION, null, null);
    

    private static void runEnterAnimation(Activity activity,
                                          ArrayList<BinTransitionOptions.ViewAttrs> attrs,
                                          final long duration,
                                          final TimeInterpolator interpolator,
                                          final Animator.AnimatorListener listener) 
        if (null == attrs || attrs.size() == 0)
            return;

        for (final BinTransitionOptions.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;
                        
                    );
        
    

    /**
     * 退出活动,调用此方法以启动退出转换动画
     */
    public static void exit(Activity activity, long duration, TimeInterpolator interpolator) 
        Intent intent = activity.getIntent();
        ArrayList<BinTransitionOptions.ViewAttrs> attrs = intent.getParcelableArrayListExtra(EASY_TRANSITION_OPTIONS);
        runExitAnimation(activity, attrs, duration, interpolator);
    


    public static void exit(Activity activity, TimeInterpolator interpolator) 
        exit(activity, DEFAULT_TRANSITION_ANIM_DURATION, interpolator);
    


    public static void exit(Activity activity, long duration) 
        exit(activity, duration, null);
    



    private static void runExitAnimation(final Activity activity,
                                         ArrayList<BinTransitionOptions.ViewAttrs> attrs,
                                         long duration,
                                         TimeInterpolator interpolator) 
        if (null == attrs || attrs.size() == 0)
            return;

        for (final BinTransitionOptions.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);
    



/**
 * 过度动画选项
 */
public class BinTransitionOptions 

    private Activity activity;
    private View[] views;
    private ArrayList<ViewAttrs> attrs;

    public BinTransitionOptions(Activity activity, View[] views) 
        this.activity = activity;
        this.views = views;
    

 
    public static BinTransitionOptions makeTransitionOptions(Activity activity, View... views) 
        return new BinTransitionOptions(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];
            
        ;
    

关键的一个地方

在两个布局中的两个视图之间使用相同的ID。

A列表页的布局:
<?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>

B详情页的其中一部分布局

   <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"
        />

在A页面,进行转换选项,并使用EasyTransition#startActivity启动 去B页面

 
    EasyTransitionOptions options =
        EasyTransitionOptions.makeTransitionOptions(
            ActivityA.this,
            findViewById(R.id.iv_icon),
            findViewById(R.id.tv_name)); // 根据你自己的需求可以带上多个id
 
 
    Intent intent = new Intent(ActivityA.this, ActivityB.class);
    EasyTransition.startActivity(intent, options);

在B页面中, 退出效果

  // onCreate
    EasyTransition.enter(ActivityB.this);
 
    // onBackPressed
    EasyTransition.exit(ActivityB.this);

继续接着最开始的需求说,因为一开始牵涉到id不一致的问题,所以在布局里面嵌了一层布局 id是想通的(蒙层),跳转的时候 先显示蒙层的图,等动画结束,在隐藏蒙层就好了,效果就出来了

以上是关于Android--列表页跳转详情页,动画效果需求之共享元素场景切换动画的实现的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 中从一个详情页跳转到另一个详情页并返回列表页?

vue列表页跳转到列表详情页再返回到列表页, 页面不刷新保持原来的状态

thinkCMF----列表页跳转

小程序实现的一些常用功能

react 页面跳转

SwiftUI之深入解析如何创建列表展示页和导航跳转详情页