Android 转场动画

880 阅读3分钟

# Android 转场动画

Android sdk 包里关于转场过度相关的包 android.transition android.transition

此包中的类为视图层次结构启用“场景和转换”功能。

# 转场效果必知

  • Scence

Scence 视图层次结构状态的封装,包括该层次结构中的视图以及这些视图具有的各种值(与布局相关和其他)场景可以直接由布局层次结构定义,也可以通过代码定义场景,该场景在输入时动态设置场景。

  • Transition

Transition是一种自动为输入新场景时发生的更改动画的机制。一些过渡功能是自动的。也就是说,进入一个场景可能会导致动画运行,淡出视图消失 changeBounds并调整更改的现有视图的大小,并淡化可见的视图。还有其他过渡可以为其他属性设置动画,例如颜色变化并且可以选择指定在特定场景变化期间进行。最后开发人员可以定义自己的Transition子类,这些子类监视特定的属性更改,并在这些属性更改值时运行自定义动画

  • TransitionManager

用于指定特定场景更改的自定义过渡,以及使特定过渡发生的场景更改。

秉承着官方有不动手的原则下面的例子来自官方gitHub仓库: android/animation

# 场景一

ActivityA 跳转 ActivityB

ActivityB 退回 ActivityA

ActivityA:


<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/grid"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:columnWidth="120dp"
    android:drawSelectorOnTop="true"
    android:horizontalSpacing="@dimen/grid_spacing"
    android:numColumns="auto_fit"
    android:padding="@dimen/grid_spacing"
    android:verticalSpacing="@dimen/grid_spacing" />
    
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.example.android.activityscenetransitionbasic.SquareFrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/imageview_item"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@null"
            android:scaleType="centerCrop" />

    </com.example.android.activityscenetransitionbasic.SquareFrameLayout>

    <TextView
        android:id="@+id/textview_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:maxLines="1"
        android:padding="16dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
        android:theme="@style/Theme.AppCompat" />

</LinearLayout>

ActivityB:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <com.example.android.activityscenetransitionbasic.SquareFrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:id="@+id/imageview_header"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop" />

        </com.example.android.activityscenetransitionbasic.SquareFrameLayout>

        <TextView
            android:id="@+id/textview_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:maxLines="2"
            android:padding="16dp"
            android:textAppearance="@style/TextAppearance.AppCompat.Title"
            android:theme="@style/Theme.AppCompat" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:text="@string/bacon_ipsum"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

    </LinearLayout>

</ScrollView>

# 转场

 /**
         * Called when an item in the {@link android.widget.GridView} is clicked. Here will launch
         * the {@link DetailActivity}, using the Scene Transition animation functionality.
         */
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
            Item item = (Item) adapterView.getItemAtPosition(position);

            // Construct an Intent as normal
            Intent intent = new Intent(MainActivity.this, DetailActivity.class);
            intent.putExtra(DetailActivity.EXTRA_PARAM_ID, item.getId());

            // BEGIN_INCLUDE(start_activity)
            /*
             * Now create an {@link android.app.ActivityOptions} instance using the
             * {@link ActivityOptionsCompat#makeSceneTransitionAnimation(Activity, Pair[])} factory
             * method.
             //该方法 用来创建可转场元素的集合
             */
            @SuppressWarnings("unchecked")
            ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
                    MainActivity.this,
                    // Now we provide a list of Pair items which contain the view we can transitioning
                    // from, and the name of the view it is transitioning to, in the launched activity
                    new Pair<>(view.findViewById(R.id.imageview_item),
                            DetailActivity.VIEW_NAME_HEADER_IMAGE),
                    new Pair<>(view.findViewById(R.id.textview_name),
                            DetailActivity.VIEW_NAME_HEADER_TITLE));

            // Now we can start the Activity, providing the activity options as a bundle
            ActivityCompat.startActivity(MainActivity.this, intent, activityOptions.toBundle());
            // END_INCLUDE(start_activity)
        }
        
    @SuppressWarnings("unchecked")
    public static ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
            Pair<View, String>... sharedElements) {
        if (Build.VERSION.SDK_INT >= 21) {
            android.util.Pair<View, String>[] pairs = null;
            if (sharedElements != null) {
                pairs = new android.util.Pair[sharedElements.length];
                for (int i = 0; i < sharedElements.length; i++) {
                    pairs[i] = android.util.Pair.create(
                            sharedElements[i].first, sharedElements[i].second);
                }
            }
            return createImpl(ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
        }
        return new ActivityOptionsCompat();
    }
    
     // BEGIN_INCLUDE(detail_set_view_name)
        /*
         * Set the name of the view's which will be transition to, using the static values above.
         * This could be done in the layout XML, but exposing it via static variables allows easy
         * querying from other Activities
         */
        ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
        ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
        // END_INCLUDE(detail_set_view_name)
        
     /**
     * Try and add a {@link Transition.TransitionListener} to the entering shared element
     * {@link Transition}. We do this so that we can load the full-size image after the transition
     * has completed.
     *
     * @return true if we were successful in adding a listener to the enter transition
     * 添加 过渡 动画执行监听
     */
    @RequiresApi(21)
    private boolean addTransitionListener() {
        final Transition transition = getWindow().getSharedElementEnterTransition();

        if (transition != null) {
            // There is an entering shared element transition so add a listener to it
            transition.addListener(new Transition.TransitionListener() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    // As the transition has ended, we can now load the full-size image
                    loadFullSizeImage();

                    // Make sure we remove ourselves as a listener
                    transition.removeListener(this);
                }

                @Override
                public void onTransitionStart(Transition transition) {
                    // No-op
                }

                @Override
                public void onTransitionCancel(Transition transition) {
                    // Make sure we remove ourselves as a listener
                    transition.removeListener(this);
                }

                @Override
                public void onTransitionPause(Transition transition) {
                    // No-op
                }

                @Override
                public void onTransitionResume(Transition transition) {
                    // No-op
                }
            });
            return true;
        }

        // If we reach here then we have not added a listener
        return false;
    }