利用FloatingActionButton+ValueAnimator 完成卫星菜单效果

来源:互联网 发布:手机淘宝店铺头像 编辑:程序博客网 时间:2024/05/21 06:59

前言:卫星菜单其实已经很常见了,网上也有很多教程甚至都有开源的控件了。嫌麻烦自己写的可以直接取拿来用。接下来文章会简单说明实现的过程。

github开源库:https://github.com/oguzbilgener/CircularFloatingActionMenu
这个效果很不错,需要的朋友可以去看看

正文之前先说说FloatingActionButton到底是什么吧,相信还是有部分朋友并不太清楚。

  • FloatingActionButton(FAB悬浮按钮) 是 Android 5.0 新特性——Material Design
    中的一个控件,是一种悬浮的按钮。
  • FloatingActionButton 是 ImageView 的子类,因此它具备ImageView的全部属性。
  • FloatingActionButton 结合 CoordinatorLayout 使用,即可实现悬浮在任意控件的任意位置。
  • 使用 FloatingActionButton 的难点主要是布局,其在JAVA代码中的用法和普通的 ImageView 基本相同。
    这里写图片描述

跟所有MD控件一样,要使用FAB,需要在gradle文件中先注册依赖:

compile 'com.android.support:design:25.0.0'//        FAB 基本属性://        android:src:FAB中显示的图标//        app:backgroundTint:正常的背景颜色//        app:rippleColor:按下时的背景颜色//        app:elevation:正常的阴影大小//        app:pressedTranslationZ:按下时的阴影大小//        app:layout_anchor:设置FAB的锚点,即以哪个控件为参照设置位置//        app:layout_anchorGravity:FAB相对于锚点的位置//        app:fabSize:FAB的大小,normal或mini(对应56dp和40dp)//        注意:要想让FAB显示点击后的颜色和阴影变化效果,必须设置onClick事件

想进一步了解FloatingActionButton的可以去看看鸿洋大神的博客:
http://blog.csdn.net/lmj623565791/article/details/46678867


好,FloatingActionButton知道是什么了后我们回到卫星菜单上面来。
其实卫星菜单的效果就是当我们点击主菜单之后弹出附属的子item菜单,再次点击收回子item菜单。那么就会考虑到这么几点:
1.定位Item(设置每一个菜单项的位置)
2.展开Item(动画效果实现,包括菜单按钮旋转动画、菜单项平移、旋转动画、菜单项缩放、透明度变换动画等,这里我们只做平移效果)
3.收回Item

定位涉及到的方法一共有下面几个:参考http://blog.csdn.net/jason0539/article/details/42743531

//view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()//view获取自身宽高:getHeight(),getWidth()//motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()//getTop:获取到的,是view自身的顶边到其父布局顶边的距离//getLeft:获取到的,是view自身的左边到其父布局左边的距离//getRight:获取到的,是view自身的右边到其父布局左边的距离//getBottom:获取到的,是view自身的底边到其父布局顶边的距离//getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离//getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离//getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离//getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离

文字或许不太理解,那么我们上图
这里写图片描述

展开Item

需要做些什么呢,这里我们以子item为3举例说明:
这里写图片描述

根据上图可以清晰的看出来当子item菜单为3个的时候,子itemA应该是左移M单位(M为我们自己设置的长度);子itemC上移M单位;重点为子itemB的位置,由图可以知道当我们以初始item(以下用O代替),子itemB和坐标轴来画一个三角形的话,就可以根据三角函数sin与cos来知道子itemB的坐标。∠AOC为90°OB平分∠AOC,即∠BOC = (∠AOC/2)=45° ,由三角函数可以很简单的得到B相对于O 左移了M*sin(45°)距离,上移了M*cos(45°) 距离。 以此可以推导当子item为4的时候只需要根据个数算出彼此之间的角度大小,然后同理运用三角函数可以得到位移之后的坐标。

**

收回item

**

相对于展开,收回要简单多了。
展开后我们可以分别得到每个子item的位置坐标,和初始点的位置坐标,那么就可以利用平移动画将其移动回初始点即可。

代码部分:

xml布局代码:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    xmlns:app="http://schemas.android.com/apk/res-auto">    <android.support.design.widget.FloatingActionButton        android:id="@+id/item1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:layout_marginBottom="20dp"        android:layout_marginRight="16dp"        android:src="@mipmap/ic_launcher"        app:fabSize="mini"/>    <android.support.design.widget.FloatingActionButton        android:id="@+id/item2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:layout_marginBottom="20dp"        android:layout_marginRight="16dp"        android:src="@mipmap/ic_launcher"        app:fabSize="mini"/>    <android.support.design.widget.FloatingActionButton        android:id="@+id/item3"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:layout_marginBottom="20dp"        android:layout_marginRight="16dp"        android:src="@mipmap/ic_launcher"        app:fabSize="mini"/>    <android.support.design.widget.FloatingActionButton        android:id="@+id/fab"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:layout_marginBottom="20dp"        android:layout_marginRight="16dp"        android:src="@mipmap/ic_launcher"        app:fabSize="mini"/></RelativeLayout>

