Android之卫星菜单的实现

  卫星菜单是现在一个非常受欢迎的“控件”,很多Android程序员都趋之若鹜,预览如下图。传统的卫星菜单是用Animation实现的,需要大量的代码,而且算法极多,一不小心就要通宵Debug。本帖贴出用属性动画Animator来实现卫星菜单。

一、浅析属性动画Animator

  Animator是Android3.0发布的新功能,代码简单,效果丰富。属性动画,顾名思义,只要是可以GET和SET的属性,我们都可以用属性动画进行处理。属性动画中常用的属性和方法如下:

ValueAnimator  //数值发生器,可以实现很多很灵活的动画效果
ObjectAnimator  //ValueAnimator的子类,对ValueAnimator进行了封装,让我们可以更轻松的使用属性动画,我们通过ObjectAnimator来操纵一个对象,产生动画效果
AnimatorListener  //对动画的开始、结束、暂停、重复等动作的事件监听(需要重写四个方法)
AnimatorListenerAdapter  //对动画的开始、结束、暂停、重复中的一个动作的事件监听(根据选择的动作,只需要重写一个方法)
AnimatorSet  //动画的集合,用来设置多个动画之间的关系(之前、之后、同时等)
PropertyValuesHolder  //动画的集合,和AnimatorSet类似
TypeEvaluator  //值计算器,在使用ValueAnimator.ofObject()方法时引入自定义的属性对象
Interpolator  //插值器,设置动画的特效(速度渐变、弹跳等)

下面对这几个类做一下简单的介绍:

(一)ObjectAnimator:ObjectAnimator是最简单、最常用的属性动画,根据文档上的叙述: This subclass of ValueAnimator provides support for animating properties on target objects. ,这个ValueAnimator的子类对Object对象的属性提供动画。ObjectAnimator中常用的属性如下:

translationX / translationY             水平/垂直平移
rotaionX / rotationY                    横向/纵向旋转
scaleX / scaleY                         水平/垂直缩放
X / Y                                   直接到达X/Y坐标
alpha                                   透明度

我们使用一些方法( ofFloat() 、 ofInt() 、 ofObject() 、 ofPropertyValuesHolder() 等)来实现动画,调用 start() 方法来启动动画。选择哪个方法,主要是属性值的类型决定的。我们先来看看文档中对这几个方法的介绍:

target指的是动画的作用对象,一般指控件;propertyName就是上面说的“translationX”、“alpha”等属性名;values是一个不定长数组,记录着属性的始末值。唯一不同的是ofPropertyValuesHolder()方法,这个方法没有property参数,是因为这个参数在PropertyValuesHolder对象中就已经使用(下面会介绍PropertyValuesHolder的使用方法)。

(二)AnimatorListener:这是一个接口,监听属性动画的状态(开始/重复/结束/取消),Animator及其子类(包括ValueAnimator和ObjectAnimator等)都可以使用这个接口,使用 anim.addListener() 使用这个接口。具体的使用方法如下:

animator.addListener(new AnimatorListener() {@Override  // 动画开始的监听器public void onAnimationStart(Animator animation) { ... }@Override  // 动画重复的监听器public void onAnimationRepeat(Animator animation) { ... }@Override  // 动画结束的监听器public void onAnimationEnd(Animator animation) { ... }@Override  // 动画取消的监听器public void onAnimationCancel(Animator animation) { ... }
});

(三)AnimatorListenerAdapter:我们在实际开发中往往不会用到AnimatorListener中的全部四个方法,所以如果我们使用AnimatorListener,不仅浪费系统资源,代码看上去也不好看,因此,我们需要一个适配器(Adapter),可以让我们根据自己的意愿,只实现监听器中的一个或几个方法,这就要用到AnimatorListenerAdapter了。AnimatorListenerAdapter的使用方法和AnimatorListener一样,都需要使用 anim.addListener() 方法,不同的是参数。代码如下:

animator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) { ... }@Overridepublic void onAnimationCancel(Animator animation) { ... }
});

(四)AnimatorSet:先来看文档中的介绍: This class plays a set of Animator objects in the specified order. Animations can be set up to play together, in sequence, or after a specified delay.  这个类支持按顺序播放一系列动画,这些动画可以同时播放、按顺序播放,也可以在一段时间之后播放(主要通过 setStartDelay() 方法实现)。下面是文档中对这些方法的介绍:

