在Android里面,一些炫酷的动画确实是很吸引人的地方,让然看了就赏心悦目,一个好看的动画可能会提高用户对软件的使用率。另外说到动画,在Android里面支持3种动画: 逐帧动画(Frame Animation)、补间动画(Tween Animation)和属性动画(Property Animation),至于这几种动画的区别这里不再介绍,希望开发者都能在使用的过程中体会两者的不同。

  本文使用属性动画完成,说到属性动画,肯定要提到 JakeWharton大神写的NineOldAndroids动画库,如果你的app需要在android3.0以下使用属性动画,那么这个库就很有作用了,如果只需要在高版本使用,那么直接使用系统提供的动画API即可。

首先看一下本文要实现的动画效果:手指向上移动到开关按钮处, 然后一个点击动作,开关从关到开动画执行,同时手指向下移动回到原来的位置

点击图片调转到对应Github链接查看动画

动画的使用场景

  引导用户去打开某个功能的开关按钮或者去打开系统的某项设置的时候,增加动画可以提高用户的点击率,表达的意思也更明确

实现之前先做好如下准备工作

  1. 下载nineoldandroids-2.4.0.jar的库,放到android studio 工程目录的libs文件夹中

  2. 在build.gradle文件中引入

dependencies {
compile files('libs/nineoldandroids-2.4.0.jar')
}

  3. 准备好相关的图片资源

接下来封装一个自定义控件来实现整个动画

第一步:先定义一个布局文件finger_switch_on_guide_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/switch_anim_root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"> <ImageView
android:layout_width="42dp"
android:layout_height="25dp"
android:background="@drawable/switch_container" /> <ImageView
android:id="@+id/switch_anim_circle_point"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginLeft="2.5dp"
android:layout_marginTop="2.5dp"
android:background="@drawable/switch_off_circle_point" />
</FrameLayout> <ImageView
android:id="@+id/finger_switch"
android:layout_width="34dp"
android:layout_height="41dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="25dp"
android:background="@drawable/finger_normal" />
</merge>

布局文件预缆长这样:

第二步:定义自定义控件(SwitchOnAnimView)实现整个动画

package com.androidanimation.animationview;