Activity代码:

@ContentView(R.layout.activity_fab)public class FABActivity extends BaseActivity {    @ViewInject(R.id.fab)    private FloatingActionButton FABButton;    @ViewInject(R.id.item1)    private FloatingActionButton Item1;    @ViewInject(R.id.item2)    private FloatingActionButton Item2;    @ViewInject(R.id.item3)    private FloatingActionButton Item3;    //菜单是否展开    private boolean menuOpen = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);    }    //点击事件    @Event(R.id.fab)    private void fab(View view) {        if (menuOpen == false) {            showMenu();//展开        } else {            hideMenu();//收回        }    }    //展开菜单    private void showMenu() {        //设置为展开菜单        menuOpen = true;        //获取1°的值,后面动画移动会用到        final double r = Math.PI / 180;        //取得主菜单坐标        int x = (int) FABButton.getX();        int y = (int) FABButton.getY();        //设置第一个子菜单x轴移动动画        ValueAnimator v1 = ValueAnimator.ofInt(x, x - 200);//起始位置主菜单x坐标,终位置向左移200        v1.setDuration(500);        v1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) animation.getAnimatedValue();                int t = (int) Item1.getY();                int r = Item1.getWidth() + l;                int b = Item1.getHeight() + t;                Item1.layout(l, t, r, b);            }        });        //设置第二个子菜单x轴与y轴移动动画        ValueAnimator v2x = ValueAnimator.ofInt(x, x - (int) (200 * Math.sin(45 * r)));//起始位置主菜单x坐标,终位置向左移200*sin(45°)        ValueAnimator v2y = ValueAnimator.ofInt(y, y - (int) (200 * Math.cos(45 * r)));//起始位置主菜单x坐标,终位置向左移200*cos(45°)        v2x.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) animation.getAnimatedValue();                int t = (int) Item2.getY();                int r = Item2.getWidth() + l;                int b = Item2.getHeight() + t;                Item2.layout(l, t, r, b);            }        });        v2y.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) Item2.getX();                int t = (int) animation.getAnimatedValue();                int r = Item2.getWidth() + l;                int b = Item2.getHeight() + t;                Item2.layout(l, t, r, b);            }        });        //设置第三个子菜单y轴移动动画        ValueAnimator v3 = ValueAnimator.ofInt(y, y - 200);//起始位置主菜单y坐标,终位置向上移200        v3.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) Item3.getX();                int t = (int) animation.getAnimatedValue();                int r = Item3.getWidth() + l;                int b = Item3.getHeight() + t;                Item3.layout(l, t, r, b);            }        });        //开始上面设置好的展开动画        v1.start();        v2x.start();        v2y.start();        v3.start();    }    //收回菜单    private void hideMenu() {        //设置为收回菜单        menuOpen = false;        //获取现在第一个子菜单的x轴坐标        int x = (int) Item1.getX();        ValueAnimator v1 = ValueAnimator.ofInt(x, (int) FABButton.getX());//始:现x坐标;  终:主菜单x坐标        v1.setDuration(500);        v1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) animation.getAnimatedValue();                int t = (int) Item1.getY();                int r = Item1.getWidth() + l;                int b = Item1.getHeight() + t;                Item1.layout(l, t, r, b);            }        });        //获取现在第二个子菜单的x轴,y轴坐标        x = (int) Item2.getX();        int y = (int) Item2.getY();        ValueAnimator v2x = ValueAnimator.ofInt(x, (int) FABButton.getX());        ValueAnimator v2y = ValueAnimator.ofInt(y, (int) FABButton.getY());        v2x.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) animation.getAnimatedValue();                int t = (int) Item2.getY();                int r = Item2.getWidth() + l;                int b = Item2.getHeight() + t;                Item2.layout(l, t, r, b);            }        });        v2y.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) Item2.getX();                int t = (int) animation.getAnimatedValue();                int r = Item2.getWidth() + l;                int b = Item2.getHeight() + t;                Item2.layout(l, t, r, b);            }        });        //获取现在第三个子菜单的y轴坐标        y = (int) Item3.getY();        ValueAnimator v3 = ValueAnimator.ofInt(y, (int) FABButton.getY());        v3.setDuration(500).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int l = (int) Item3.getX();                int t = (int) animation.getAnimatedValue();                int r = Item3.getWidth() + l;                int b = Item3.getHeight() + t;                Item3.layout(l, t, r, b);            }        });        //开始设置好的收回动画        v1.start();        v2x.start();        v2y.start();        v3.start();    }}

运行效果:
这里写图片描述

谢谢大家观看!


为了向别人、向世界证明自己而努力拼搏,而一旦你真的取得了成绩,才会明白:人无须向别人证明什么,只要你能超越自己。

原创粉丝点击