Android 实现Path2.0中绚丽的的旋转菜单

上图先:

那么下面开始吧~

首先,将整个菜单动画分解开来。

1.       一级菜单按钮的旋转动画2个,十字和叉叉状态的转换。

2.       二级菜单按钮的平移动画2个,弹簧效果的in和out

3.       二级菜单按钮的点击效果,放大消失,其他未点击按钮缩小消失。

4.       一级菜单按钮的恢复效果,放大出现

好的 逐一去实现:

首先是一级菜单按钮的旋转动画,这2个动画可以直接在xml中定义,然后load到代码中来,具体代码如下:

rotate_story_add_button_in.xml

<?xml version="1.0" encoding="UTF-8"?>
 <rotate
     xmlns:android="http://schemas.android.com/apk/res/android"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="150"
          android:fromDegrees="0.0"
          android:toDegrees="-225.0"
          android:pivotX="50.0%"
          android:pivotY="50.0%"
          android:fillAfter="true"
          android:fillEnabled="true"
          />

rotate_story_add_button_out.xml

<?xml version="1.0" encoding="UTF-8"?>
 <rotate
     xmlns:android="http://schemas.android.com/apk/res/android"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="150"
          android:fromDegrees="-225.0"
          android:toDegrees="0.0"
          android:pivotX="50.0%"
          android:pivotY="50.0%"
          android:fillAfter="true"
          android:fillEnabled="true"
           />

这2段没什么好说的,定义好角度即可。

接下来是需要我们在代码中定义的动画部分,这几个动画的部分需要定义一个基类,作为统一的调用接口,这个基类被称作InOutAnimation,继承自AnimationSet,这个基类的主要工作是为view提供in和out两种不同的状态时的动画效果。其子类需要实现2个方法:

 protected abstract void addInAnimation(View aview[]);
 protected abstract void addOutAnimation(View aview[]);

从而进行view的入场和离场动画。

下面是InOutAnimation的代码部分:

public abstract class InOutAnimation extends AnimationSet {

          public Direction        direction;

          public enum Direction {
                    IN, OUT;
          }

          public InOutAnimation(Direction direction, long l, View[] aview) {
                    super(true);
                    this.direction = direction;
                    switch (this.direction) {
                    case IN:
                             addInAnimation(aview);
                             break;
                    case OUT:
                             addOutAnimation(aview);

                             break;
                    }
                    setDuration(l);
          }

          protected abstract void addInAnimation(View aview[]);

          protected abstract void addOutAnimation(View aview[]);

 }

接下来就是重头戏啦,二级菜单按钮的平移动画。

这部分动画看起来可能会比较复杂和神秘,其实不然,当把整个动画过程分解开来的时候,都是最最简单的平移而已,我们要做的只是定义一下平移的起点和终点、开始动画的顺序以及插值(Interpolator),让整个过程看起来很炫。

先说动画的起点和终点吧,起点很简单,就是整个view的左下角,即0,0点,为了效果漂亮一些,我们稍微的将左下角位置定义的有一些偏移,经验上的值是16,-13,这个点的位置看你心情而定咯~ 好 终点就是你想让他在的点上就好了,终点我们将定义到layout中去,为这个2级菜单指定一个margin的值就好。

还是上代码比较直观:

动画如下:

收缩部分:TranslateAnimation(xOffset + -mlp.leftMargin, 0F,yOffset + mlp.bottomMargin, 0F)
扩张部分:TranslateAnimation(0F, xOffset + -mlp.leftMargin, 0F,yOffset + mlp.bottomMargin)

位置定义部分:

例如:

android:layout_marginBottom="142dp"  android:layout_marginLeft="10.667dp"

这个位置大家可以直观的在布局文件中看到,详细的布局文件也将在下面展示。

以上是单独的每一个二级按钮的动画,而组合的动画就是指定了一下开始的时间差以及插值:

这个就是奥妙所在了,OvershootInterpolator AnticipateInterpolator 这2个插值器提供了弹力效果。

整段的代码如下:

ComposerButtonAnimation.java

public class ComposerButtonAnimation extends InOutAnimation {

          public static final int                  DURATION       = 500;
          private static final int       xOffset              = 16;
          private static final int       yOffset              = -13;

          public ComposerButtonAnimation(Direction direction, long l, View view) {
                    super(direction, l, new View[] { view });
          }

          public static void startAnimations(ViewGroup viewgroup,
                             InOutAnimation.Direction direction) {
                    switch (direction) {
                    case IN:
                             startAnimationsIn(viewgroup);
                             break;
                    case OUT:
                             startAnimationsOut(viewgroup);
                             break;
                    }
          }

