今天開始記錄工作中遇到的需要實(shí)現(xiàn)的動畫效果實(shí)現(xiàn)自定義view動畫,后期會有一些列動畫設(shè)計(jì)思路的文章控妻。
在這里分享的是設(shè)計(jì)實(shí)現(xiàn)思路壶愤,僅供學(xué)習(xí)使用对雪,讓大家拿到稍微復(fù)雜點(diǎn)的動畫的時候要知道該如何去一步步分解實(shí)現(xiàn),而不是抱怨
下邊就先來看看設(shè)計(jì)需要的效果圖及我們最終實(shí)現(xiàn)的效果圖当犯,畢竟有圖有真相嘛垢村!
其實(shí)我剛拿到設(shè)計(jì)圖的時候心想,MD直接給一張gif圖不就行了何必這個麻煩吶嚎卫,
隨后冷靜下來之后(其實(shí)就是抱怨之后)想想作為一名Android開發(fā)者總不能什么動畫都依賴設(shè)計(jì)師吧嘉栓,那樣的話會顯得我們開發(fā)者沒什么卵用啊,說不定還會被設(shè)計(jì)師鄙視哦拓诸,
于是就開始了動畫分析及實(shí)現(xiàn)之旅侵佃。
通過這個gif動畫我們分析出動畫過程的實(shí)質(zhì):
一個長方形(或者是圓角長方形)逐漸過渡成為兩邊是半圓的長方形,于此同時長方形兩邊向中間靠攏最終形成一個圓奠支,然后圓上升一定高度馋辈,最后在圓里邊畫出對勾(?).整個動畫分解的其實(shí)就是這幾個部分,那么我們該如何實(shí)現(xiàn)吶倍谜,不要捉急迈螟,繼續(xù)往下看。
第一步:我們要先畫出一個圓角矩形吧
/**
* 繪制帶圓角的矩形
*
* @param canvas 畫布
*/
private void draw_oval_to_circle(Canvas canvas) {
//這里是對矩形的位置大小的設(shè)置
rectf.left = two_circle_distance;
rectf.top = 0;
rectf.right = width - two_circle_distance;
rectf.bottom = height;
//畫圓角矩形
canvas.drawRoundRect(rectf, circleAngle, circleAngle, paint);
}
圓角矩形繪制完成之后就是改變圓角半徑的大小使其兩邊形成半圓的效果尔崔,那么怎么才能讓他成為半圓吶井联,來看看一張圖,若要繪制成半圓效果您旁,那么這個圓的直徑就是view自身的高度,那么這個圓的半徑就是height/2
/**
* 設(shè)置矩形過度圓角矩形的動畫
*/
private void set_rect_to_angle_animation() {
animator_rect_to_angle = ValueAnimator.ofInt(0, height / 2);
animator_rect_to_angle.setDuration(duration);
animator_rect_to_angle.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
circleAngle = (int) animation.getAnimatedValue();
invalidate();
}
});
}
添加動畫之后的效果如下
第二步:當(dāng)矩形兩邊都是半圓之后就要處理使其向中間靠攏逐漸形成一個圓轴捎,那么問題又來了鹤盒,需要向中間移動多少吶蚕脏,并且怎么移動才能使兩邊都想中間聚攏吶
下邊來看一張圖分析一下
有圖可知移動的距離是(width-height)/2,然后在寫一個動畫讓其改變距離最終兩個半圓靠攏在一起形成圓
/**
* 設(shè)置圓角矩形過度到圓的動畫
* default_two_circle_distance = (w-h)/2
*/
private void set_rect_to_circle_animation() {
animator_rect_to_square = ValueAnimator.ofInt(0, default_two_circle_distance);
animator_rect_to_square.setDuration(duration);
animator_rect_to_square.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
two_circle_distance = (int) animation.getAnimatedValue();
//在靠攏的過程中設(shè)置文字的透明度,使文字逐漸消失的效果
int alpha = 255 - (two_circle_distance * 255) / default_two_circle_distance;
textPaint.setAlpha(alpha);
invalidate();
}
});
}
完成上邊代碼后再來看下效果
第三步:讓圓上移移動距離侦锯。這個移動很好實(shí)現(xiàn),直接改變Y軸方法的坐標(biāo)就行了驼鞭,這個很簡單就直接看代碼吧
/**
* 設(shè)置view上移的動畫
*/
private void set_move_to_up_animation() {
final float curTranslationY = this.getTranslationY();
animator_move_to_up = ObjectAnimator.ofFloat(this, "translationY", curTranslationY, curTranslationY - move_distance);
animator_move_to_up.setDuration(duration);
animator_move_to_up.setInterpolator(new AccelerateDecelerateInterpolator());
}
第四步:在圓中繪制一個對勾,而且是帶動畫的對勾尺碰,讓對勾以動畫的形式慢慢繪制出來
如果對相關(guān)API不熟悉的話不知道會怎么去實(shí)現(xiàn)吶挣棕,或許你會想通過繪制線的方式,在對勾起點(diǎn)開始不斷改變移動點(diǎn)的坐標(biāo)進(jìn)行繪制亲桥,那么怎么獲取這些點(diǎn)的坐標(biāo)吶洛心,這里我們使用Path和DashPathEffect兩個方法實(shí)現(xiàn),對DashPathEffect不了解的小伙伴可以去查一下文檔哦
DashPathEffect這個類的作用就是將Path的線段虛線化题篷。
構(gòu)造函數(shù)為DashPathEffect(float[] intervals, float offset)词身,其中intervals為虛線的ON和OFF數(shù)組,該數(shù)組的length必須大于等于2番枚,phase為繪制時的偏移量法严。
我們先拿到對勾的path路徑在對其改變偏移量加上DashPathEffect就能實(shí)現(xiàn)動態(tài)繪制對勾的效果了,那么怎么計(jì)算對勾的起點(diǎn)折點(diǎn)和終點(diǎn)的坐標(biāo)吶葫笼,在網(wǎng)上找了一個不錯的圖片深啤,如果你的設(shè)計(jì)師直接把位置給你標(biāo)明的很詳細(xì)的話你就省了這些自己計(jì)算的麻煩
/**
* 繪制對勾
* 下邊計(jì)算比例是參考網(wǎng)上一些例子加上自己一步一步嘗試的出來的比例,僅供參考
* 如果條件允許最好還是讓設(shè)計(jì)師給你標(biāo)明一下比例哦路星!
*/
private void initOk() {
//對勾的路徑
path.moveTo(default_two_circle_distance + height / 8 * 3, height / 2);
path.lineTo(default_two_circle_distance + height / 2, height / 5 * 3);
path.lineTo(default_two_circle_distance + height / 3 * 2, height / 5 * 2);
pathMeasure = new PathMeasure(path, true);
}
/**
* 繪制對勾的動畫
*/
private void set_draw_ok_animation() {
animator_draw_ok = ValueAnimator.ofFloat(1, 0);
animator_draw_ok.setDuration(duration);
animator_draw_ok.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
startDrawOk = true;
float value = (Float) animation.getAnimatedValue();
effect = new DashPathEffect(new float[]{pathMeasure.getLength(), pathMeasure.getLength()}, value * pathMeasure.getLength());
okPaint.setPathEffect(effect);
invalidate();
}
});
}
再來看效果
至此動畫分解都已完成溯街,但是機(jī)智的你應(yīng)該已經(jīng)發(fā)現(xiàn)問題了,就是感覺動畫播放銜接的不是很好奥额,那么接下來我們就處理這個問題苫幢,回到最初的效果圖上,矩形變圓角和縮放成圓形是同時進(jìn)行的垫挨,那么我們有什么辦法可以實(shí)現(xiàn)動畫同時播放吶韩肝,哈哈,身為老司機(jī)的想必已經(jīng)知道了使用AnimatorSet九榔,他可以播放動畫集哀峻、順序播放等,那么我們就開始處理吧
我們讓矩形變圓角和矩形往中間縮放同時進(jìn)行哲泊,然后圓在上移剩蟀,最后繪制對勾
animatorSet
.play(animator_move_to_up)
.before(animator_draw_ok)
.after(animator_rect_to_square)
.after(animator_rect_to_angle);
最終奉上我們自己一步一步完整實(shí)現(xiàn)的效果圖
至此我們可以理直氣壯地帶著作品找設(shè)計(jì)師互懟了