值得说的是play()方法,它返回的是一个Builder对象,而Builder对象可以通过 with() 、 before() 、 after() 等方法,非常方便的控制一个动画与其他Animator动画的先后顺序。例如:

AnimatorSet s = new AnimatorSet();
s.play(anim1).with(anim2);
s.play(anim2).before(anim3);
s.play(anim4).after(anim3);

(五)PropertyValuesHolder:使用PropertyValuesHolder结合ObjectAnimator或ValueAnimator,可以达到AnimatorSet的效果。可以说,PropertyValuesHolder就是为了动画集而存在的,它不能单独的存在,因为它没有target参数(因此可以节省系统资源),所以只能依靠ObjectAnimator或ValueAnimator存在。一个实例的代码如下:

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 0F, 200F);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("translationY", 0F, 200F);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("rotation", 0F, 360F);
ObjectAnimator.ofPropertyValuesHolder(top, pvh1, pvh2, pvh3).setDuration(2000).start();

(六)ValueAnimator:是ObjectAnimator的父类,但由于不能相应动画,也不能设置属性(值的是没有target和property两个参数),所以不常用。如果我们要监听ValueAnimator,则只能为ValueAnimator添加一个AnimatorUpdateListener(AnimatorUpdateListener可以监听Animator每个瞬间的变化,取出对应的值)。一个实例的代码如下:

ValueAnimator animator = ValueAnimator.ofInt(0, 100); // 没有target和property参数
animator.setDuration(5000);
animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Integer value = (Integer) animation.getAnimatedValue();System.out.println("-->" + value);}
});
animator.start();

(七)TypeEvaluator:主要用于ValueAnimator的ofObject()方法。根据文档中的介绍, Evaluators allow developers to create animations on arbitrary property types ,Evaluators允许开发者自定义动画参数。因此,使用TypeEvaluator,我们可以打破ObjectAnimator的动画范围禁锢,创造我们自己的动画。大致代码如下:

ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<Object>() {@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {return null;}
});
animator.start();

(八)Interpolator:插值器,可以设置动画的效果。Animator内置了很多插值器,如渐加速、渐减速、弹跳等等。插值器的种类可以在模拟器API DEMO应用的Views - Animation - Interpolator中查看。为Animator添加插值器只需要 animator.setInterpolator(new OvershootInterpolator()); 即可。

 

二、实现卫星菜单

  实现卫星菜单,我们主要使用了ObjectAnimator。先来介绍一下这个DEMO的效果(如下组图):开始的时候在屏幕左上角显示一个红色的按钮,点击之后弹出一列子菜单,每个子菜单之间的距离递增,弹出的时间递减,弹出时会有OverShoot效果,点击每个子菜单都会弹出相应的Toast;弹出菜单后再次点击红色按钮,则收回所有子菜单。

下面贴出代码。

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:background="@color/white" >
 6 
 7     <!-- 上面的七张图片是七个子菜单 -->
 8     <ImageView
 9         android:id="@+id/menuitem_07"