          private static void startAnimationsIn(ViewGroup viewgroup) {
                    for (int i = 0; i < viewgroup.getChildCount(); i++) {
                             if (viewgroup.getChildAt(i) instanceof InOutImageButton) {
                                      InOutImageButton inoutimagebutton = (InOutImageButton) viewgroup
                                                         .getChildAt(i);
                                      ComposerButtonAnimation animation = new ComposerButtonAnimation(
                                                         InOutAnimation.Direction.IN, DURATION, inoutimagebutton);
                                      animation.setStartOffset((i * 100)
                                                         / (-1 + viewgroup.getChildCount()));
                                      animation.setInterpolator(new OvershootInterpolator(2F));
                                      inoutimagebutton.startAnimation(animation);
                             }
                    }
          }

          private static void startAnimationsOut(ViewGroup viewgroup) {
                    for (int i = 0; i < viewgroup.getChildCount(); i++) {
                             if (viewgroup.getChildAt(i) instanceof InOutImageButton) {
                                      InOutImageButton inoutimagebutton = (InOutImageButton) viewgroup
                                                         .getChildAt(i);
                                      ComposerButtonAnimation animation = new ComposerButtonAnimation(
                                                         InOutAnimation.Direction.OUT, DURATION,
                                                         inoutimagebutton);
                                      animation.setStartOffset((100 * ((-1 + viewgroup
                                                         .getChildCount()) - i))
                                                         / (-1 + viewgroup.getChildCount()));
                                       animation.setInterpolator(new AnticipateInterpolator(2F));
                                      inoutimagebutton.startAnimation(animation);
                             }
                    }
          }

          @Override
          protected void addInAnimation(View[] aview) {
                    MarginLayoutParams mlp = (MarginLayoutParams) aview[0]
                                      .getLayoutParams();
                    addAnimation(new TranslateAnimation(xOffset + -mlp.leftMargin, 0F,
                                      yOffset + mlp.bottomMargin, 0F));
          }

          @Override
          protected void addOutAnimation(View[] aview) {
                    MarginLayoutParams mlp = (MarginLayoutParams) aview[0]
                                      .getLayoutParams();
                    addAnimation(new TranslateAnimation(0F, xOffset + -mlp.leftMargin, 0F,
                                      yOffset + mlp.bottomMargin));
          }
 }

剩下的增大出现、增大消失及缩小消失都是scale和alpha的组合动画

例如增大出现为:

 addAnimation(new ScaleAnimation(0F, 1F, 0F, 1F, 1, 0.5F, 1, 0.5F));
 addAnimation(new AlphaAnimation(0F, 1F));

整段的代码如下:

public class ComposerButtonGrowAnimationIn extends InOutAnimation {

          public ComposerButtonGrowAnimationIn(int i) {
                    super(InOutAnimation.Direction.IN, i, new View[0]);
          }

          @Override
          protected void addInAnimation(View[] aview) {
                    addAnimation(new ScaleAnimation(0F, 1F, 0F, 1F, 1, 0.5F, 1, 0.5F));
                    addAnimation(new AlphaAnimation(0F, 1F));

          }

          @Override
          protected void addOutAnimation(View[] aview) {}

 }

 public class ComposerButtonGrowAnimationOut extends InOutAnimation {

          public ComposerButtonGrowAnimationOut(int i) {
                    super(InOutAnimation.Direction.OUT, i, new View[0]);
          }

          @Override
          protected void addInAnimation(View[] aview) {}

          @Override
          protected void addOutAnimation(View[] aview) {
                    addAnimation(new ScaleAnimation(1F, 5F, 1F, 5F, 1, 0.5F, 1, 0.5F));
                    addAnimation(new AlphaAnimation(1F, 0F));
          }

 } public class ComposerButtonShrinkAnimationOut extends InOutAnimation {

          public ComposerButtonShrinkAnimationOut(int i) {
                    super(InOutAnimation.Direction.OUT, i, new View[0]);
          }

          @Override
          protected void addInAnimation(View[] aview) {

          }

          @Override
          protected void addOutAnimation(View[] aview) {
                    addAnimation(new ScaleAnimation(1F, 0F, 1F, 0F, 1, 0.5F, 1, 0.5F));
                    addAnimation(new AlphaAnimation(1F, 0F));
          }

 }

接下来我们需要为这些控件做一下扩展,以便其可以再动画完成后显示或消失。

很简单

public class InOutImageButton extends ImageButton {

          private Animation    animation;

          public InOutImageButton(Context context, AttributeSet attrs, int defStyle) {
                    super(context, attrs, defStyle);
          }

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

          public InOutImageButton(Context context) {
                    super(context);
          }

          @Override
          protected void onAnimationEnd() {
                    super.onAnimationEnd();
                    if ((this.animation instanceof InOutAnimation)) {
                             setVisibility(((InOutAnimation) this.animation).direction != InOutAnimation.Direction.OUT ? View.VISIBLE
                                                : View.GONE);
                    }
          }

          @Override
          protected void onAnimationStart() {
                    super.onAnimationStart();
                    if ((this.animation instanceof InOutAnimation))
                             setVisibility(View.VISIBLE);
          }