import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.widget.ImageView; import com.androidanimation.R;
import com.androidanimation.animations.BaseAnimatorListener;
import com.androidanimation.utils.ViewUtil;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper; /**
* Created by popfisher on 2016/9/3.
*/ public class SwitchOnAnimView extends FrameLayout { private Handler mHandler = new Handler();
/** 开关中间的圆圈View */
private ImageView mCirclePtImgv;
/** 手指View */
private ImageView mFingerImgv;
/** 手指移动的距离 */
private float mFingerMoveDistance;
/** 开关中间的圆圈View需要移动的距离 */
private float mCirclePtMoveDistance;
private static final int FINGER_ANIM_DURATION = 300;
private static final int CIRCLE_PT_ANIM_DURATION = 500; private boolean isStopAnim = false; public SwitchOnAnimView(Context context) {
this(context, null);
} public SwitchOnAnimView(Context context, AttributeSet attrs) {
super(context, attrs);
// 加载布局
LayoutInflater.from(context).inflate(R.layout.finger_switch_on_guide_layout, this, true);
initView();
} private void initView() {
mCirclePtImgv = (ImageView) findViewById(R.id.switch_anim_circle_point);
mFingerImgv = (ImageView) findViewById(R.id.finger_switch); // 下面两个距离要根据UI布局来确定
mFingerMoveDistance = ViewUtil.dp2px(getContext(), 20f);
mCirclePtMoveDistance = ViewUtil.dp2px(getContext(), 17.5f);
} /**
* 启动动画
*/
public void startAnim() {
isStopAnim = false;
// 启动动画之前先恢复初始状态
ViewHelper.setTranslationX(mCirclePtImgv, 0);
mCirclePtImgv.setBackgroundResource(R.drawable.switch_off_circle_point);
mFingerImgv.setBackgroundResource(R.drawable.finger_normal);
startFingerUpAnim();
} /**
* 停止动画
*/
public void stopAnim() {
isStopAnim = true;
} /**
* 中间的圈点View平移动画
*/
private void startCirclePointAnim() {
if (mCirclePtImgv == null) {
return;
}
ObjectAnimator circlePtAnim = ObjectAnimator.ofFloat(mCirclePtImgv, "translationX", 0, mCirclePtMoveDistance);
circlePtAnim.setDuration(CIRCLE_PT_ANIM_DURATION);
circlePtAnim.start();
} /**
* 手指向上移动动画
*/
private void startFingerUpAnim() {
ObjectAnimator fingerUpAnim = ObjectAnimator.ofFloat(mFingerImgv, "translationY", 0, -mFingerMoveDistance);
fingerUpAnim.setDuration(FINGER_ANIM_DURATION);
fingerUpAnim.addListener(new BaseAnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
if (mFingerImgv == null || mHandler == null) {
return;
}
// 手指向上动画执行完成就设置手指View背景为点击状态的背景
mFingerImgv.setBackgroundResource(R.drawable.finger_click);
// 点击之后为了提现停顿一下的感觉,延迟200毫秒执行其他动画
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mCirclePtImgv == null || mHandler == null) {
return;
}
// 将中间圆圈View背景设置为开关打开状态然后开始向右平移
mCirclePtImgv.setBackgroundResource(R.drawable.switch_on_circle_point);
startCirclePointAnim();
// 延迟100毫秒启动手指向下平移动画
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 手指向下移动开始时设置手指背景为正常的状态
if (mFingerImgv != null) {
mFingerImgv.setBackgroundResource(R.drawable.finger_normal);
}
startFingerDownAnim();
}
}, 100);
}
}, 200);
}
});
fingerUpAnim.start();
} /**
* 手指向下移动动画
*/
private void startFingerDownAnim() {
if (mFingerImgv == null) {
return;
}
ObjectAnimator fingerDownAnim = ObjectAnimator.ofFloat(mFingerImgv, "translationY", -mFingerMoveDistance, 0);
fingerDownAnim.setDuration(FINGER_ANIM_DURATION);
fingerDownAnim.addListener(new BaseAnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
// 手指向下移动动画完成,整个动画流程结束,重新开始下一次流程,循环执行动画,间隔1秒
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (isStopAnim) {
return;
}
startAnim();
}
}, 1000);
}
});
fingerDownAnim.start();
}
}

最后一步:就是找个载体把SwitchOnAnimView加进去,调用其startAnim方法执行动画,这里在一个Activity中把播放此动画

定义activity布局文件activity_finger_switchon_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_animation_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.androidanimation.animationview.SwitchOnAnimView
android:id="@+id/switch_on_anim_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/> </LinearLayout>

定义并实现Activity:FingerSwitchOnAnimActivity

package com.androidanimation;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler; import com.androidanimation.animationview.SwitchOnAnimView; public class FingerSwitchOnAnimActivity extends Activity { private Handler mHandler = new Handler();
private SwitchOnAnimView mSwitchOnAnimView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_finger_switchon_anim);
mSwitchOnAnimView = (SwitchOnAnimView) findViewById(R.id.switch_on_anim_view);
    
      mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mSwitchOnAnimView.startAnim();
}
}, 500);
} }

动画实现总结:

  掌握Android的动画并不难,难的时候怎么实现一些复杂的动画,这里总结一下实现复杂动画的几个步骤。

  1. 动画分解:任何复杂的动画都可以分解为很多个原子动画的组合

  2. 动画衔接时机分析:复杂动画分解为很多个原子动画之后,要重新衔接起来

            这里其实就是各个原子动画的执行时机,谁先谁后还是同时执行

  3. 实现原子动画:将拆解的原子动画依次实现

  4. 动画组装:上面都准备好之后,将原子动画按照一定的规律组装串联起来,整个复杂的动画就开始工作了

  原子动画:本文指不能再继续拆分的动画

  拿本文中的动画来说,动画可以分为四个:

  a. 手指向上平移动画

  b. 手指点击操作(这里不是动画,也可以当做一个简单的动画吧)

  c. 开关按钮原点向右平移动画

  d. 手指向下平移动画。

  本文动画执行时机为:

  a 先执行,a 执行完成之后立即执行 b,b 执行完成之后等待200ms执行 c(体现点击效果)

  c 执行开始100ms后开始执行 d

  

  动画的分解和动画衔接时机分析是不太容易的事,因为凭借肉眼有时候没法观察出来,所以播放动画的时候要放慢来看,如果还是不能看出来,最好还是要找公司的UI同事协助分析。因为我们能简单的区分平移动画,缩放动画这种简单,但是我们不能区分那种正弦算法动画或者是另外一些其他算法控制的动画。本文中的动画相对还是比较简单,实现起来也比较容易,但是思想确实一样的。

