最近看到幾篇自定義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)多反饋。