10         style="@style/SateliteMenu_Item_Theme"
11         android:contentDescription="@string/app_name"
12         android:src="@drawable/item_07" />
13 
14     <ImageView
15         android:id="@+id/menuitem_06"
16         style="@style/SateliteMenu_Item_Theme"
17         android:contentDescription="@string/app_name"
18         android:src="@drawable/item_06" />
19 
20     <ImageView
21         android:id="@+id/menuitem_05"
22         style="@style/SateliteMenu_Item_Theme"
23         android:contentDescription="@string/app_name"
24         android:src="@drawable/item_05" />
25 
26     <ImageView
27         android:id="@+id/menuitem_04"
28         style="@style/SateliteMenu_Item_Theme"
29         android:contentDescription="@string/app_name"
30         android:src="@drawable/item_04" />
31 
32     <ImageView
33         android:id="@+id/menuitem_03"
34         style="@style/SateliteMenu_Item_Theme"
35         android:contentDescription="@string/app_name"
36         android:src="@drawable/item_03" />
37 
38     <ImageView
39         android:id="@+id/menuitem_02"
40         style="@style/SateliteMenu_Item_Theme"
41         android:contentDescription="@string/app_name"
42         android:src="@drawable/item_02" />
43 
44     <ImageView
45         android:id="@+id/menuitem_01"
46         style="@style/SateliteMenu_Item_Theme"
47         android:contentDescription="@string/app_name"
48         android:src="@drawable/item_01" />
49 
50     <!-- 最上层的红色按钮,因为它显示在最上面,因此布局代码在最后 -->
51     <ImageView
52         android:id="@+id/menu_top"
53         android:layout_width="35.0dip"
54         android:layout_height="35.0dip"
55         android:layout_marginLeft="17.0dip"
56         android:layout_marginTop="17.0dip"
57         android:contentDescription="@string/app_name"
58         android:src="@drawable/top_toopen" />
59 
60 </RelativeLayout>
MainActivity布局代码
 1 public class MainActivity extends Activity implements OnClickListener {
 2     private ImageView top; // 红色按钮
 3     // 七个子菜单的ID组成的数组
 4     private int[] ids = new int[] { R.id.menuitem_01, R.id.menuitem_02, R.id.menuitem_03, R.id.menuitem_04, R.id.menuitem_05, R.id.menuitem_06, R.id.menuitem_07, };
 5     private List<ImageView> itemList; // 七个子菜单都加到List中
 6     private boolean isMenuOpen; // 记录子菜单是否打开了
 7 
 8     @Override
 9     protected void onCreate(Bundle savedInstanceState) {
10         super.onCreate(savedInstanceState);
11         setContentView(R.layout.activity_main);
12         initView();
13     }
14 
15     private void initView() {
16         top = (ImageView) findViewById(R.id.menu_top);
17         top.setOnClickListener(this);
18 
19         itemList = new ArrayList<ImageView>();
20         for (int i = 0; i < ids.length; i++) {
21             ImageView imageView = (ImageView) findViewById(ids[i]);
22             imageView.setOnClickListener(this);
23             itemList.add(imageView);
24         }
25     }
26 
27     @Override
28     public void onClick(View v) {
29         switch (v.getId()) {
30         case R.id.menu_top:
31             if (!isMenuOpen) { // 如果子菜单处于关闭状态,则使用Animator动画打开菜单
32                 for (int i = 0; i < itemList.size(); i++) {
33                     // 子菜单之间的间距对着距离的增加而递增
34                     ObjectAnimator animator = ObjectAnimator.ofFloat(itemList.get(i), "translationY", 0, (i + 1) * (30 + 2 * i));
35                     animator.setDuration((7 - i) * 100); // 最远的子菜单弹出速度最快
36                     animator.setInterpolator(new OvershootInterpolator()); // 设置插值器
37                     animator.start();
38                 }
39                 top.setImageResource(R.drawable.top_toclose);
40                 isMenuOpen = true;
41             } else { // 如果子菜单处于打开状态,则使用Animator动画关闭菜单
42                 for (int i = 0; i < itemList.size(); i++) {
43                     ObjectAnimator animator = ObjectAnimator.ofFloat(itemList.get(i), "translationY", (i + 1) * (30 + 2 * i), 0);
44                     animator.setDuration((7 - i) * 100);
45                     animator.start();
46                 }
47                 top.setImageResource(R.drawable.top_toopen);
48                 isMenuOpen = false;
49             }
50             break;
51         default:
52             Toast.makeText(MainActivity.this, "Item" + (itemList.indexOf(v) + 1) + " Clicked...", Toast.LENGTH_SHORT).show();
53             break;
54         }
55     }
56 }
MainActivity代码

 


三、扩展

  使用属性动画可以实现的功能还有很多,这里再追加一个“评分”的功能实现。直接显示成绩往往给人一种突兀的感觉,所以我们想利用属性动画来实现一个数字变换的“成绩单”,让成绩滚动显示,同时,越到最后滚动的越慢,最后停止在真正的分数上。

  思路:我们需要用到ValueAnimator,并用AnimatorUpdateListener作为动画的监听器,监听动画的每一个动作,并为成绩的TextView设置Text。

  以下是代码。

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:background="@color/white" >
 6 
 7     <TextView
 8         android:id="@+id/main_mark"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_centerInParent="true"
