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

我在實(shí)現(xiàn)過(guò)程中,橫幅的裁剪計(jì)算,搗騰了比較久的時(shí)間,初版采用屬性動(dòng)畫(huà)計(jì)算float的一個(gè)比率值,來(lái)配合每一幀的裁剪繪制,如下代碼:
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;//因精度問(wèn)題,這里強(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è)精度損失的問(wèn)題,所以在計(jì)算中強(qiáng)制加上了1px(代碼中有);
這樣雖然解決了有1px沒(méi)有繪制的問(wèn)題,但是會(huì)發(fā)生繪制時(shí)不夠平滑,而出現(xiàn)抖動(dòng)的情形(在某些devices上)
所以最好還是不要使用float來(lái)計(jì)算
> 后來(lái),同事猜想使用一個(gè)固定int值 來(lái)參與計(jì)算,可能可以解決上述問(wèn)題:
比如每秒30幀,這里動(dòng)畫(huà)時(shí)長(zhǎng)2秒,即共30*2=60幀;
圖片寬度、左畫(huà)軸、右畫(huà)軸 對(duì) 60幀數(shù) 做相應(yīng)的除法及其他計(jì)算,可得出一個(gè)單幀中 它們應(yīng)該運(yùn)動(dòng)的x距離
> 之后,我又想了一種,使用一個(gè)屬性動(dòng)畫(huà),來(lái)計(jì)算出從0到getWidth()之間的 動(dòng)畫(huà)值,
從而通過(guò)計(jì)算,使得橫幅從左向右拉開(kāi), 如下:

代碼就不整體開(kāi)源了
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android studio 出現(xiàn)錯(cuò)誤Run with --stacktrace option to get the s
這篇文章主要介紹了 Android studio 出現(xiàn)錯(cuò)誤Run with --stacktrace option to get the stack trace. Run with --info or --debu的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android高仿京東垂直循環(huán)滾動(dòng)新聞欄
通過(guò)自定義的LinearLayout,并且textView能夠循環(huán)垂直滾動(dòng),而且條目可以點(diǎn)擊,顯示區(qū)域最多顯示2個(gè)條目,并且還有交替的屬性垂直移動(dòng)的動(dòng)畫(huà)效果,通過(guò)線(xiàn)程來(lái)控制滾動(dòng)的實(shí)現(xiàn)2016-03-03
Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解
這篇文章主要介紹了Android 手機(jī)瀏覽器調(diào)試使用Chrome進(jìn)行調(diào)試實(shí)例詳解的相關(guān)資料,這里提供了實(shí)例,需要的朋友可以參考下2016-12-12
深入理解Android中Scroller的滾動(dòng)原理
今天給大家講解的是Scroller類(lèi)的滾動(dòng)實(shí)現(xiàn)原理,可能很多朋友不太了解該類(lèi)是用來(lái)干嘛的,但是研究Launcher的朋友應(yīng)該對(duì)他很熟悉,Scroller類(lèi)是滾動(dòng)的一個(gè)封裝類(lèi),可以實(shí)現(xiàn)View的平滑滾動(dòng)效果,而我們今天就來(lái)探究一下為什么Scroller能夠?qū)崿F(xiàn)平滑滾動(dòng)。2016-08-08
Android中判斷網(wǎng)絡(luò)連接是否可用及監(jiān)控網(wǎng)絡(luò)狀態(tài)
獲取網(wǎng)絡(luò)信息需要在AndroidManifest.xml文件中加入相應(yīng)的權(quán)限,接下來(lái)詳細(xì)介紹Android中判斷網(wǎng)絡(luò)連接是否可用及監(jiān)控網(wǎng)絡(luò)狀態(tài),感興趣的朋友可以參考下2012-12-12
Android EditText實(shí)現(xiàn)分割輸入內(nèi)容
這篇文章主要為大家詳細(xì)介紹了Android EditText實(shí)現(xiàn)分割輸入內(nèi)容的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Ubuntu中為Android實(shí)現(xiàn)Application Frameworks層增加硬件訪(fǎng)問(wèn)服務(wù)
本文主要介紹Android實(shí)現(xiàn) Application Frameworks層增加硬件訪(fǎng)問(wèn)服務(wù),這里對(duì)實(shí)現(xiàn)增加硬件訪(fǎng)問(wèn)服務(wù)的功能做出了詳細(xì)的工作流程,并提供示例代碼,有需要的小伙伴參考下2016-08-08

