Material Design 系列 Transition

该篇涉及内容比较多就不多于废话了,直接x入主题

PathMotion

抽象类PathMotion一个与路径移动相关的,这个基类可以扩展提供运动沿着路径转换,比如指定两个位置进行移动,通过PathMotion路径下指定维度进行移动。该类提供了一个getPath方法,源码如下


public abstract class PathMotion {

    public PathMotion() {}

    public PathMotion(Context context, AttributeSet attrs) {}

    /**
     * Provide a Path to interpolate between two points <code>(startX, startY)</code> and
     * <code>(endX, endY)</code>. This allows controlled curved motion along two dimensions.
     *
     * @param startX The x coordinate of the starting point.
     * @param startY The y coordinate of the starting point.
     * @param endX   The x coordinate of the ending point.
     * @param endY   The y coordinate of the ending point.
     * @return A Path along which the points should be interpolated. The returned Path
     * must start at point <code>(startX, startY)</code>, typically using
     * {@link android.graphics.Path#moveTo(float, float)} and end at <code>(endX, endY)</code>.
     */
    public abstract Path getPath(float startX, float startY, float endX, float endY);
}

在Transition里面有具体实现,默认的是直线路径

 private static final PathMotion STRAIGHT_PATH_MOTION = new PathMotion() {
        @Override
        public Path getPath(float startX, float startY, float endX, float endY) {
            Path path = new Path();
            path.moveTo(startX, startY);
            path.lineTo(endX, endY);
            return path;
        }
    };

然而这种效果并不是很友好的,它的继承类Arcmotion诞生了,该类提供了自定义属性:minimumVerticalAngle、minimumHorizontalAngle、ArcMotion_maximumAngle水平方向和垂直方向的最小最大维度。以便于我们xml自定义ChangeBounds引用,getPath经过算法处理得到最终的路径。ChangeBounds本质是一个继承自Transition的类,如果引用了我们自定义的ArcMotion就替换了Transtion默认的直线路径移动,ChangeBounds稍后再细说,先来看看我们该怎么在xml引用自定义的ArcMotion

<changeBounds>
   <arcMotion android:minimumHorizontalAngle="15"
              android:minimumVerticalAngle="0"
              android:maximumAngle="90"/>
 </changeBounds>

AutoTransition

工具类,当我们执行ChangeBounds相同控件Id位移渐变,而多余的控件要进行fade变化,该类帮助我们进行移动和调整大小视图中一个场景的变化。


public class AutoTransition extends TransitionSet {

    /**
     * Constructs an AutoTransition object, which is a TransitionSet which
     * first fades out disappearing targets, then moves and resizes existing
     * targets, and finally fades in appearing targets.
     *
     */
    public AutoTransition() {
        init();
    }

    public AutoTransition(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setOrdering(ORDERING_SEQUENTIAL);
        addTransition(new Fade(Fade.OUT)).
                addTransition(new ChangeBounds()).
                addTransition(new Fade(Fade.IN));
    }
}

这里的Fade是Transition的子类Visibility的子类,自定义属性提供了Fade的mode设置fadingMode,Fade.in Fade.out值来自其父类的transitionVisibilityMode属性对应的mode,从源码里面不难看出这是一个简单的透明度变化的属性动画

  private Animator createAnimation(final View view, float startAlpha, final float endAlpha) {
        if (startAlpha == endAlpha) {
            return null;
        }
        view.setTransitionAlpha(startAlpha);
        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "transitionAlpha", endAlpha);
        if (DBG) {
            Log.d(LOG_TAG, "Created animator " + anim);
        }
        final FadeAnimatorListener listener = new FadeAnimatorListener(view);
        anim.addListener(listener);
        anim.addPauseListener(listener);
        addListener(new TransitionListenerAdapter() {
            @Override
            public void onTransitionEnd(Transition transition) {
                view.setTransitionAlpha(1);
            }
        });
        return anim;
    }

Change系列

ChangeBounds这一过渡捕捉目标视图的布局范围场景改变前后的变化。提供属性是否支持大小裁剪,默认不支持,该类通过Property封装了一系列的键值对,供createAnimator调用,类似ChangeBounds的类还有好几个就不一 一细说,下面列出来如果你感兴趣可以查看相关源码(Change系列与Scene配合TransitionManager使用下面Demo介绍):

  • ChangeBounds

  • ChangeClipBounds

  • ChangeImageTransfor

  • ChangeScroll

  • ChangeTransform


官方demo实践
BasicTransition

透过上面这一点源码粗略过一遍,在脑海中有了多多少少的一点印象,下面开始通过一些demo,来帮助我们进一步的了解Transition。官网提供了一个BasicTransition,实现效果如下:

实现原理:首先要有一个根布局rootView,其次是多个场景View,通过TransitionManager.go(Scene)进行场景变换(注意场景的view对应的id要相同才有效果),而Scene的实例获取方式可以通过new和getSceneForLayout方式,下面来简单看看demo核心代码示例:


