浅谈属性动画简单使用之实现卫星菜单(二)

大家对于卫星菜单应该都不陌生了,其实这个菜单如果能合适运用到我们的APP项目中,确实是一个不错的选择,交互性非常好。在写Demo之前我也上网搜了一些关于卫星菜单的实现,感觉好多人实现卫星菜单这种动画,采用的是补间动画,并且代码还不少,通过上一讲我们知道,补间动画不具备与用户交互的特点,就比如卫星菜单展开后,产生很多子菜单有很多的点击事件,会发现产生点击事件的位置不会随着补间动画而产生位置改变而改变,反而点击事件一直保存在初始的位置。当然补间动画也能做,那就需要产生动画后发生位置的改变,并且也需要将点击事件触发的位置随着动画改变而改变。这样实现起来,可能很麻烦。今天我给大家带来的是用属性动画来实现卫星菜单,用了后你会发现,核心代码仅仅100行左右就能完成,并且可以利用属性动画的特点,它具备与用户交互的特点,它的点击事件触发位置,会随着动画的位置改变而改变,说白了属性动画实质就是修改动画的属性,而补间动画则是不断调用draw()方法不断重绘来达到一种动画的效果。

实现卫星菜单的第一步:

分析一下,这样卫星菜单怎么实现?实现该菜单对布局有什么要求?

首先,来分析一下布局,布局应该是这样的,肯定有一个按钮,并且其他的子菜单都是重叠在第一个可见按钮下面,其他的子菜单都是不可见的。当点击最上面的一个按钮时,其他子菜单则呈1/4的圆弧显示,当再次点击,将这些按钮重新回到原来的位置,隐藏起来。

所以这样布局大家应该想到了什么布局吧,我们应该使用帧布局(FrameLayout),将几个ImageView重叠在一起,并且让第一个显示的按钮放在最后,这样就会第一个显示了。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/index2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/b" />

    <ImageView
        android:id="@+id/index3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/c" />

    <ImageView
        android:id="@+id/index4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/d" />

    <ImageView
        android:id="@+id/index5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/e" />

    <ImageView
        android:id="@+id/index6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/f" />

    <ImageView
        android:id="@+id/index7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/g" />

    <ImageView
        android:id="@+id/index8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:src="@drawable/h" />

    <ImageView
        android:id="@+id/index1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:src="@drawable/a" />

</FrameLayout>

那么接着分析一下具体实现的逻辑,原理图如下:

由以上原理图上可得出,可根据三角函数即可得出每个子菜单分别在X方向和Y方向上的偏移量,然后通过偏移量就可以利用的属性动画中的ObjectAnimator中指定的"translationX"和"translationY"两个属性,通过控制这两个属性偏移量从而达到偏移动画的效果。但是如何得到这个他们之间的夹角a呢?因为我们知道屏幕的一个左下角正好是一个90度角,然后看平行X轴和Y轴方向两个子菜单之间看有多少个间隔,一个间隔就是一个夹角,然后可以得到一个平均夹角:(90/间隔数)。

package com.mikyou.satellitemenutest;

import java.util.ArrayList;
import java.util.List;

