小动画之“菜单”

来吧展示


1. 什么是AnimatorSet?

ValueAnimator 和 ObjectAnimator 都只能单独实现一个动画,如果我们想要使用一个组合动画,就需要用到 AnimatorSet;


2. 原理

2.1 需求分析

  1. 当点击 menu 时,items 将沿着以 menu 为圆心,90° 角散开;
  2. 当再次点击 menu ,或者点击任何一个 item 时,items 将原路返回。

2.2 路径分析

  1. 已知其展开时角度,以及展开时半径,可以得出,每一个 item 的位移数据;

    在这里插入图片描述

    X = radius * sin(a)
    Y = radius * cos(a)
    

2.3 数据计算

  1. 夹角 a 的值;
    90° 5 个 item,4 个间隙;故,a = 90 / 4
    第 i 个item,需要旋转的角度,degree = a * i
  2. 正弦余弦怎么计算?
    Math 提供了计算类:double sin(double d);但是这里的参数,不是度数,是弧度
  3. 弧度怎么获得?
    1. degree = Math.sin(angdeg* Math.PI /180 );
    2. degree = Math.toRadians(double angdeg);

3. 布局

  1. 使用帧布局 FrameLayout,将 items 的图片盖在 menu 之下;这里要注意,最好让顶部的 imageview 写在最后面,防止被之前的 view 覆盖;

  2. 代码;

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary">
        
        <ImageView
            android:id="@+id/iv_item1"
            style="@style/item_button_style"
            android:background="@drawable/channel4" />
        ···
        <ImageView
            android:id="@+id/iv_menu"
            style="@style/munu_button_style"
            android:background="@drawable/icon_home" />
            
    </FrameLayout>
    
    <style name="munu_button_style">
        <item name="android:layout_width">50dp</item>
        <item name="android:layout_height">50dp</item>
        <item name="android:layout_marginBottom">10dp</item>
        <item name="android:layout_marginRight">10dp</item>
        <item name="android:layout_gravity">center_horizontal|center_vertical</item>
    </style>
    <style name="item_button_style">
        <item name="android:layout_width">35dp</item>
        <item name="android:layout_height">35dp</item>
        <item name="android:visibility">gone</item>
        <item name="android:layout_marginBottom">10dp</item>
        <item name="android:layout_marginRight">10dp</item>
        <item name="android:layout_gravity">center_horizontal|center_vertical</item>
    </style>
    

4. 点击事件

  1. 点击 menu 对菜单进行操作,开着就给合上,合着就给开开;
  2. 点击 item 时,如果开着就给关上;
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv_menu:
                Toast.makeText(this, "点击了 menu", Toast.LENGTH_SHORT).show();
                ctrlMenu();
                break;
            case R.id.iv_item1:
            case R.id.iv_item2:
            case R.id.iv_item3:
            case R.id.iv_item4:
            case R.id.iv_item5:
                Toast.makeText(this, "点击了 item", Toast.LENGTH_SHORT).show();
                if (!isClose) {
                    Toast.makeText(MenuActivity.this, "You Clicked " + v, Toast.LENGTH_SHORT).show();
                    closeMenu();
                    isClose = !isClose;
                }
                break;
            default:
                break;
        }
    }
    

5. 操作菜单

  1. 默认是关闭的,private boolean isClose = true;;
    private void ctrlMenu() {
        if (isClose) {
            openMenu();
        } else {
            closeMenu();
        }
        isClose = !isClose;
    }
    

5.1 打开菜单

  1. 依次打开 item;
    private void openMenu() {
        doOpenMenu(iv_item1, 0, 5, 300);
        doOpenMenu(iv_item2, 1, 5, 300);
        doOpenMenu(iv_item3, 2, 5, 300);
        doOpenMenu(iv_item4, 3, 5, 300);
        doOpenMenu(iv_item5, 4, 5, 300);
    }
    
  2. 打开 item 的动画
    private void doOpenMenu(ImageView imageView, int index, int total, long radius) {
        angle = (double) Math.toRadians(90 / (total - 1);
        double degree = angle * index;
        if (imageView.getVisibility() == View.GONE) {
            imageView.setVisibility(View.VISIBLE);
        }
        int translaX = -(int) (radius * Math.cos(degree));
        int translaY = -(int) (radius * Math.sin(degree));
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(imageView, "translationX", 0, translaX),
                ObjectAnimator.ofFloat(imageView, "translationY", 0, translaY),
                ObjectAnimator.ofFloat(imageView, "scaleX", 0f, 1f),
                ObjectAnimator.ofFloat(imageView, "scaleY", 0f, 1f),
                ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f)
        );
        animatorSet.setDuration(500);
        animatorSet.start();
    }
    
  3. 注意:
    这里默认将 items 的可是状态,设置为 gone ,当点击时,如果是 gone ,就设置为可见;
    此处是向左上弹出菜单,故 都为负数;

5.2 关闭菜单

  1. 依次合上 items;

    private void closeMenu() {
        doCloseMenu(iv_item1, 0, 5, 300);
        doCloseMenu(iv_item2, 1, 5, 300);
        doCloseMenu(iv_item3, 2, 5, 300);
        doCloseMenu(iv_item4, 3, 5, 300);
        doCloseMenu(iv_item5, 4, 5, 300);
    }
    
  2. 合上 item 的动画

    private void doCloseMenu(final ImageView imageView, int index, int total, long radius) {
        angle = (double) Math.toRadians(90 / (total - 1);
        double degree = angle * index;
        int translaX = (int) (-radius * Math.cos(degree));
        int translaY = (int) (-radius * Math.sin(degree));
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(imageView, "translationX", translaX, 0),
                ObjectAnimator.ofFloat(imageView, "translationY", translaY, 0),
                ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0f),
                ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 0f),
                ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f)
        );
        animatorSet.setDuration(500);
        animatorSet.start();
        animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                if (imageView.getVisibility() == View.VISIBLE) {
                    imageView.setVisibility(View.GONE);
                }
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
    }
    
  3. 注意:
    这里都将 items 设置为 gone,防止出现:合上时,items 和 menu 都可以点击,造成的后果就是点击后开关一气呵成……

    这里把设置 gone 的操作放在动画的监听里,动画执行完成后,再设置为 gone;


6. 坑坑坑…

  1. 对于 Math.sin(a); 其参数不是度数是弧度!
  2. 获取弧度值:degree = Math.toRadians(double angdeg);
  3. ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0f) 中,参数的 f 一定要加上!

声明:本文整理自《《Android自定义控件开发入门与实战》_启舰》;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liusaisaiV1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值