public class BasicTransitionFragment extends Fragment
        implements RadioGroup.OnCheckedChangeListener {

    //.........略..............

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        mSceneRoot = (ViewGroup) view.findViewById(R.id.scene_root);

        mScene1 = new Scene(mSceneRoot, (ViewGroup) mSceneRoot.findViewById(R.id.container));

        mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, getActivity());

        mScene3 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene3, getActivity());


        mTransitionManagerForScene3 = TransitionInflater.from(getActivity())
                .inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot);


        return view;
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        switch (checkedId) {
            case R.id.select_scene_1: {
                TransitionManager.go(mScene1);    
                break;
            }
            case R.id.select_scene_2: {
                TransitionManager.go(mScene2);
                break;
            }
            case R.id.select_scene_3: {
                mTransitionManagerForScene3.transitionTo(mScene3);
                break;
            }
            case R.id.select_scene_4: {
                //缩放动画完成后重新设置控件大小
                TransitionManager.beginDelayedTransition(mSceneRoot);
                View square = mSceneRoot.findViewById(R.id.transition_square);
                ViewGroup.LayoutParams params = square.getLayoutParams();
                int newSize = getResources().getDimensionPixelSize(R.dimen.square_size_expanded);
                params.width = newSize;
                params.height = newSize;
                square.setLayoutParams(params);
                break;
            }
        }
    }

}

场景的转换我们可以通过TransitionManager.go也可以通过上面代码中的TransitionInflater获取TransitionManager实例调用内部定义的其静态方法transitionTo。

ActivitySceneTransitionBasic

这是一个官方提供的一个Activity无缝切换的demo,Activity的切换通过ActivityOptionsCompat向下兼容,调用ActivityOptionsCompat的startActivity方法,Pair形式封装的键要与跳转界面的值一致。官方源码跳转代码如下(这种效果比较适合用于列表跳转详情界面,起点小说的app就是如此实现)

  @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
        Item item = (Item) adapterView.getItemAtPosition(position);

        Intent intent = new Intent(this, DetailActivity.class);
        intent.putExtra(DetailActivity.EXTRA_PARAM_ID, item.getId());
        ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
                this,
                new Pair<View, String>(view.findViewById(R.id.imageview_item),
                        DetailActivity.VIEW_NAME_HEADER_IMAGE),
                new Pair<View, String>(view.findViewById(R.id.textview_name),
                        DetailActivity.VIEW_NAME_HEADER_TITLE));

        ActivityCompat.startActivity(this, intent, activityOptions.toBundle());
    }
//...............下面是详情界面,接手传递数据调用transition方法................................

        mItem = Item.getItem(getIntent().getIntExtra(EXTRA_PARAM_ID, 0));

        mHeaderImageView = (ImageView) findViewById(R.id.imageview_header);
        mHeaderTitle = (TextView) findViewById(R.id.textview_title);

        ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
        ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);

当然如果需要变化的View只有一个就没必要用Pair了,直接调用makeSceneTransitionAnimation(Activity activity,View sharedElement, String sharedElementName) {}即可,实现效果图如下:


开源库Interpolator

动画都少不了插值器,Transition配合Interpolator让效果显得更PL了,在这里推荐两个比较不错的动画插值器相关的库,有了它们基本足以满足我们的开发需求

根据现在了解的Transition系列相关的只是,做一个简单的demo测试一下,效果图如下(录制效果不好见谅哈):

Transition相关的开发中,我们有必要明确一下几点区别:Enter、Exit、ReEnter、allow等相关属性含义,这里以下列代码为例做个简短说明

  <!--允许场景变化,代码设置 getWindow.requestFeature(Window.FEATURE_CONTENT_TRANSITION)-->
        <item name="android:windowContentTransitions">true</item>

        <!--动画播放是否能重叠-->
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>

        <!--Activity A intent >Activity B : Activity A 需要执行Exit,Activity B 需要Enter-->
        <item name="android:windowExitAnimation">@transition/scene1</item>
        <item name="android:windowEnterAnimation">@transition/scene1</item>

        <!--Activity B返回ActivityA :需要执行ActivityB的Return动画,ActivityA的ReEnter动画-->
        <item name="android:windowReturnTransition">@transition/scene1</item>
        <item name="android:windowReenterTransition">@transition/scene1</item>

        <!--Activity A B定义共同的id元素得控件,再切换Activity执行对应的动画效果-->
        <item name="android:windowSharedElementEnterTransition">@transition/scene1</item>
        <item name="android:windowSharedElementExitTransition">@transition/scene1</item>

当然这些属性我们可以不仅可以在Style中定义,还可以代码调用通过Window、Slide等进行配置,在动画执行的时候还可以添加监听,在我们结束当前界面有用到Transition相关的动画,最好在动画结束的时候调用finishAfterTransition();而不是finish方法,这样会显得更自然一些。


哎,Transition动画在低版本不能使用,这是一件令人不太开心的事,我满怀激情的来了,你却让我洗个冷水澡,萎了咋办!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值