import android.R.animator;
import android.R.bool;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.Window;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener{

	private int[] imageIds={//开辟一个数组用于存放各个子菜单的图片,注意:index1是最外面的图片不属于子菜单,所以只有7个子菜单
			R.id.index1
			,R.id.index2
			,R.id.index3
			,R.id.index4
			,R.id.index5
			,R.id.index6
			,R.id.index7
			,R.id.index8
	};
	private boolean flag=false;//设置一个标记变量用于实现菜单的开关。
	private List<ImageView> mImageViewsList=new ArrayList<ImageView>();//定义一个集合存放每个ImageView
	//属性动画相关
	private ObjectAnimator animatorX;//x方向的位移动画
	private ObjectAnimator animatorY;//y方向的位移动画
	private float line=400f;//这个则是line的长度,是三角形那个最长的边,根据三角函数即可得出X,Y的偏移量
	private float angle;//平均夹角
	private float sinAngle;//sinAngle=-line*sin a(a为0~90度范围内夹角)
	private float cosAngle;//cosAngle=line*cos a(a为0~90度范围内夹角)
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		initView();
	}
	private void initView() {

		for (int i = 0; i < imageIds.length; i++) {
			ImageView imageView=(ImageView) findViewById(imageIds[i]);//找到每一个ImageView的对象
			imageView.setOnClickListener(this);//给每一个ImageView设置点击事件
			mImageViewsList.add(imageView);
		}
		angle=(float) (90.0/(mImageViewsList.size()-2));//把每个子菜单的之间夹角平均分,总共8张图片除去第一张还剩7张,7个子菜单之间将会产生6个间隔,所以要减去2
	}
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.index1://表示是第一个可见的按钮
			if (!flag) {//展开卫星菜单,
				//注意:我们以flag值来代表当前菜单的状态:若为false,则表示当前是关闭状态,则需要打开菜单,所以应调用打开菜单的方法;若为true则表示当前是打开状态,则需要关闭菜单,所以应调用关闭菜单的方法
				toast("打开主菜单");
				SwitchOfSatlliteMenuWithAttributeAnimation(flag);
				flag=true;//打开菜单后,菜单当前状态也就变化了,变为打开了,所以需要更新flag的值为true
			}else{//关闭卫星菜单
				toast("关闭主菜单");
				SwitchOfSatlliteMenuWithAttributeAnimation(flag);
				flag=false;//关闭菜单后,菜单当前状态也就变化了,变为关闭了,所以需要更新flag的值为false
			}
			break;
		case R.id.index2:
			toast("相机");
			break;
		case R.id.index3:
			toast("音乐");
			break;
		case R.id.index4:
			toast("地图");
			break;
		case R.id.index5:
			toast("天气");
			break;
		case R.id.index6:
			toast("通讯录");
			break;
		case R.id.index7:
			toast("其他");
			break;
		case R.id.index8:
			toast("信息");
			break;
		default:
			break;
		}
	}
	/**
	 * @author zhongqihong
	 * 卫星菜单的开关,根据switchOfSatlliteMenu的值来判断打开或者关闭菜单
	 * */
	public  void SwitchOfSatlliteMenuWithAttributeAnimation(boolean switchOfSatlliteMenu){
		float startX,endX,startY,endY;
		for (int i = 1; i < mImageViewsList.size(); i++) {
			sinAngle=(float) (Math.sin((i-1)*angle*Math.PI/180)*-line);//sin15度 sin30度 sin45度 sin60度 sin75度 sin90度值的 变化,Math.PI/180代表1度
			cosAngle=(float) (Math.cos((i-1)*angle*Math.PI/180)*line);//cos15度 cos30度 cos45度 cos60度 cos75度 cos90度 值的变化
			AnimatorSet set =new AnimatorSet();//创建一个AnimatorSet属性动画集合
			if (!switchOfSatlliteMenu) {//打开菜单是X轴上:0->cosAngle,Y轴上:0->sinAngle
				startX=0f;
				endX=cosAngle;
				startY=0f;
				endY=sinAngle;
				set.setDuration(1000);//设置打开卫星菜单的动画时长
				set.setStartDelay(i*100);//设置每个子菜单打开的时间延迟
				set.setInterpolator(new AccelerateDecelerateInterpolator());//设置一个先加速后减速的插值器
			}else{//关闭菜单是X轴上:cosAngle->0,Y轴上:sinAngle->0
				startX=cosAngle;
				endX=0f;
				startY=sinAngle;
				endY=0f;
				set.setDuration(600);//设置关闭卫星菜单的动画时长
				set.setStartDelay(i*100);//设置每个子菜单打开的时间延迟
				set.setInterpolator(new AccelerateDecelerateInterpolator());//设置一个先加速后减速的插值器
			}
			animatorX=ObjectAnimator.ofFloat(mImageViewsList.get(i), "translationX", startX,endX);//创建X方向上位移属性动画
			animatorY=ObjectAnimator.ofFloat(mImageViewsList.get(i), "translationY", startY,endY);//创建Y方向上位移属性动画
			set.playTogether(animatorX,animatorY);//表示X,Y轴方向上的动画同时开始
			set.start();//开启动画
		}
	}
	public void toast(String str){
		Toast.makeText(MainActivity.this, str, 0).show();
	}
}

