欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-30 18:19:46
...

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

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

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

  • FloatingActionButton(FAB悬浮按钮) 是 Android 5.0 新特性——Material Design
    中的一个控件,是一种悬浮的按钮。
  • FloatingActionButton 是 ImageView 的子类,因此它具备ImageView的全部属性。
  • FloatingActionButton 结合 CoordinatorLayout 使用,即可实现悬浮在任意控件的任意位置。
  • 使用 FloatingActionButton 的难点主要是布局,其在JAVA代码中的用法和普通的 ImageView 基本相同。
    利用FloatingActionButton+ValueAnimator 完成卫星菜单效果

跟所有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轴坐标,即点击事件距离整个屏幕顶边的距离

文字或许不太理解,那么我们上图
利用FloatingActionButton+ValueAnimator 完成卫星菜单效果

展开Item

需要做些什么呢,这里我们以子item为3举例说明:
利用FloatingActionButton+ValueAnimator 完成卫星菜单效果

根据上图可以清晰的看出来当子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();
    }
}

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

谢谢大家观看!


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

相关标签: 卫星菜单