12         android:textColor="#ff0000"
13         android:textSize="65.0sp" />
14 
15 </RelativeLayout>
MainActivity布局代码
 1 public class MainActivity extends Activity {
 2     private TextView mark;
 3     private static final int REAL_MARK = 96;
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         initView();
10     }
11 
12     private void initView() {
13         mark = (TextView) findViewById(R.id.main_mark);
14 
15         ValueAnimator animator = ValueAnimator.ofInt(0, REAL_MARK);
16         animator.setDuration(3000);
17         animator.setInterpolator(new DecelerateInterpolator());
18         animator.addUpdateListener(new AnimatorUpdateListener() {
19             @Override
20             public void onAnimationUpdate(ValueAnimator animation) {
21                 Integer value = (Integer) animation.getAnimatedValue();
22                 mark.setText(value + "");
23             }
24         });
25         animator.start();
26     }
27 }
MainActivity代码

 

转载于:https://www.cnblogs.com/blog-wzy/p/5324316.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373358.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为云量身定制您的服务

相信大家都听说过Amazon的AWS。作为业内最为成熟的云服务提供商&#xff0c;其运行规模&#xff0c;稳定性&#xff0c;安全性都已经经过了市场的考验。时至今日&#xff0c;越来越多的应用被部署在了AWS之上。这其中不乏Zynga及Netflix这样著名的服务。 然而这一切并没有停滞不…

在Vaadin和JSF之间选择

随着最新版本的Primefaces 3.0的发布&#xff0c;JSF终于达到了前所未有的成熟度和实用性&#xff0c;使其与其他流行的Rich Internet Applications&#xff08;RIA&#xff09;选项如Google Web Toolkit&#xff08;GWT&#xff09;&#xff0c;ExtJS&#xff0c;Vaadin&#…

flask开发restful api系列(1)

在此之前&#xff0c;向大家说明的是&#xff0c;我们整个框架用的是flask sqlalchemy redis。如果没有开发过web&#xff0c;还是先去学习一下&#xff0c;这边只是介绍如果从开发web转换到开发移动端。如果flask还不是很熟悉&#xff0c;我建议先到这个网站简单学习一下&am…

JS显示当前时间(包含农历时间)

时间格式&#xff1a; JavaScript代码&#xff1a; var sWeek new Array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");var dNow new Date();var CalendarData new Arra…

MyBatis操作指南-与Spring集成(基于注解)

转载于:https://www.cnblogs.com/weilu2/p/mybatis_spring_integration_basic_on_annotation.html

JVM:如何分析线程转储

本文将教您如何分析JVM线程转储&#xff0c;并查明问题的根本原因。 从我的角度来看&#xff0c;线程转储分析是掌握Java EE生产支持的任何个人最重要的技能。 您可以从线程转储快照中获取的信息量通常远远超出您的想象。 我的目标是与您分享我在过去10年中积累的有关线程转储分…

极光推送JPush的快速集成

首先到极光推送的官网上创建一个应用&#xff0c;填写对应的应用名和包名。 创建好之后下载Demo 提取Sdk里面的图片和xml等资源文件放自己项目的相应位置&#xff0c;然后要注意的是.so文件的放置位置&#xff1a; 在main目录下新建一个jniLibs文件夹&#xff0c;放在这个文件夹…

elk系列1之入门安装与基本操作

preface 我们每天都要查看服务器的日志&#xff0c;一方面是为了开发的同事翻找日志&#xff0c;另一方面是巡检服务器查看日志&#xff0c;而随着服务器数量以及越来越多的业务上线&#xff0c;日志越来越多&#xff0c;人肉运维相当痛苦了&#xff0c;此时&#xff0c;参考现…

Java 7 –反编译项目硬币

大家好&#xff0c;该是从2012年开始写作的时候了。正如您在其他博客中可能已经看到的那样&#xff0c;有一些更改可以使您使用Java编程时的开发人员生活变得更加轻松&#xff1a;Diamond运算符&#xff0c;Switchs中的Strings&#xff0c;尝试使用资源&#xff0c;多次捕获等 …

在Excel表里面插入背景图

