Android动画 实现开关按钮动画(属性动画之平移动画)实例代码

作者:lqh 时间:2023-01-02 23:06:30 

Android动画 实现开关按钮动画(属性动画之平移动画),最近做项目,根据项目需求,有一个这样的功能,实现类似开关的动画效果,经过自己琢磨及上网查找资料,终于解决了,这里就记录下:

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

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

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

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

Android动画 实现开关按钮动画(属性动画之平移动画)实例代码

 动画的使用场景

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

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

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

2. 在build.gradle文件中引入

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

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

      Android动画 实现开关按钮动画(属性动画之平移动画)实例代码

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

第一步:先定义一个布局文件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>

布局文件预缆长这样:

Android动画 实现开关按钮动画(属性动画之平移动画)实例代码

 第二步:定义自定义控件(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,动画
0
投稿

猜你喜欢

  • Jmeter post上传文件实现过程详解

    2022-01-09 14:02:28
  • 一文详解无痕埋点在Android中的实现

    2022-06-30 07:53:18
  • C#中数组Array,ArrayList,泛型List详细对比

    2023-02-22 05:44:33
  • Java中finally和return的关系实例解析

    2023-05-31 04:21:31
  • SpringBoot 添加本地 jar 文件的操作步骤

    2022-03-09 21:36:11
  • SpringBoot整合Mybatis的知识点汇总

    2023-11-15 21:56:06
  • android 封装抓取网页信息的实例代码

    2021-11-28 09:40:32
  • java自定义注解验证手机格式的实现示例

    2023-06-24 10:42:44
  • 新浪微博第三方登录界面上下拉伸图片之第三方开源PullToZoomListViewEx(二)

    2021-07-25 04:52:35
  • JAVA字符串拼接常见方法汇总

    2021-11-13 03:11:31
  • java中的session对象及其常用方法小结

    2023-11-25 02:31:33
  • SpringBoot中@ConfigurationProperties注解实现配置绑定的三种方法

    2023-03-19 12:36:25
  • Java利用apache ftp工具实现文件上传下载和删除功能

    2022-03-17 02:04:01
  • Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    2023-07-02 18:42:50
  • Java并发编程学习之Unsafe类与LockSupport类源码详析

    2022-10-21 19:07:01
  • c#通过xpath读取xml示例

    2023-07-16 01:31:05
  • java实现哈弗曼编码与反编码实例分享(哈弗曼算法)

    2023-11-25 04:54:05
  • Android自定义View实现等级滑动条的实例

    2023-12-08 19:57:53
  • c#二叉树存储介绍

    2022-11-02 07:58:30
  • Spring5新功能@Nullable注解及函数式注册对象

    2021-10-27 05:49:44
  • asp之家 软件编程 m.aspxhome.com