最后的运行效果:

时间: 2024-10-17 02:51:54

浅谈属性动画简单使用之实现卫星菜单(二)的相关文章

浅谈属性动画简单使用之实现爱的贝塞尔曲线浪漫告白效果(三)

谁说程序员不浪漫的啊,每次看到别人在黑程序员心中就有一种无奈,只是他们看到的是程序员不好的一面,今天我将用这个案例告诉那些人,程序猿也是一个很浪漫,很有情调的人.在程序员心中他们只想做最高效的事情,没有什么比效率更重要了.那就开始今天程序猿的告白之旅. 我们都知道属性动画有个强大的地方,它实现让某个控件按照我们指定的运动轨迹来运动.也就是说它可以按照一个抛物线来运动,也可以按照一个线性的线来运动,还可以按照我们今天所讲的贝塞尔曲线的轨迹来运动.为什么他可以按照某一个轨迹来运动呢??首先我们来分析

安卓开发_浅谈Android动画(四)

Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1.  ValueAnimator 基本属性动画类 方法 描述 setDuration(long duration) 设置动画持续时间的方法 setEvaluator(TypeEvaluator value) 设置插值计算的类型 setInterpolator(TimeInterpolator value) 设置时间插值器的类型 addUp

属性动画简单分析(二)

在<属性动画简单解析(一)>分析了属性动画ObjectAnimation的初始化流程: 1)通过ObjectAnimation的ofXXX方法,设置propertyName和values. 2)将propertyName和values封装成PropertyValueHolder对象:每个PropertyValueHolder对象持有values组成的帧序列对象KeyFrameSet对象: 3)将步骤2创建的PropertyValueHolder对象用ObjectAnimation的mValue

浅谈MD5及简单使用

原理简介: MD5即Message-Digest Algorithm 5(信息-摘要算法 第5版),用于确保信息传输完整一致.是计算机广泛使用的杂凑算法之一(又名:摘要算法.哈希算法),主流编程语言普遍已由MD5实现.将数据运算为另一固定长度值(十六进制的话:32位),是杂凑算法的基础原理,MD5的前身有MD2.MD3和MD4. MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串).除了MD5以外,

浅谈属性

  属性是OC2.0之后出来的新的语法,用来替代setter以及getter方法,使用属性可以快速创建setter以及getter方法的声明,setter以及getter方法的实现.另外还添加了对实例变量操作的安全处理 在.h文件 @property用来定义属性,NSString *属性的类型(和实例变量类型相同) name属性名和实例变量名相同)切记:@property只是自动声明setter以及getter  方法的声明 例如 @property NSString *name;   NSSt

浅谈Java回收对象的标记和对象的二次标记过程_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 一.对象的标记 1.什么是标记?怎么标记? 第一个问题相信大家都知道,标记就是对一些已死的对象打上记号,方便垃圾收集器的清理. 至于怎么标记,一般有两种方法:引用计数和可达性分析. 引用计数实现起来比较简单,就是给对象添加一个引用计数器,每当有一个地方引用它时就加1,引用失效时就减1,当计数器为0的时候就标记为可回收.这种判断效率很高,但是很多主流的虚拟机并没有采用这种方法,主要是因为它很难解决几个对象之间循环引用的

转: 浅谈C/C++中的指针和数组(二)

转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的. C语言标准对此作了说明: 规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针: 注:下面几种情况例外 1)数组名作为sizeof的操作数 2)使用&取数组的地址 规则2:下标总是与指针的偏移量

Android属性动画简单剖析

运行效果图: 先看布局文件吧,activity_main.xml: 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 and

使用属性动画简单实现view飞入效果

比较简单的效果,可以用来菜单飞入之类,作为记录吧, package com.test.animation; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import com.nineoldandroids.animation.AnimatorSet;