小玩一把__MaterialDesign过渡动画

这篇文章的套路如下,带你装逼带你飞。

  1. 概念
  2. 如何使用
    • 设置 相同的名称
    • 启动 startActivity
    • Fragment to Fragment
    • 设置多组元素
  3. 自定义过度动画
  4. 源码解读
  5. 如何兼容低版本

先看一下 效果吧!


系统自带过渡动画

自定义过渡动画

1.概念

在5.0之前,不同活动或片段的过渡是进入和退出动画,视图层次结构彼此独立的转换。
过渡动画则是:把两个activity当中的相同元素关联起来做连贯的动画; 从而达到不同视图之间的元素关联达到 酷炫、爆炸等酷炫而又优雅地效果。
引导用户视觉。

共享元素类型:

explode:从场景的中心移入或移出
slide:从场景的边缘移入或移出
fade:调整透明度产生渐变效果

动画效果:

changeBounds - 改变目标视图的布局边界
changeClipBounds - 裁剪目标视图边界
changeTransform - 改变目标视图的缩放比例和旋转角度
changeImageTransform - 改变目标图片的大小和缩放比例

2. 如何使用

2.1 设置主题

先要在系统主题中设置主题,或者在java代码中设置支持过渡动画的主题,我们在页面切换时才有过渡动画的效果,有以下两种方式:

2.1.1 方法一

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);  

// 设置一个exit transition
getWindow().setExitTransition(new Explode());

可以通过如下方法在代码总设置进入与退出时 Transition 效果:

  • Window.setEnterTransition():普通transition的进入效果
  • Window.setExitTransition():普通transition的退出效果
  • Window.setSharedElementEnterTransition():共享元素transition的进入效果
  • Window.setSharedElementExitTransition():共享元素transition的退出效果

2.1.2 方法二

在xml中设置 默认配置

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="android:windowContentTransitions">true</item>
    
        <!-- 指定进入和退出transitions -->
    <item name="android:windowEnterTransition">@transition/explode</item>
    <item name="android:windowExitTransition">@transition/explode</item>
    
        <!-- 指定shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">
        @transition/change_image_transform</item>
    <item name="android:windowSharedElementExitTransition">
        @transition/change_image_transform</item>
    ...
</style>

2.2 设置 相同的名称

共享元素切换,首先要保证两个页面内view的transitionName是一致的
设置:android:transitionName
如下:
MainActivity.xml

<android.support.v7.widget.CardView
  ...>
      <ImageView
          android:id="@+id/ivProfile"
          android:transitionName="profile"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="160dp" />
      ...
</android.support.v7.widget.CardView>

DetailActivity.xml

<LinearLayout
  ...>
      <ImageView
          android:id="@+id/ivProfile"
          android:transitionName="profile"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="380dp" />
      ...
</LinearLayout>

2.3 启动 startActivity

第一个Acitivty执行页面跳转时需要加上 ActivityOptionsCompat.toBundle() 才会显示出过渡动画的效果

Intent intent = new Intent(this, DetailsActivity.class);
// Pass data object in the bundle and populate details activity.
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);

//
getWindow().setExitTransition(new Explode());  //设置页面切换效果
//第一次进入时使用
getWindow().setEnterTransition(new explode);
//再次进入时使用
getWindow().setReenterTransition(new explode);

Transition ts = new ChangeClipBounds();         //设置元素动画
ts.setDuration(3000);
getWindow().setSharedElementExitTransition(ts);

ActivityOptionsCompat options = ActivityOptionsCompat.
    makeSceneTransitionAnimation(this, (View)ivProfile, "profile");    
startActivity(intent, options.toBundle());

从第二个Activity反转场景转换动画,请调用Activity.supportFinishAfterTransition()而不是Activity.finish(),如果你使用了toolbar的返回按钮行为,也需要去覆盖它

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            supportFinishAfterTransition();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

2.4 Fragment to Freagment

// Get access to or create instances to each fragment
FirstFragment fragmentOne = ...;
SecondFragment fragmentTwo = ...;
// Check that the device is running lollipop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Inflate transitions to apply
    Transition changeTransform = TransitionInflater.from(this).
          inflateTransition(R.transition.change_image_transform);
    Transition explodeTransform = TransitionInflater.from(this).
          inflateTransition(android.R.transition.explode);
 
    // Setup exit transition on first fragment
    fragmentOne.setSharedElementReturnTransition(changeTransform);
    fragmentOne.setExitTransition(explodeTransform);

    // Setup enter transition on second fragment
    fragmentTwo.setSharedElementEnterTransition(changeTransform);
    fragmentTwo.setEnterTransition(explodeTransform);

    // Find the shared element (in Fragment A)
    ImageView ivProfile = (ImageView) findViewById(R.id.ivProfile);

    // Add second fragment by replacing first 
    FragmentTransaction ft = getFragmentManager().beginTransaction()
            .replace(R.id.container, fragmentTwo)
            .addToBackStack("transaction")
            .addSharedElement(ivProfile, "profile");
    // Apply the transaction
    ft.commit();
}
else {
    // Code to run on older devices
}

2.6 设置多组元素

如最开始的展示效果,可以知道,我们是可以设置多组元素的关联并且对每个元素可以执行不同的过渡动画的,它的方式如下:

