自定義View動(dòng)畫(huà)


最近看到幾篇自定義View和動(dòng)畫(huà)效果的文章,于是想自己抽幾個(gè)練練手,主要是鞏固和分析一下,加深自定義view和viewgroup流程。

58同城頁(yè)面加載動(dòng)畫(huà):



一.分析

需求如上圖。先觀察一下,這個(gè)動(dòng)畫(huà)可以分為三部分:第一部分是三角形,圓形和矩形在做同樣一個(gè)平移動(dòng)畫(huà),第二部分是做縮放動(dòng)畫(huà)的陰影,第三是不動(dòng)的文字部分。

這個(gè)效果有很多種實(shí)現(xiàn)方式,今天主要研究自定義VIEW怎么去實(shí)現(xiàn)主體部分的動(dòng)畫(huà)。

動(dòng)畫(huà)的主體部分是這個(gè)一直在變形狀并上下平移的部分,先不管陰影和文字。仔細(xì)觀察,這部分一直在做同一組動(dòng)畫(huà),那么我們只需要在動(dòng)畫(huà)的某個(gè)臨界點(diǎn)改變它的形狀即可,再深入一點(diǎn),如果用自定view ,只需要在這個(gè)臨界點(diǎn)重新draw一個(gè)形狀即可,即調(diào)用invalidate(),在onDraw()里面判斷畫(huà)圓形,三角形還是矩形。

在這里,我們將三角形,圓形和矩形看成同一個(gè)view,這個(gè)view平移到底部后然后彈回去時(shí)改變了形狀然后重復(fù)這一過(guò)程。作者在這兒采用的是屬性動(dòng)畫(huà)ObjectAnimation去實(shí)現(xiàn)的平移,只需要作出向下平移的這段動(dòng)畫(huà)即可,彈回去這段可以調(diào)用setRepeatMode(ObjectAnimator.REVERSE)讓其重復(fù)時(shí)反向即可,臨界點(diǎn)的操作可以到AnimatorListener里面去做。

二.主要代碼部分

1.自定義一個(gè)View,先畫(huà)出圖形,設(shè)置枚舉狀態(tài)

private Paint paint=newPaint(Paint.ANTI_ALIAS_FLAG);//到構(gòu)造方法初始化,此處省略

private int width;

private float height;

private boolean isFirst=true;//第一次invalidate()

private int radius;//圓半徑

private int mcount=1;//累加動(dòng)畫(huà)重復(fù)次數(shù)

privateViewShapemshape=ViewShape.CIRCLE;//默認(rèn)圓形

@Override

protected voidonSizeChanged(intw, inth, intoldw, intoldh) {

super.onSizeChanged(w,h,oldw,oldh);

width=(float)w;//

height=(float) h;

radius=width/20;

doAnimation();//動(dòng)畫(huà)

}

//繪制

@Override

protected voidonDraw(Canvas canvas) {

super.onDraw(canvas);

if(mshape==ViewShape.CIRCLE) {//圓形

paint.setColor(Color.BLUE);

canvas.drawCircle(width/2,height/4,radius,paint);

}else if(mshape==ViewShape.TRIANGLE){//三角形,用Path繪制,需要計(jì)算坐標(biāo)

paint.setColor(Color.GREEN);

Path path=newPath();

path.moveTo(width/2,height/4-radius);

path.lineTo(width/2-radius,height/4+radius);

path.lineTo(width/2+radius,height/4+radius);

canvas.drawPath(path,paint);

}else{//矩形

paint.setColor(Color.RED);

canvas.drawRect(width/2-radius,height/4-radius,width/2+radius,height/4+radius,paint);

}}

//列出枚舉

public enum ViewShape{

CIRCLE,RANGE,TRIANGLE

}

2.動(dòng)畫(huà)效果,以及設(shè)置監(jiān)聽(tīng)去改變枚舉狀態(tài)

private void doAnimation(){

ObjectAnimator anim=ObjectAnimator.ofFloat(this,"translationY",0f,height-height/3);

anim.setInterpolator(newAccelerateInterpolator());

anim.setDuration(800);

anim.setRepeatMode(ObjectAnimator.REVERSE);

anim.setRepeatCount(ObjectAnimator.INFINITE);

anim.start();

anim.addListener(newAnimator.AnimatorListener() {

@Override

public voidonAnimationRepeat(Animator animation) {//只需要在重復(fù)監(jiān)聽(tīng)里操作即可

if(isFirst){

isFirst=false;

}else{

if(mcount%2==0){//判斷彈上去的時(shí)候,圖形并沒(méi)有發(fā)生變化

if(mshape==ViewShape.CIRCLE){//改變枚舉狀態(tài)

mshape=ViewShape.TRIANGLE;

}else if(mshape==ViewShape.TRIANGLE){

mshape=ViewShape.RANGE;

}else{

mshape=ViewShape.CIRCLE;

}

}

}

mcount++;//累加用于判斷

invalidate();

}

});

}

這里我調(diào)用控件時(shí)設(shè)置的控件寬高均為matchParent,效果如下



The end

ok,今天實(shí)現(xiàn)的效果就到這里,關(guān)于陰影部分:可以看到它在view下落的時(shí)候縮小,彈回去變大,實(shí)現(xiàn)思路基本差不多。這樣寫(xiě)完還不能用,需要進(jìn)一步的去做屏幕適配和性能優(yōu)化,但是大概的流程我們已經(jīng)清楚了。

關(guān)于一些思考問(wèn)題:1.onDraw()的canvas在調(diào)用invalidate()之后和原來(lái)相比并不是同一個(gè),原因在哪?

2.view的繪制流程,onSizeChanged()

哈哈,發(fā)現(xiàn)問(wèn)題請(qǐng)多反饋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容