Android自定義View新年煙花、祝福語橫幅動(dòng)畫
新年了,項(xiàng)目中要作個(gè)動(dòng)畫,整體要求實(shí)現(xiàn)彩帶亂飛,煙花沖天而起,煙花縮放,小雞換圖,小雞飄移,橫幅裁剪、展開等動(dòng)畫效果,全局大量使用了屬性動(dòng)畫來實(shí)現(xiàn)。
如下效果圖:

我在實(shí)現(xiàn)過程中,橫幅的裁剪計(jì)算,搗騰了比較久的時(shí)間,初版采用屬性動(dòng)畫計(jì)算float的一個(gè)比率值,來配合每一幀的裁剪繪制,如下代碼:
private static class RollView extends View {
private Bitmap mBitmap;
private Rect mSrc;
private Rect mDst;
private int mRollWidth = 60;
private float mRate;
private boolean mIsStopAnim;
public RollView(Context context) {
super(context);
mSrc = new Rect();
mDst = new Rect();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) return;
drawFromMiddleByFloatCompute(canvas);
}
private void drawFromMiddleByFloatCompute(Canvas canvas) {
/*
以下src 都需要加上mBitmap. 的前綴,, 因從drawable拿到的是原始圖片寬高
而適配時(shí),可能view的寬高比 drawable的寬高還小或大
*/
final float rate = mRate;
mSrc.left = 0;
mSrc.top = 0;
mSrc.right = mRollWidth;
mSrc.bottom = mBitmap.getHeight();
mDst.left = (int) ((getWidth() / 2 - mRollWidth) - (getWidth() / 2 - mRollWidth) * rate);
mDst.top = 0;
mDst.right = mDst.left + mRollWidth + 1;//因精度問題,這里強(qiáng)制+1
mDst.bottom = getHeight();
canvas.drawBitmap(mBitmap, mSrc, mDst, null);
//中間
int sw = (int) ((mBitmap.getWidth() - mRollWidth * 2) * rate);
mSrc.left = mBitmap.getWidth() / 2 - sw / 2;
mSrc.top = 0;
mSrc.right = mSrc.left + sw;
mSrc.bottom = mBitmap.getHeight();
int dw = (int) ((getWidth() - mRollWidth * 2) * rate);
mDst.left = getWidth() / 2 - dw / 2;
mDst.top = 0;
mDst.right = mDst.left + dw;
mDst.bottom = getHeight();
canvas.drawBitmap(mBitmap, mSrc, mDst, null);
//右邊
mSrc.left = mBitmap.getWidth() - mRollWidth;
mSrc.top = 0;
mSrc.right = mBitmap.getWidth();
mSrc.bottom = mBitmap.getHeight();
mDst.left = (int) (getWidth() / 2 + (getWidth() / 2 - mRollWidth) * rate);
mDst.top = 0;
mDst.right = mDst.left + mRollWidth;
mDst.bottom = getHeight();
canvas.drawBitmap(mBitmap, mSrc, mDst, null);
}
public void setRes(int resId) {
mBitmap = getBitmapFromLocal(resId);
}
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
public void startFloatComputeAnim() {
/*
如果有float獲取比率值,從而計(jì)算出相應(yīng)的坐標(biāo)值,那么可能由于最終在轉(zhuǎn)成Rect的坐標(biāo)時(shí),
float to int ,有精度的損失:1個(gè)px 而引起效果的不理想
*/
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mIsStopAnim) {
animation.cancel();
return;
}
mRate = (float) animation.getAnimatedValue();
invalidate();
}
});
animator.setDuration(2000);
animator.start();
}
public void stopAnim() {
mIsStopAnim = true;
}
}
> 因float轉(zhuǎn)int有一個(gè)精度損失的問題,所以在計(jì)算中強(qiáng)制加上了1px(代碼中有);
這樣雖然解決了有1px沒有繪制的問題,但是會(huì)發(fā)生繪制時(shí)不夠平滑,而出現(xiàn)抖動(dòng)的情形(在某些devices上)
所以最好還是不要使用float來計(jì)算
> 后來,同事猜想使用一個(gè)固定int值 來參與計(jì)算,可能可以解決上述問題:
比如每秒30幀,這里動(dòng)畫時(shí)長(zhǎng)2秒,即共30*2=60幀;
圖片寬度、左畫軸、右畫軸 對(duì) 60幀數(shù) 做相應(yīng)的除法及其他計(jì)算,可得出一個(gè)單幀中 它們應(yīng)該運(yùn)動(dòng)的x距離
> 之后,我又想了一種,使用一個(gè)屬性動(dòng)畫,來計(jì)算出從0到getWidth()之間的 動(dòng)畫值,
從而通過計(jì)算,使得橫幅從左向右拉開, 如下:

代碼就不整體開源了
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android中Intent傳值與Bundle傳值的區(qū)別詳解
本篇文章是對(duì)android中Intent傳值與Bundle傳值的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
Android使用AsyncTask下載圖片并顯示進(jìn)度條功能
這篇文章主要介紹了Android使用AsyncTask下載圖片并顯示進(jìn)度條功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
Android registerForActivityResult動(dòng)態(tài)申請(qǐng)權(quán)限案例詳解
這篇文章主要介紹了Android registerForActivityResult動(dòng)態(tài)申請(qǐng)權(quán)限案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Android自定義控件之組合控件學(xué)習(xí)筆記分享
這篇文章主要為大家分享了Android自定義控件之組合控件學(xué)習(xí)筆記,具有一定的實(shí)用性和參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
解決Bitmap通過getWidth和getHeight獲取尺寸不符的問題
這篇文章主要介紹了解決Bitmap通過getWidth和getHeight獲取尺寸不符的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Android開發(fā)教程之調(diào)用攝像頭功能的方法詳解
這篇文章主要介紹了Android調(diào)用攝像頭功能的方法,詳細(xì)分析了Android調(diào)用攝像頭功能的權(quán)限設(shè)置、功能代碼與實(shí)現(xiàn)步驟,需要的朋友可以參考下2016-06-06