          @Override
          public void startAnimation(Animation animation) {
                    super.startAnimation(animation);
                    this.animation = animation;
                    getRootView().postInvalidate();
          }
 }

那么到这里基本上就已经搞定了所有的事情了,剩下点没做的事就是把这些动画效果设置给对应的控件了.

代码:这里

时间: 2024-08-07 16:45:47

Android 实现Path2.0中绚丽的的旋转菜单的相关文章

Path2.0中绚丽的的旋转菜单

我们看一下实现的效果图: 在上图中,我将菜单弹出的效果设置成直线型,最终的弹出或汇总点在下面的红色按钮中. 它的实现原理是设置动画的同时并利用动画中的插入器(interpolator)来实现弹力.主要用到了OvershootInterpolator和AnticipateOvershootInterpolator,简单介绍下这两个插入器. OvershootInterpolator:表示向前甩一定值后再回到原来位置. AnticipateOvershootInterpolator:表示开始的时候向

Android Studio 3.0 新特性

Kotlin 集成 基于IDEA 2017.1 版本构建 对Android O 的支持 对Java8的支持 新的Android Gradle 插件 Google's Maven repository 新的Android分析器(New Android Profiler) APK 调试器 新的设备文件浏览器 Instant Apps support 布局编辑器改进 新的Android Tings 模板 Layout Inspector改进 APK Analyzer 改进 新的Android模拟器功能

Android Studio 3.0 下载 使用新功能介绍

谷歌2017发布会更新了挺多内容的,而且也发布了AndroidStudio3.0预览版,一些功能先睹为快.(英语一般,有些翻译不太好) 下载地址 https://developer.android.google.cn/studio/archive.html 选择显示全部即可看到下载地址,这里给出来了. Windows (64-bit): android-studio-ide-171.4010489-windows.zip (702075896 bytes) https://dl.google.c

【Android Developer Blog】Android Studio 2.0(07 APRIL 2016)

Android Studio 2.0 Android Studio 2.0是打造高品质,高性能应用的Android开发平台,包括手机和平板,Android Auto,Android Wear和 Android TV.作为Google官方的IDE,Android Studio包含了你需要的一切,包括代码编辑器(code editor),代码分析工具(code analysis tools),模拟器(emulators )等.Android Studio 2.0 stable有着更快的编译速度,更好

android圆形旋转菜单,并支持移动换位功能

LZ最近接手公司一个项目,需要写一个圆形的旋转菜单,并且支持菜单之间的移动换位,本来以为这种demo应该网上是很多的,想不到度娘也是帮不了我,空有旋转功能但是却不能换位置,所以LZ就只能靠自己摸索了. 最终LZ参考了网上的部分代码,重写了一个自定义的view终于实现了这个看似很吊,却没有实际意义的功能.在此贡献出来给广大码农们共享. 话不多说,先上代码: 自定义view类: public class RoundSpinView extends View { private Paint mPain

android圆形旋转菜单,而对于移动转换功能支持

LZ该公司最近接手一个项目,需要写一个圆形旋转菜单,和菜单之间的移动换位支持,我本来以为这样的demo如若互联网是非常.想想你妈妈也帮不了我,空旋转,但它不能改变位置,所以LZ我们只能靠自己摸索. 最后LZ参考代码的在线部分.了一个自己定义的view最终实现了这个看似非常吊.却没有实际意义的功能. 在此贡献出来给广大码农们共享. 话不多说,先上代码: 自己定义view类: public class RoundSpinView extends View { private Paint mPaint

Android 6.0 中TimePicker显示为滚动样式的方法

在Android6.0中,TimePicker控件的默认样式为转盘的样式,就像这个样子: 如果想要显示为之前的滚动样式的话也很简单,只要在布局文件中设置TimePicker的timePickerMode属性为spinner就好了,即: [html] view plain copy <TimePicker android:id="@+id/id_add_timePicker" android:layout_width="match_parent" android

在Android 5.0中使用JobScheduler

在Android 5.0中使用JobScheduler 原文链接 : using-the-jobscheduler-api-on-android-lollipop 译者 : Mr.Simple 校对者 : Mr.Simple 在这篇文章中,你会学习到在Android 5.0中如何使用JobScheduler API.JobScheduler API允许开发者在符合某些条件时创建执行在后台的任务. 介绍 在Android开发中,会存在这么些场景 : 你需要在稍后的某个时间点或者当满足某个特定的条件

Windows环境下Android Studio v1.0安装教程

Windows环境下Android Studio v1.0安装教程 Windows环境下Android Studio v1.0安装教程 准备工具 JDK安装包. 要求:JDK 7以及以上版本. Android Studio安装文件. Windows: exe(包含SDK) (813 MB) exe(不包含SDK) (250 MB) zip (235 MB) Mac dmg (234 MB) zip (233 MB) Linux: zip (233 MB) 说明: 32位系统和64位系统是同一个安