Intent intent = new Intent(context, DetailsActivity.class);
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);
Pair<View, String> p1 = Pair.create((View)ivProfile, "profile");
Pair<View, String> p2 = Pair.create(vPalette, "palette");
Pair<View, String> p3 = Pair.create((View)tvName, "text");
ActivityOptionsCompat options = ActivityOptionsCompat.
    makeSceneTransitionAnimation(this, p1, p2, p3);
startActivity(intent, options.toBundle());

注意:默认情况下android.util.Pair将被导入,但是我们要选择android.support.v4.util.Pair类。

这里要避免元素过多导致分散注意力的动画,这样可能会失去过渡动画的意义。

3.自定义过度动画

本质上就是重写 Transition;

套路如下:
1、继承 Visibility 或者 Transition
2、自定义动画:实现 VIsibility/Transition内方法
3、在页面中将自定义Transition设入: getWindow().setEnterTransition或者setSharedElementEnterTransition

看一下自定义的效果

自定义过渡动画

3.1 一些概念:

  1. Visibility extends Transition 页面切换转场动画类;
  2. shareView 的移动的轨迹路径PathMotion类
ArcMotion arcMotion = new ArcMotion();
arcMotion.setMinimumHorizontalAngle(50f);
arcMotion.setMinimumVerticalAngle(50f);

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/ChangePosition.java

3.2 自定义动画

继承 Visibility:

public void captureStartValues(TransitionValues transitionValues) 这里保存计算动画初始状态的一个属性值
public void captureEndValues(TransitionValues transitionValues) 这里保存计算动画结束状态的一个属性值 

public Animator onAppear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是进入动画 即显示某个 View 则会执行这个方法  
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是退出 ,  VIew 则会执行这个方法

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/CommentEnterTransition.java

继承 Transition:

    
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        //初始值保存
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) { 
    //结束值保存
    }
    
    createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues){
  //创建执行动画
  }

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/ChangeColor.java

3.3 调用

            getWindow().setEnterTransition(new CommentEnterTransition(this, toolbar, bottom_aty_love));//设置
            getWindow().setSharedElementEnterTransition(buildShareElemEnterSet());//设置进入转换动画
            getWindow().setSharedElementReturnTransition(buildShareElemReturnSet());//设置退出转换动画

自定义过渡动画的Activity:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/activity/LoveActivity.java

4. 源码解读

带着几个疑问:

  • Transition内方法在哪里被调用
  • 看一个 系统 的 转换类写法 ,这边选择Explode
  1. TransitionKitKat 内被调用
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        android.transition.TransitionValues internalValues =
                new android.transition.TransitionValues();
        copyValues(transitionValues, internalValues);
        mTransition.captureEndValues(internalValues);
        copyValues(internalValues, transitionValues);
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        android.transition.TransitionValues internalValues =
                new android.transition.TransitionValues();
        copyValues(transitionValues, internalValues);
        mTransition.captureStartValues(internalValues);
        copyValues(internalValues, transitionValues);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        android.transition.TransitionValues internalStartValues;
        android.transition.TransitionValues internalEndValues;
        if (startValues != null) {
            internalStartValues = new android.transition.TransitionValues();
            copyValues(startValues, internalStartValues);
        } else {
            internalStartValues = null;
        }
        if (endValues != null) {
            internalEndValues = new android.transition.TransitionValues();
            copyValues(endValues, internalEndValues);
        } else {
            internalEndValues = null;
        }
        return mTransition.createAnimator(sceneRoot, internalStartValues, internalEndValues);
    }

Explode源码

  1. Explode结构如下:
public class Explode extends Visibility {
    public Explode() {
        setPropagation(new CircularPropagation());
    }
    private void captureValues(TransitionValues transitionValues) {
    }

//初始值
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        captureValues(transitionValues);
    }
//结束值
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
        captureValues(transitionValues);
    }
// 进入时被调用
    @Override
    public Animator onAppear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
            //执行动画
        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
                startX, startY, endX, endY, sDecelerate, this);
    }
//退出页面时调用
    @Override
    public Animator onDisappear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {

        return TranslationAnimationCreator.createAnimation(view, startValues,
                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate, this);
    }

    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
    }
}

太简单了,,就是调用 TranslationAnimationCreator.createAnimation 创建动画....,我们上面的自定义过渡动画就是跟 这货学习到....

5. 兼容低版本

到了这里, 送大家一首歌:感觉身体被掏空.......
由于复杂度的原因,
低版本兼容还是说下思路吧,以后有时间或者有明确需求在去实现:
思路:

  1. 将第一个页面中共享元素拿出保存。
  2. 在第二个页面的onCreate()方法内,设置动画,并执行第一个页面view的动画。
  3. 第二个界面退出时,执行第二个界面共享元素动画。

大玩一发——MaterialDesign元素转换动画兼容低版本(有可能不会写!)
网络上有人实现过 大概是这种思路:https://github.com/huzenan/EasyTransition,具体代码我没运行过,不过目测应该是可行的。

参考:

demo地址:戳我!!!


希望我的文章不会误导在观看的你,如果有异议的地方欢迎讨论和指正。
如果能给观看的你带来收获,那就是最好不过了。

人生得意须尽欢, 桃花坞里桃花庵
点个关注呗,对,不信你点试试?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容