Android中我們要對項目添加其他動畫效果,這對網(wǎng)站體驗會有很大的提升,今天武林技術(shù)頻道小編就帶大家了解Android自定義水波紋動畫Layout實例代碼吧。
話不多說,我們先來看看效果:

Hi前輩搜索預(yù)覽
這一張是《Hi前輩》的搜索預(yù)覽圖,你可以在這里下載這個APP查看更多效果:
http://www.wandoujia.com/apps/com.superlity.hiqianbei
LSearchView

這是一個MD風格的搜索框,集成了ripple動畫以及search時的loading,使用很簡單,如果你也需要這樣的搜索控件不妨來試試:https://github.com/onlynight/LSearchView
RippleEverywhere
女友的照片:

女友的照片:

這是一個水波紋動畫支持庫,由于使用暫時只支持Android4.0以上版本。https://github.com/onlynight/RippleEverywhere
實現(xiàn)原理
使用屬性動畫完成該動畫的實現(xiàn),由于android2.3以下已經(jīng)不是主流機型,故只兼容4.0以上系統(tǒng)。
關(guān)于屬性動畫,如果還有童鞋不了解可以去看看hongyang大神的這篇文章:
http://m.survivalescaperooms.com/article/82668.htm
在我看來屬性動畫實際上就類似于定時器,所謂定時器就是獨立在主線程之外的另外一個用于計時的線程,每當?shù)竭_你設(shè)定時間的時候這個線程就會通知你;屬性動畫也不光是另外一個線程,他能夠操作主線程UI元素屬性就說明了它內(nèi)部已經(jīng)做了線程同步。
基本原理
我們先來看下關(guān)鍵代碼:
@Overrideprotected void onDraw(Canvas canvas) {if (running) {// get canvas current statefinal 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()這個兩個方法用于繪制狀態(tài)的保存與恢復(fù)。繪制之前先保存上一次的狀態(tài);繪制完成后恢復(fù)前一次的狀態(tài);以此類推直到running成為false,中間的這個過程就是動畫的過程。
Path#addCircle()和Canvas#clipPath()
addCircle用于在path上繪制一個圈;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();}這一段是動畫的動效關(guān)鍵,首先要有一個隨著時間推移而變化的值,當每次這個值變化的時候我們需要跟新界面讓view重新繪制調(diào)用onDraw方法,我們不能手動調(diào)用onDraw方法,系統(tǒng)給我們提供的invalidate會強制view重繪進而調(diào)用onDraw方法。
以上就是這個動畫的全部關(guān)鍵原理了,下面我們來一份完整的源碼:
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.*
* RippleImageView use the {@link Path#addCircle} function * to draw the view when {@link RippleImageView#onDraw} called. *
* 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(); } }
通過武林技術(shù)頻道小編介紹的Android自定義水波紋動畫Layout實例代碼,其實很多知識都要花費時間和精力的,所以我們的學習一定要科學,保證學習質(zhì)量。
新聞熱點
疑難解答
圖片精選