Android自定義水波紋動(dòng)畫(huà)Layout實(shí)例代碼
話不多說(shuō),我們先來(lái)看看效果:
Hi前輩搜索預(yù)覽
這一張是《Hi前輩》的搜索預(yù)覽圖,你可以在這里下載這個(gè)APP查看更多效果:
http://www.wandoujia.com/apps/com.superlity.hiqianbei
LSearchView
這是一個(gè)MD風(fēng)格的搜索框,集成了ripple動(dòng)畫(huà)以及search時(shí)的loading,使用很簡(jiǎn)單,如果你也需要這樣的搜索控件不妨來(lái)試試:https://github.com/onlynight/LSearchView
RippleEverywhere
女友的照片:
女友的照片:
這是一個(gè)水波紋動(dòng)畫(huà)支持庫(kù),由于使用暫時(shí)只支持Android4.0以上版本。https://github.com/onlynight/RippleEverywhere
實(shí)現(xiàn)原理
使用屬性動(dòng)畫(huà)完成該動(dòng)畫(huà)的實(shí)現(xiàn),由于android2.3以下已經(jīng)不是主流機(jī)型,故只兼容4.0以上系統(tǒng)。
關(guān)于屬性動(dòng)畫(huà),如果還有童鞋不了解可以去看看hongyang大神的這篇文章:
http://chabaoo.cn/article/82668.htm
在我看來(lái)屬性動(dòng)畫(huà)實(shí)際上就類似于定時(shí)器,所謂定時(shí)器就是獨(dú)立在主線程之外的另外一個(gè)用于計(jì)時(shí)的線程,每當(dāng)?shù)竭_(dá)你設(shè)定時(shí)間的時(shí)候這個(gè)線程就會(huì)通知你;屬性動(dòng)畫(huà)也不光是另外一個(gè)線程,他能夠操作主線程UI元素屬性就說(shuō)明了它內(nèi)部已經(jīng)做了線程同步。
基本原理
我們先來(lái)看下關(guān)鍵代碼:
@Override protected void onDraw(Canvas canvas) { if (running) { // get canvas current state final int state = canvas.save(); // add circle to path to crate ripple animation // attention: you must reset the path first, // otherwise the animation will run wrong way. ripplePath.reset(); ripplePath.addCircle(centerX, centerY, radius, Path.Direction.CW); canvas.clipPath(ripplePath); // the {@link View#onDraw} method must be called before // {@link Canvas#restoreToCount}, or the change will not appear. super.onDraw(canvas); canvas.restoreToCount(state); return; } // in a normal condition, you should call the // super.onDraw the draw the normal situation. super.onDraw(canvas); } Canvas#save()和Canvas#restoreToCount()
這個(gè)兩個(gè)方法用于繪制狀態(tài)的保存與恢復(fù)。繪制之前先保存上一次的狀態(tài);繪制完成后恢復(fù)前一次的狀態(tài);以此類推直到running成為false,中間的這個(gè)過(guò)程就是動(dòng)畫(huà)的過(guò)程。
Path#addCircle()和Canvas#clipPath()
addCircle用于在path上繪制一個(gè)圈;clipPath繪制剪切后的path(只繪制path內(nèi)的區(qū)域,其他區(qū)域不繪制)。
radiusAnimator = ObjectAnimator.ofFloat(this, "animValue", 0, 1); /** * This method will be called by {@link this#radiusAnimator} * reflection calls. * * @param value animation current value */ public void setAnimValue(float value) { this.radius = value * maxRadius; System.out.println("radius = " + this.radius); invalidate(); }
這一段是動(dòng)畫(huà)的動(dòng)效關(guān)鍵,首先要有一個(gè)隨著時(shí)間推移而變化的值,當(dāng)每次這個(gè)值變化的時(shí)候我們需要跟新界面讓view重新繪制調(diào)用onDraw方法,我們不能手動(dòng)調(diào)用onDraw方法,系統(tǒng)給我們提供的invalidate會(huì)強(qiáng)制view重繪進(jìn)而調(diào)用onDraw方法。
以上就是這個(gè)動(dòng)畫(huà)的全部關(guān)鍵原理了,下面我們來(lái)一份完整的源碼:
import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; import android.util.AttributeSet; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageView; /** * Created by lion on 2016/11/11. * <p> * RippleImageView use the {@link Path#addCircle} function * to draw the view when {@link RippleImageView#onDraw} called. * <p> * When you call {@link View#invalidate()} function,then the * {@link View#onDraw(Canvas)} will be called. In that way you * can use {@link Path#addCircle} to draw every frame, you will * see the ripple animation. */ public class RippleImageView extends ImageView { // view center x private int centerX = 0; // view center y private int centerY = 0; // ripple animation current radius private float radius = 0; // the max radius that ripple animation need private float maxRadius = 0; // record the ripple animation is running private boolean running = false; private ObjectAnimator radiusAnimator; private Path ripplePath; public RippleImageView(Context context) { super(context); init(); } public RippleImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RippleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(21) public RippleImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { ripplePath = new Path(); // initial the animator, when animValue change, // radiusAnimator will call {@link this#setAnimValue} method. radiusAnimator = ObjectAnimator.ofFloat(this, "animValue", 0, 1); radiusAnimator.setDuration(1000); radiusAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); radiusAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { running = true; } @Override public void onAnimationEnd(Animator animator) { running = false; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); centerX = (right - left) / 2; centerY = (bottom - top) / 2; maxRadius = maxRadius(left, top, right, bottom); } /** * Calculate the max ripple animation radius. * * @param left view left * @param top view top * @param right view right * @param bottom view bottom * @return */ private float maxRadius(int left, int top, int right, int bottom) { return (float) Math.sqrt(Math.pow(right - left, 2) + Math.pow(bottom - top, 2) / 2); } /** * This method will be called by {@link this#radiusAnimator} * reflection calls. * * @param value animation current value */ public void setAnimValue(float value) { this.radius = value * maxRadius; System.out.println("radius = " + this.radius); invalidate(); } @Override protected void onDraw(Canvas canvas) { if (running) { // get canvas current state final int state = canvas.save(); // add circle to path to crate ripple animation // attention: you must reset the path first, // otherwise the animation will run wrong way. ripplePath.reset(); ripplePath.addCircle(centerX, centerY, radius, Path.Direction.CW); canvas.clipPath(ripplePath); // the {@link View#onDraw} method must be called before // {@link Canvas#restoreToCount}, or the change will not appear. super.onDraw(canvas); canvas.restoreToCount(state); return; } // in a normal condition, you should call the // super.onDraw the draw the normal situation. super.onDraw(canvas); } /** * call the {@link Animator#start()} function to start the animation. */ public void startAnimation() { if (radiusAnimator.isRunning()) { radiusAnimator.cancel(); } radiusAnimator.start(); } }
以上所述是小編給大家介紹的Android自定義水波紋動(dòng)畫(huà)Layout實(shí)例代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android 自定義view實(shí)現(xiàn)水波紋動(dòng)畫(huà)效果
- Android自定義View 實(shí)現(xiàn)水波紋動(dòng)畫(huà)引導(dǎo)效果
- Android實(shí)現(xiàn)水波紋效果
- Android實(shí)現(xiàn)自定義華麗的水波紋效果
- Android自定義view實(shí)現(xiàn)水波紋進(jìn)度球效果
- Android實(shí)現(xiàn)兼容的水波紋效果
- Android特效之水波紋的實(shí)現(xiàn)
- Android仿水波紋流量球進(jìn)度條控制器
- Android項(xiàng)目實(shí)戰(zhàn)手把手教你畫(huà)圓形水波紋loadingview
- Android實(shí)現(xiàn)水波紋點(diǎn)擊效果
相關(guān)文章
基于Android6.0實(shí)現(xiàn)彈出Window提示框
這篇文章主要為大家詳細(xì)介紹了基于Android6.0實(shí)現(xiàn)彈出Window提示框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Android studio有關(guān)側(cè)滑的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android studio有關(guān)側(cè)滑的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Android 6.0 藍(lán)牙搜索不到設(shè)備原因,MIUI權(quán)限申請(qǐng)機(jī)制方法
今天小編就為大家分享一篇Android6.0 藍(lán)牙搜索不到設(shè)備原因,MIUI權(quán)限申請(qǐng)機(jī)制方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07native.js獲取手機(jī)硬件基本信息實(shí)例代碼android版
本文為大家分享了native.js獲取手機(jī)硬件基本信息實(shí)例代碼android版包括手機(jī)MAC地址,手機(jī)內(nèi)存大小,手機(jī)存儲(chǔ)空間大小,手機(jī)CPU信息等手機(jī)硬件基本信息2018-09-09Android中的常用尺寸單位(dp、sp)快速入門(mén)教程
本文詳細(xì)介紹了Android開(kāi)發(fā)中常用尺寸單位的含義,重點(diǎn)講解了sp與dp這兩個(gè)尺寸單位的本質(zhì)以及它們與px的換算公式,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-05-05Android搭建grpc環(huán)境過(guò)程分步詳解
本篇文章使用的IDE是Android Studio。這里先吐槽一句,安卓項(xiàng)目搭建grpc環(huán)境,不管是引入插件還是引入第三方庫(kù),對(duì)于版本的要求都極為苛刻,一旦版本不匹配就會(huì)報(bào)錯(cuò),所以對(duì)于版本的搭配一定要注意2023-04-04Ubuntu中為Android實(shí)現(xiàn)Application Frameworks層增加硬件訪問(wèn)服務(wù)
本文主要介紹Android實(shí)現(xiàn) Application Frameworks層增加硬件訪問(wèn)服務(wù),這里對(duì)實(shí)現(xiàn)增加硬件訪問(wèn)服務(wù)的功能做出了詳細(xì)的工作流程,并提供示例代碼,有需要的小伙伴參考下2016-08-08Android共享元素動(dòng)畫(huà)效果顯示問(wèn)題解決
什么是共享元素呢?可以理解為當(dāng)頁(yè)面跳轉(zhuǎn)是,看起來(lái)一個(gè)View屬于界面A又屬于界面B,下面這篇文章主要給大家介紹了關(guān)于Android共享元素動(dòng)畫(huà)效果顯示問(wèn)題的相關(guān)資料,需要的朋友可以參考下2022-02-02Android下Activity間通信序列化過(guò)程中的深淺拷貝淺析
這篇文章主要給大家介紹了關(guān)于Android下Activity間通信序列化過(guò)程中深淺拷貝的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10Android studio 下JNI編程實(shí)例并生成so庫(kù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android studio 下JNI編程實(shí)例并生成so庫(kù),需要的朋友可以參考下2017-09-09