AnimatorSet实现菜单动画
1. 什么是AnimatorSet?
ValueAnimator 和 ObjectAnimator 都只能单独实现一个动画,如果我们想要使用一个组合动画,就需要用到 AnimatorSet;
2. 原理
2.1 需求分析
- 当点击 menu 时,items 将沿着以 menu 为圆心,90° 角散开;
- 当再次点击 menu ,或者点击任何一个 item 时,items 将原路返回。
2.2 路径分析
-
已知其展开时角度,以及展开时半径,可以得出,每一个 item 的位移数据;
X = radius * sin(a) Y = radius * cos(a)
2.3 数据计算
- 夹角 a 的值;
90° 5 个 item,4 个间隙;故,a = 90 / 4
;
第 i 个item,需要旋转的角度,degree = a * i
; - 正弦余弦怎么计算?
Math 提供了计算类:double sin(double d)
;但是这里的参数,不是度数,是弧度; - 弧度怎么获得?
degree = Math.sin(angdeg* Math.PI /180 );
degree = Math.toRadians(double angdeg);
3. 布局
-
使用帧布局 FrameLayout,将 items 的图片盖在 menu 之下;这里要注意,最好让顶部的 imageview 写在最后面,防止被之前的 view 覆盖;
-
代码;
<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. 点击事件
- 点击 menu 对菜单进行操作,开着就给合上,合着就给开开;
- 点击 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. 操作菜单
- 默认是关闭的,
private boolean isClose = true;;
private void ctrlMenu() { if (isClose) { openMenu(); } else { closeMenu(); } isClose = !isClose; }
5.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); }
- 打开 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(); }
- 注意:
这里默认将 items 的可是状态,设置为 gone ,当点击时,如果是 gone ,就设置为可见;
此处是向左上弹出菜单,故 都为负数;
5.2 关闭菜单
-
依次合上 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); }
-
合上 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) { } }); }
-
注意:
这里都将 items 设置为 gone,防止出现:合上时,items 和 menu 都可以点击,造成的后果就是点击后开关一气呵成……这里把设置 gone 的操作放在动画的监听里,动画执行完成后,再设置为 gone;
6. 坑坑坑…
- 对于
Math.sin(a);
其参数不是度数是弧度! - 获取弧度值:
degree = Math.toRadians(double angdeg);
ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0f)
中,参数的 f 一定要加上!
声明:本文整理自《《Android自定义控件开发入门与实战》_启舰》;