編輯:關於Android編程
Android動畫 實現開關按鈕動畫(屬性動畫之平移動畫),最近做項目,根據項目需求,有一個這樣的功能,實現類似開關的動畫效果,經過自己琢磨及上網查找資料,終於解決了,這裡就記錄下:
在Android裡面,一些炫酷的動畫確實是很吸引人的地方,讓然看了就賞心悅目,一個好看的動畫可能會提高用戶對軟件的使用率。另外說到動畫,在Android裡面支持3種動畫: 逐幀動畫(Frame Animation)、補間動畫(Tween Animation)和屬性動畫(Property Animation),至於這幾種動畫的區別這裡不再介紹,希望開發者都能在使用的過程中體會兩者的不同。
本文使用屬性動畫完成,說到屬性動畫,肯定要提到 JakeWharton大神寫的NineOldAndroids動畫庫,如果你的app需要在android3.0以下使用屬性動畫,那麼這個庫就很有作用了,如果只需要在高版本使用,那麼直接使用系統提供的動畫API即可。
首先看一下本文要實現的動畫效果:手指向上移動到開關按鈕處, 然後一個點擊動作,開關從關到開動畫執行,同時手指向下移動回到原來的位置
點擊圖片調轉到對應鏈接查看動畫

動畫的使用場景
引導用戶去打開某個功能的開關按鈕或者去打開系統的某項設置的時候,增加動畫可以提高用戶的點擊率,表達的意思也更明確
實現之前先做好如下准備工作
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 Studio創建AVD
Android Studio是專門為Android開發設計的IDE,比Eclipse開發Android更加方便、快捷。安裝Android Studio以後,想運行AVD,
qq火炬傳遞失敗怎麼辦
2016裡約奧運會馬上就要開始了,QQ新推出了一個有趣的ar火炬傳遞功能,讓所有人都可以體驗點亮火炬的樂趣,非常受大眾的歡迎,但是也有人反映,在qq火炬傳遞
Android用PopupWindow實現自定義Dailog
Android的PopupWindow是個很有用的widget,利用它可以實現懸浮窗體的效果,比如實現一個懸浮的菜單,最常見的應用就是在視頻播放界面裡,做一個工具欄,用來
git 對比兩個分支差異
比如我們有 2 個分支:master, dev,現在想查看這兩個 branch 的區別,有以下幾種方式:1.查看 dev 有,而 master 中沒有的:git log