源码下载地址:https://github.com/PopFisher/AndroidAnimationDemos

Android动画:模拟开关按钮点击打开动画(属性动画之平移动画)的更多相关文章

  1. Android 安装应用后点击打开带来的问题

    今天安装完APP的时候.界面会显示两个button,一个完毕键,一个打开键,点击Open键之后,外部打开应用.此时,我们点击HOME键.程序将会在后台. 然后再点击该桌面上应用程序的图标,app会自己 ...

  2. Swift-使用transform 实现重复平移动画

    摘要 要实现一组重复的动画,本质上就是找到动画开始点.结束点.在动画结束的时候,触发开始点,持续这样的动作. 这里面要梳理的逻辑就是1.触发开始点和2.监听动画结束点.这两个逻辑是实现重复动画的基础. ...

  3. 【Android】 给我一个Path,还你一个酷炫动画

    本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家公布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/53040506 ...

  4. Android动画效果之Property Animation进阶(属性动画)

    前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...

  5. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  6. Android 手机卫士--平移动画实现

    本文实现如下几个界面之间的平移动画实现 本文地址:http://www.cnblogs.com/wuyudong/p/5954847.html,转载请注明出处. 分析: 导航界面移动过程中,平移动画 ...

  7. 使用css3的动画模拟太阳系行星公转

    本文介绍使用css3的animation画一个太阳系行星公转的动画,再加以改进,讨论如何画椭圆的运行轨迹.然后分析京东和人人网使用animation的实际案例,最后结合css3的clip-path做一 ...

  8. Android UI开发第四十一篇——墨迹天气3.0引导界面及动画实现

    周末升级了墨迹天气,看着引导界面做的不错,模仿一下,可能与原作者的代码实现不一样,但是实现的效果还是差不多的.先分享一篇以前的文章,android动画的基础知识,<Android UI开发第十二 ...

  9. 【读书笔记《Android游戏编程之从零开始》】16.游戏开发基础(动画)

    1. Animation动画   在Android 中,系统提供了动画类 Animation ,其中又分为四种动画效果: ● AlphaAnimation:透明度渐变动画 ● ScaleAnimati ...

随机推荐

  1. AutoMapper

    什么是AutoMapper? AutoMapper是一个对象和对象间的映射器.对象与对象的映射是通过转变一种类型的输入对象为一种不同类型的输出对象工作的.让AutoMapper有意思的地方在于它提供了 ...

  2. Socket聊天程序——客户端

    写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细 ...

  3. java字符乱码

    在java中处理字符时,经常会发生乱码,而主要出现的地方在读取文本文件时发生,或者是写入到文件中,在其他地方打开乱码. 如下例子: BufferedReader br = null; try { br ...

  4. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  5. 【原创经验分享】WCF之消息队列

    最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...

  6. 在centos7中添加一个新用户,并授权

    前言 笔记本装了一个centos,想要让别人也可以登录访问,用自己的账号确实不太好,于是准备新建一个用户给他. 创建新用户 创建一个用户名为:zhangbiao [root@localhost ~]# ...

  7. gulp初学

    原文地址:gulp初学 至于gulp与grunt的区别,用过的人都略知一二,总的来说就是2点: 1.gulp的gulpfile.js  配置简单而且更容易阅读和维护.之所以如此,是因为它们的工作方式不 ...

  8. 代码的坏味道(14)——重复代码(Duplicate Code)

    坏味道--重复代码(Duplicate Code) 重复代码堪称为代码坏味道之首.消除重复代码总是有利无害的. 特征 两个代码片段看上去几乎一样. 问题原因 重复代码通常发生在多个程序员同时在同一程序 ...

  9. ios 获取或修改网页上的内容

    UIWebView是iOS最常用的SDK之一,它有一个stringByEvaluatingJavaScriptFromString方法可以将javascript嵌     入页面中,通过这个方法我们可 ...

  10. 《Walking the callstack(转载)》

    本文转载自:https://www.codeproject.com/articles/11132/walking-the-callstack Download demo project with so ...