工作中我们会经常用到MS Excel&#xff0c;通常我们打开MS Excel&#xff0c;里面的工作表都是空白单调的背景。当然了&#xff0c;MS Excel可以在工作簿里面插入背景图片。那么问题来了&#xff0c;如果你没有安装Microsoft Office&#xff0c;该如何在Excel文件里面插入好看的…

实现两级下拉框的联动

1.实现两级下拉框的联动。 功能&#xff1a;实现点击年级下拉框&#xff0c;加载对应科目的下拉框。 第一步&#xff1a;首先要加载年级下拉框中的数据。 01.在GradeDAL层&#xff08;数据访问层&#xff09;写一个方法&#xff0c;查询所有年级的信息。 /// <summary>//…

python连接SQL Server取多个结果集:Pymssql模块

基本的用法可以参考&#xff1a;python连接SQL Server&#xff1a;Pymssql模块 和上一篇文章中的代码&#xff0c;只取一个结果集不同&#xff0c;这次会一次运行2个sql语句&#xff0c;然后分别取出2个结果集&#xff0c;打印输出。 代码中有详细的注释&#xff0c;一看就明白…

Xen安全架构sHype/ACM策略配置图文教程

实验要求 1. 熟悉Xen虚拟化平台部署&#xff1b; 2. Xen sHype/ACM安全架构中的Simple TE和Chinese Wall策略及事实上现机制的分析与验证。 第1章 Xen环境部署 1.1 版本号选择 因为Ubuntu使用广泛。软件包易于下载。我们选择Ubuntu系统进行Xen部署…

java弹出虚拟键盘_JS实现电脑虚拟键盘的操作

本文实例为大家分享了JS实现电脑虚拟键盘的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下需求&#xff1a;1.当输入框光标聚焦时&#xff0c;电脑虚拟键盘弹出2.在输入框输入内容时&#xff0c;键盘跟着变化具体实现代码如下&#xff1a;Html部分&#xff1a;电脑键…

JBoss Drools –入门

这篇文章是关于我如何掌握JBoss Drools的 。 其背后的原因是&#xff1a;SAP收购了我公司当前的规则引擎&#xff0c;而Drools是我们将寻找的另一种选择&#xff0c;只要有人掌握了概念验证的技能即可。 尽管似乎有大量的文档&#xff0c;但是我总是会通过示例来发现它是有帮助…

android使用bintray发布aar到jcenter

前言 这两天心血来潮突然想把自己的android library的aar放到jcenter里面&#xff0c;这样一来自己便可以在任何时间任何地点通过internet得到自己的library的引用了&#xff0c;况且现在android studio已经默认使用jcenter的repositories作为依赖来源&#xff0c;以前的mavenc…

PHP 进程详解

PHP 进程详解PHP 进程详解 如下内容从《操作系统精髓与设计原理》中总结提炼得出&#xff0c;删除了大部分对于理解进程有干扰的文字&#xff0c;对进程知识结构进行的梳理。几乎所有内容为按照书本上摘抄下来的&#xff0c;我目前还总结提炼不出像作者这么深刻的见解。那么就先…

java都要caps标点_第 1 章 管理 Java CAPS 用户

第 1 章 管理 Java CAPS 用户在此处列出的主题提供了有关如何管理 Sun JavaTM Composite Application Platform Suite (Java CAPS) 中的用户的信息。如果您有任何问题&#xff0c;请参见 http://goldstar.stc.com/ 中的 Java CAPS Web 站点。管理系统信息库用户此类别包含以下用…

基于OpenCV 的美颜相机推送直播流

程序流程&#xff1a; 1.图像采集 先从opencv&#xff08;2.4.10版本&#xff09;采集回来摄像头的图像&#xff0c;是一帧一帧的 每一帧图像是一个矩阵&#xff0c;opencv中的mat 数据结构。 2.人脸的美化 人脸美化&#xff0c;我们用的皮肤检测&#xff0c;皮肤在颜色空间是特…

Spring线程池服务

线程池对于执行同步和异步过程非常重要。 本文介绍如何使用Spring开发和监视线程池服务。 创建线程池已通过两种替代方法进行了说明。 二手技术 &#xff1a; JDK 1.6.0_21 Spring3.0.5 Maven的3.0.2 步骤1&#xff1a;建立已完成的专案 创建一个Maven项目&#xff0c;如下…