今天给大家带来动画的实战练习,今天是一年一度的高考,祝各位考生能梦寐以求.
- 完成效果
- ListView滑动进入动画
- **android:animationOrder = \”normal\”** 正序播放效果:
- **android:animationOrder=\”reverse\”** 倒序播放效果
- **android:animationOrder=\”random\”** 随机效果
- 动态代码实现
完成效果
今天给大家带来2个效果:
- 一个是ListView的滑动进入
- 另一个是右下角的点击弹框效果
ListView滑动进入动画
第一步,先在布局中使用LIstView,在res/layout_animation创建文件,并使用android:layoutAnimation=\”@anim/layout_animation\”引用到ListView中
第二步:layout_animation文件根目录为layoutAnimation
<?xml version=\"1.0\" encoding=\"utf-8\"?><layoutAnimation xmlns:android=\"http://schemas.android.com/apk/res/android\"android:delay=\"0.3\"android:animationOrder=\"normal\"android:animation=\"@anim/set_buttonanimator_activity\"></layoutAnimation>
- android:delay= “float” 可以理解为每个个item相隔滑动的时间
- android:animationOrder= “normal | random | reverse”normal 按顺序弹出
- random 随机弹出
- reverse 倒序弹出
第三步: 在anim下创建set_buttonanimator_activity.xml设置向右滑动,并且透明度由0-1,时长为1s
<set xmlns:android=\"http://schemas.android.com/apk/res/android\" android:duration=\"1000\" ><translate android:fromXDelta=\"-50%p\" android:toXDelta=\"0\"/><alpha android:fromAlpha=\"0.0\" android:toAlpha=\"1.0\"/></set>
第四步:在布局中使用ListView
ListView listView = findViewById(R.id.listview);ArrayList<String> list = new ArrayList<>();list.add(\"测试数据1\");list.add(\"测试数据2\");list.add(\"测试数据3\");list.add(\"测试数据4\");list.add(\"测试数据5\");list.add(\"测试数据6\");这里使用的布局是android中自带的ArrayAdapter<String> stringArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, list);listView.setAdapter(stringArrayAdapter);
<layoutAnimation xmlns:android=\"http://schemas.android.com/apk/res/android\"android:delay=\"0.3\"android:animationOrder=\"normal\"android:animation=\"@anim/set_buttonanimator_activity\"></layoutAnimation>
android:animationOrder = “normal” 正序播放效果:
android:animationOrder=“reverse” 倒序播放效果
android:animationOrder=“random” 随机效果
动态代码实现
//代码设置通过加载XML动画设置文件来创建一个Animation对象;Animation animation= AnimationUtils.loadAnimation(this,R.anim.set_buttonanimator_activity); //得到一个LayoutAnimationController对象;LayoutAnimationController controller = new LayoutAnimationController(animation); //设置控件显示的顺序;controller.setOrder(LayoutAnimationController.ORDER_RANDOM); //设置控件显示间隔时间;controller.setDelay(0.3f); //为ListView设置LayoutAnimationController属性;listView.setLayoutAnimation(controller);listView.startLayoutAnimation();
- ORDER_NORMAL = 0 正常顺序
- ORDER_REVERSE = 1倒序
- ORDER_RANDOM = 2 随机顺序
大家可以从源码中找到注释来看是什么意思;
菜单弹框效果:
滑动的效果很简单,alpha从0 – 1 , scale 从0 – 1,最复杂的就是找到每个控件对应的X,和Y的位置,接下来给大家简单分析一下.
从这张图中可以简单地看出,想要求得X和Y的距离,就必须获得半径r,和夹角a,半径默认为600,那么每个a的夹角是多少呢?从黄色控件和,紫色控件来看,他们的夹角为直角,就是90°,那么他们吧5个控件分为了4等份,那么每一份的夹角就是90 / 4 = 22.5°,那么蓝色与紫色的夹角就是22.5 * 2 = 45°,红色与紫色就是 22.5 * 3 = 67.5黄色与紫色就是22.5 * 4 = 90°.然后通过数学公式:
X = r * sin(a)
Y = r * cos(a)
即可获得X和Y的坐标
想必大家知道Java提供了一个Math类:
/*** Returns the trigonometric sine of an angle. Special cases:* <ul><li>If the argument is NaN or an infinity, then the* result is NaN.* <li>If the argument is zero, then the result is a zero with the* same sign as the argument.</ul>** <p>The computed result must be within 1 ulp of the exact result.* Results must be semi-monotonic.** @param a an angle, in radians.* @return the sine of the argument.*/求正弦值@CriticalNativepublic static native double sin(double a);/*** Returns the trigonometric cosine of an angle. Special cases:* <ul><li>If the argument is NaN or an infinity, then the* result is NaN.</ul>** <p>The computed result must be within 1 ulp of the exact result.* Results must be semi-monotonic.** @param a an angle, in radians.* @return the cosine of the argument.*/求余弦值@CriticalNativepublic static native double cos(double a);/*** Returns the trigonometric tangent of an angle. Special cases:* <ul><li>If the argument is NaN or an infinity, then the result* is NaN.* <li>If the argument is zero, then the result is a zero with the* same sign as the argument.</ul>** <p>The computed result must be within 1 ulp of the exact result.* Results must be semi-monotonic.** @param a an angle, in radians.* @return the tangent of the argument.*/求正切值@CriticalNativepublic static native double tan(double a);
注意:这里的参数不是填的角度,而是填的角度对应的弧度
接下来看看什么是弧度:
简单的来说,夹角a对面的就是a的弧度;
使用Math类中的toRadians(flaot)方法,参数填的是角度
/*** Converts an angle measured in degrees to an approximately* equivalent angle measured in radians. The conversion from* degrees to radians is generally inexact.** @param angdeg an angle, in degrees* @return the measurement of the angle {@code angdeg}* in radians.* @since 1.2*/public static double toRadians(double angdeg) {return angdeg / 180.0 * PI;}
这样就可以初略的得出:
X = r * sin (Math.toRadians(22))
Y = r * cos (Math.toRadians(22))
接下来直接上代码了:
<?xml version=\"1.0\" encoding=\"utf-8\"?><RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"xmlns:app=\"http://schemas.android.com/apk/res-auto\"xmlns:tools=\"http://schemas.android.com/tools\"android:layout_width=\"match_parent\"android:layout_height=\"match_parent\"tools:context=\".ButtonAnimatorActivity\"><Buttonandroid:id=\"@+id/openButton\"style=\"@style/OpenButton\"android:background=\"@color/colorPrimary\"android:layout_margin=\"30dp\"/><Buttonandroid:id=\"@+id/bt1\"style=\"@style/OpenButton\"android:background=\"#ff00ff\"android:visibility=\"gone\"android:layout_margin=\"30dp\"/><Buttonandroid:id=\"@+id/bt2\"style=\"@style/OpenButton\"android:background=\"#00ffff\"android:visibility=\"gone\"android:layout_margin=\"30dp\"/><Buttonandroid:id=\"@+id/bt3\"style=\"@style/OpenButton\"android:background=\"#0048FF\"android:visibility=\"gone\"android:layout_margin=\"30dp\"/><Buttonandroid:id=\"@+id/bt4\"style=\"@style/OpenButton\"android:background=\"#FF003B\"android:layout_margin=\"30dp\"android:visibility=\"gone\"/><Buttonandroid:id=\"@+id/bt5\"style=\"@style/OpenButton\"android:visibility=\"gone\"android:background=\"#FFDD00\"android:layout_margin=\"30dp\"/></RelativeLayout>
将共同的代码抽取到values/styles下,然后在引用,可以减少写很多重复代码;
<style name=\"OpenButton\" ><item name=\"android:layout_width\">50dp</item><item name=\"android:layout_height\">50dp</item><item name=\"android:layout_alignParentEnd\">true</item><item name=\"android:layout_alignParentBottom\">true</item></style>
接下来获取控件Id,并且设置点击事件:
private Button openButton;private Button bt1;private Button bt2;private Button bt3;private Button bt4;private Button bt5;openButton = findViewById(R.id.openButton);bt1 = findViewById(R.id.bt1);bt2 = findViewById(R.id.bt2);bt3 = findViewById(R.id.bt3);bt4 = findViewById(R.id.bt4);bt5 = findViewById(R.id.bt5);openButton.setOnClickListener(this);bt1.setOnClickListener(this);bt2.setOnClickListener(this);bt3.setOnClickListener(this);bt4.setOnClickListener(this);bt5.setOnClickListener(this);
设置此变量是为了设置点击按钮出现不同的效果,因为这里需要打开和关闭Boolean buttonType = false;@Overridepublic void onClick(View v) {if (v.getId() == R.id.openButton) {if (!buttonType) {buttonType = true;打开bt1 - bt5setOpenButton(bt1,0,5,600);setOpenButton(bt2,1,5,600);setOpenButton(bt3,2,5,600);setOpenButton(bt4,3,5,600);setOpenButton(bt5,4,5,600);}else{buttonType = false;关闭bt1 - bt5setCloseButton(bt1,0,5,600);setCloseButton(bt2,1,5,600);setCloseButton(bt3,2,5,600);setCloseButton(bt4,3,5,600);setCloseButton(bt5,4,5,600);}}else{Toast.makeText(this, \"点击了\"+v.getId(), Toast.LENGTH_SHORT).show();}}
打开按钮方法:
/**** @param button 控件对象* @param index 当前按钮下标* @param number 按钮总个数* @param radio 圆的半径*/private void setOpenButton(Button button, int index, int number, int radio) {button.setVisibility(View.VISIBLE);double degree = Math.toRadians(90)/(number - 1) * index;Log.i(\"setOpenButton\",degree+\"\");这里需要注意,获取到结果之后要加负号(-),因为正数是往右下角弹出的,咋们现在需要向左上角弹出.float x = (float) -(radio * Math.sin(degree));float y = (float) -(radio * Math.cos(degree));Log.i(\"szjdegree\"+index,x+\"\\t\\t\"+y);AnimatorSet animatorSet = new AnimatorSet();animatorSet.playTogether(ObjectAnimator.ofFloat(button,\"translationX\",0,x),ObjectAnimator.ofFloat(button,\"translationY\",0,y),ObjectAnimator.ofFloat(button,\"alpha\",0,1),ObjectAnimator.ofFloat(button,\"scaleX\",0,1),ObjectAnimator.ofFloat(button,\"scaleY\",0,1));animatorSet.setDuration(500);animatorSet.start();}
按钮的关闭方法:
private void setCloseButton(final Button button, int index, int number, int radio) {double degree = Math.toRadians(90) / (number - 1) * index;int x = (int) - (radio * Math.sin(degree));int y = (int) - (radio * Math.cos(degree));Log.i(\"setCloseButton\",degree+\"\\n\");Log.i(\"setXXX\"+ index,x+\"\\t\\t\"+y);AnimatorSet animatorSet = new AnimatorSet();animatorSet.playTogether(ObjectAnimator.ofFloat(button,\"translationX\",x , 0),ObjectAnimator.ofFloat(button,\"translationY\",y , 0),ObjectAnimator.ofFloat(button,\"alpha\",1 , 0),ObjectAnimator.ofFloat(button,\"scaleX\",1 , 0),ObjectAnimator.ofFloat(button,\"scaleY\",1 , 0));animatorSet.setDuration(500);animatorSet.start();new Handler().postDelayed(new Runnable() {@Overridepublic void run() {button.setVisibility(View.INVISIBLE);}},500);}
按钮的打开和关闭都是一样的,只是有一点不同:
- 打开是从0 – X && 0 – Y
- 关闭时从 X – 0 &* Y – 0
要有细心地朋友可能发现在关闭的时候还多了一个方法:
new Handler().postDelayed(new Runnable() {@Overridepublic void run() {button.setVisibility(View.INVISIBLE);}},500);
为什么要加这个东西呢?咋们先看看不加这个东西的效果:
为什么会出现这种情况呢?因为在第二次缩放控件的时候,他只是回到了绿色控件的地方,并且透明度(alpha)由1变到了0,只是他的状态不存在了,可是他的值还有,所以他不显示的时候需要吧他隐藏掉,
我这里用到的是handler的延时操作:
new Handler().postDelayed(new Runnable() {@Overridepublic void run() {button.setVisibility(View.INVISIBLE);}},500);
因为代码执行的很快很快,所以不用担心有什么问题,如果觉得不保险,也可以对animatorSet监听,当动画结束的时候会响应onAnimationEnd()事件
animatorSet.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {button.setVisibility(View.INVISIBLE);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});
菜单点击效果参考链接:
启舰.
ListView滑动效果参考文档:: 启舰.
Git地址链接: langyangyang.
请各位大佬留下宝贵的建议.