Android模擬開(kāi)關(guān)按鈕點(diǎn)擊打開(kāi)動(dòng)畫(huà)(屬性動(dòng)畫(huà)之平移動(dòng)畫(huà))
在Android里面,一些炫酷的動(dòng)畫(huà)確實(shí)是很吸引人的地方,讓然看了就賞心悅目,一個(gè)好看的動(dòng)畫(huà)可能會(huì)提高用戶(hù)對(duì)軟件的使用率。另外說(shuō)到動(dòng)畫(huà),在Android里面支持兩種動(dòng)畫(huà):補(bǔ)間動(dòng)畫(huà)和屬性動(dòng)畫(huà),至于這兩種動(dòng)畫(huà)的區(qū)別這里不再介紹,希望開(kāi)發(fā)者都能在使用的過(guò)程中體會(huì)兩者的不同。
本文使用屬性動(dòng)畫(huà)完成,說(shuō)到屬性動(dòng)畫(huà),肯定要提到 JakeWharton大神寫(xiě)的NineOldAndroids動(dòng)畫(huà)庫(kù),如果你的app需要在android3.0以下使用屬性動(dòng)畫(huà),那么這個(gè)庫(kù)就很有作用了,如果只需要在高版本使用,那么直接使用系統(tǒng)提供的動(dòng)畫(huà)API即可。
首先看一下本文要實(shí)現(xiàn)的動(dòng)畫(huà)效果:手指向上移動(dòng)到開(kāi)關(guān)按鈕處, 然后一個(gè)點(diǎn)擊動(dòng)作,開(kāi)關(guān)從關(guān)到開(kāi)動(dòng)畫(huà)執(zhí)行,同時(shí)手指向下移動(dòng)回到原來(lái)的位置
點(diǎn)擊圖片調(diào)轉(zhuǎn)到對(duì)應(yīng)鏈接查看動(dòng)畫(huà)
動(dòng)畫(huà)的使用場(chǎng)景
引導(dǎo)用戶(hù)去打開(kāi)某個(gè)功能的開(kāi)關(guān)按鈕或者去打開(kāi)系統(tǒng)的某項(xiàng)設(shè)置的時(shí)候,增加動(dòng)畫(huà)可以提高用戶(hù)的點(diǎn)擊率,表達(dá)的意思也更明確
實(shí)現(xiàn)之前先做好如下準(zhǔn)備工作
1. 下載nineoldandroids-2.4.0.jar的庫(kù),放到android studio 工程目錄的libs文件夾中
2. 在build.gradle文件中引入
dependencies { compile files('libs/nineoldandroids-2.4.0.jar') }
3. 準(zhǔn)備好相關(guān)的圖片資源
接下來(lái)封裝一個(gè)自定義控件來(lái)實(shí)現(xiàn)整個(gè)動(dòng)畫(huà)
第一步:先定義一個(gè)布局文件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>
布局文件預(yù)纜長(zhǎng)這樣:
第二步:定義自定義控件(SwitchOnAnimView)實(shí)現(xiàn)整個(gè)動(dòng)畫(huà)
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(); /** 開(kāi)關(guān)中間的圓圈View */ private ImageView mCirclePtImgv; /** 手指View */ private ImageView mFingerImgv; /** 手指移動(dòng)的距離 */ private float mFingerMoveDistance; /** 開(kāi)關(guān)中間的圓圈View需要移動(dòng)的距離 */ 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); // 下面兩個(gè)距離要根據(jù)UI布局來(lái)確定 mFingerMoveDistance = ViewUtil.dp2px(getContext(), 20f); mCirclePtMoveDistance = ViewUtil.dp2px(getContext(), 17.5f); } /** * 啟動(dòng)動(dòng)畫(huà) */ public void startAnim() { isStopAnim = false; // 啟動(dòng)動(dòng)畫(huà)之前先恢復(fù)初始狀態(tài) ViewHelper.setTranslationX(mCirclePtImgv, 0); mCirclePtImgv.setBackgroundResource(R.drawable.switch_off_circle_point); mFingerImgv.setBackgroundResource(R.drawable.finger_normal); startFingerUpAnim(); } /** * 停止動(dòng)畫(huà) */ public void stopAnim() { isStopAnim = true; } /** * 中間的圈點(diǎn)View平移動(dòng)畫(huà) */ private void startCirclePointAnim() { if (mCirclePtImgv == null) { return; } ObjectAnimator circlePtAnim = ObjectAnimator.ofFloat(mCirclePtImgv, "translationX", 0, mCirclePtMoveDistance); circlePtAnim.setDuration(CIRCLE_PT_ANIM_DURATION); circlePtAnim.start(); } /** * 手指向上移動(dòng)動(dòng)畫(huà) */ 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; } // 手指向上動(dòng)畫(huà)執(zhí)行完成就設(shè)置手指View背景為點(diǎn)擊狀態(tài)的背景 mFingerImgv.setBackgroundResource(R.drawable.finger_click); // 點(diǎn)擊之后為了提現(xiàn)停頓一下的感覺(jué),延遲200毫秒執(zhí)行其他動(dòng)畫(huà) mHandler.postDelayed(new Runnable() { @Override public void run() { if (mCirclePtImgv == null || mHandler == null) { return; } // 將中間圓圈View背景設(shè)置為開(kāi)關(guān)打開(kāi)狀態(tài)然后開(kāi)始向右平移 mCirclePtImgv.setBackgroundResource(R.drawable.switch_on_circle_point); startCirclePointAnim(); // 延遲100毫秒啟動(dòng)手指向下平移動(dòng)畫(huà) mHandler.postDelayed(new Runnable() { @Override public void run() { // 手指向下移動(dòng)開(kāi)始時(shí)設(shè)置手指背景為正常的狀態(tài) if (mFingerImgv != null) { mFingerImgv.setBackgroundResource(R.drawable.finger_normal); } startFingerDownAnim(); } }, 100); } }, 200); } }); fingerUpAnim.start(); } /** * 手指向下移動(dòng)動(dòng)畫(huà) */ 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) { // 手指向下移動(dòng)動(dòng)畫(huà)完成,整個(gè)動(dòng)畫(huà)流程結(jié)束,重新開(kāi)始下一次流程,循環(huán)執(zhí)行動(dòng)畫(huà),間隔1秒 mHandler.postDelayed(new Runnable() { @Override public void run() { if (isStopAnim) { return; } startAnim(); } }, 1000); } }); fingerDownAnim.start(); } }
最后一步:就是找個(gè)載體把SwitchOnAnimView加進(jìn)去,調(diào)用其startAnim方法執(zhí)行動(dòng)畫(huà),這里在一個(gè)Activity中把播放此動(dòng)畫(huà)
定義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>
定義并實(shí)現(xiàn)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); } @Override protected void onResume() { super.onResume(); mHandler.postDelayed(new Runnable() { @Override public void run() { mSwitchOnAnimView.startAnim(); } }, 500); } @Override protected void onPause() { super.onPause(); mSwitchOnAnimView.stopAnim(); } }
動(dòng)畫(huà)實(shí)現(xiàn)總結(jié):
掌握Android的動(dòng)畫(huà)并不難,難的時(shí)候怎么實(shí)現(xiàn)一些復(fù)雜的動(dòng)畫(huà),這里總結(jié)一下實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)的幾個(gè)步驟。
1. 動(dòng)畫(huà)分解:任何復(fù)雜的動(dòng)畫(huà)都可以分解為很多個(gè)原子動(dòng)畫(huà)的組合
2. 動(dòng)畫(huà)銜接時(shí)機(jī)分析:復(fù)雜動(dòng)畫(huà)分解為很多個(gè)原子動(dòng)畫(huà)之后,要重新銜接起來(lái)
這里其實(shí)就是各個(gè)原子動(dòng)畫(huà)的執(zhí)行時(shí)機(jī),誰(shuí)先誰(shuí)后還是同時(shí)執(zhí)行
3. 實(shí)現(xiàn)原子動(dòng)畫(huà):將拆解的原子動(dòng)畫(huà)依次實(shí)現(xiàn)
4. 動(dòng)畫(huà)組裝:上面都準(zhǔn)備好之后,將原子動(dòng)畫(huà)按照一定的規(guī)律組裝串聯(lián)起來(lái),整個(gè)復(fù)雜的動(dòng)畫(huà)就開(kāi)始工作了
原子動(dòng)畫(huà):本文指不能再繼續(xù)拆分的動(dòng)畫(huà)
拿本文中的動(dòng)畫(huà)來(lái)說(shuō),動(dòng)畫(huà)可以分為四個(gè):
a. 手指向上平移動(dòng)畫(huà)
b. 手指點(diǎn)擊操作(這里不是動(dòng)畫(huà),也可以當(dāng)做一個(gè)簡(jiǎn)單的動(dòng)畫(huà)吧)
c. 開(kāi)關(guān)按鈕原點(diǎn)向右平移動(dòng)畫(huà)
d. 手指向下平移動(dòng)畫(huà)。
本文動(dòng)畫(huà)執(zhí)行時(shí)機(jī)為:
a 先執(zhí)行,a 執(zhí)行完成之后立即執(zhí)行 b,b 執(zhí)行完成之后等待200ms執(zhí)行 c(體現(xiàn)點(diǎn)擊效果)
c 執(zhí)行開(kāi)始100ms后開(kāi)始執(zhí)行 d
動(dòng)畫(huà)的分解和動(dòng)畫(huà)銜接時(shí)機(jī)分析是不太容易的事,因?yàn)閼{借肉眼有時(shí)候沒(méi)法觀察出來(lái),所以播放動(dòng)畫(huà)的時(shí)候要放慢來(lái)看,如果還是不能看出來(lái),最好還是要找公司的UI同事協(xié)助分析。因?yàn)槲覀兡芎?jiǎn)單的區(qū)分平移動(dòng)畫(huà),縮放動(dòng)畫(huà)這種簡(jiǎn)單,但是我們不能區(qū)分那種正弦算法動(dòng)畫(huà)或者是另外一些其他算法控制的動(dòng)畫(huà)。本文中的動(dòng)畫(huà)相對(duì)還是比較簡(jiǎn)單,實(shí)現(xiàn)起來(lái)也比較容易,但是思想確實(shí)一樣的。
- Android開(kāi)發(fā)之開(kāi)關(guān)按鈕用法示例
- Android開(kāi)發(fā)之開(kāi)關(guān)按鈕控件ToggleButton簡(jiǎn)單用法示例
- Android 自定義Switch開(kāi)關(guān)按鈕的樣式實(shí)例詳解
- Android中ToggleButton開(kāi)關(guān)狀態(tài)按鈕控件使用方法詳解
- Android基于ImageView繪制的開(kāi)關(guān)按鈕效果示例
- Android動(dòng)畫(huà) 實(shí)現(xiàn)開(kāi)關(guān)按鈕動(dòng)畫(huà)(屬性動(dòng)畫(huà)之平移動(dòng)畫(huà))實(shí)例代碼
- Android自定義View實(shí)現(xiàn)開(kāi)關(guān)按鈕
- Android 仿蘋(píng)果IOS6開(kāi)關(guān)按鈕
- Android自定義實(shí)現(xiàn)開(kāi)關(guān)按鈕代碼
- Android自定義開(kāi)關(guān)按鈕源碼解析
相關(guān)文章
基于android中的各種顏色在drawable.xml中的值詳解
本篇文章是對(duì)在android中的各種顏色在drawable.xml中的值進(jìn)行了詳細(xì)的介紹。需要的朋友參考下2013-05-05Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果(六)
這篇文章主要為大家詳細(xì)介紹了Flutter進(jìn)階之實(shí)現(xiàn)動(dòng)畫(huà)效果第六篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android實(shí)現(xiàn)閱讀APP平移翻頁(yè)效果
這篇文章主要介紹了Android實(shí)現(xiàn)閱讀APP平移翻頁(yè)效果的具體方法,模仿多看閱讀平移翻頁(yè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03Android相冊(cè)效果(使用C#和Java分別實(shí)現(xiàn))
這篇文章主要介紹了Android相冊(cè)效果(使用C#和Java分別實(shí)現(xiàn)),原來(lái)C#也可以開(kāi)發(fā)APP,小編第一次見(jiàn)了~感覺(jué)不錯(cuò),因?yàn)樾【帟簳r(shí)不喜歡Java,所以,需要的朋友可以參考下2015-06-06android實(shí)現(xiàn)下拉菜單三級(jí)聯(lián)動(dòng)
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)下拉菜單三級(jí)聯(lián)動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10Recycleview實(shí)現(xiàn)無(wú)限自動(dòng)輪播
這篇文章主要為大家詳細(xì)介紹了Recycleview實(shí)現(xiàn)無(wú)限自動(dòng)輪播,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07Android自定義View繪制四位數(shù)隨機(jī)碼
這篇文章主要介紹了Android自定義View繪制四位數(shù)隨機(jī)碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10解決webview 第二次調(diào)用loadUrl頁(yè)面不刷新的問(wèn)題
這篇文章主要介紹了解決webview 第二次調(diào)用loadUrl頁(yè)面不刷新的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03