天天看點

Android仿Flipboard動畫效果的實作代碼

1.上原圖

前幾天在

Hencoder

征稿看到的Filpboard 裡的的動畫效果:

Android仿Flipboard動畫效果的實作代碼

Filipboard.gif

先bb一句:在看本文的同時,如果覺得我寫的模糊看不太懂的可以直接拉到文末,配合完整代碼再一步一步看。

2.實作 整體思路:

用手機拍下來,逐幀觀看了許久,恍然大悟,就是一張紙,折起一邊之後,讓其對折線繞中心點旋轉。

關聯自定義

View

Camera

來控制對折幅度,

canves

控制旋轉。

具體:

每當對折線旋轉的時候,圖示總是一邊是折起來的,一邊是平鋪的,且中心對稱,是以将它整體分為兩部分來繪制。

使用

canves

ClipRect()

方法可以輕松實作切割畫布。

應該也能順着想到給

canves

做動畫讓折疊線(切割線)動起來,這裡會有一個問題,即:對折線是動起來了,可繪制内容也會跟着動起來,那我們要的是隻有分割線在中間旋轉實時分割圖示,是以有個小技巧,

先這樣:

canvas.rotate(-degreeZ);
 canvas.clipRect(0, -centerY, centerX, centerY);           

複制

再這樣:

canvas.rotate(degreeZ);
 canvas.drawBitmap(bitmap, x, y, paint);           

複制

嗯對,在旋轉畫闆的代碼之後做切割,切割之後呢又讓他以完全相同的角度反向旋轉,再繪制圖示,這樣就可以實作之前讓切割線旋轉,而圖示不動的目的了。

先貼這部分代碼,明天寫剩餘部分:

protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 int bitmapWidth = bitmap.getWidth();
 int bitmapHeight = bitmap.getHeight();
 int centerX = getWidth() / 2;
 int centerY = getHeight() / 2;
 int x = centerX - bitmapWidth / 2;
 int y = centerY - bitmapHeight / 2;
 // 不動的另一部分
  canvas.save();
 canvas.translate(centerX, centerY);
 canvas.rotate(-degreeZ);
 canvas.clipRect(-centerX, -centerY, 0, centerY);
 canvas.rotate(degreeZ);
 canvas.translate(-centerX, -centerY);
 canvas.drawBitmap(bitmap, x, y, paint);
  canvas.restore();
}           

複制

這時候看到的效果應該是這樣的:

Android仿Flipboard動畫效果的實作代碼

切割線旋轉

接下來寫和它中心對稱的另一半:

因為是中心對稱,旋轉速度一緻,隻需要變更切割的部分為對稱部分,是以這部分代碼隻需要将前一部分代碼複制然後修改這一行 :

canvas.clipRect(-centerX, -centerY, 0, centerY);           

複制

為:

canvas.clipRect(0,-centerY,centerX,centerY);           

複制

就可以實作目的。

完整代碼:

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int x = centerX - bitmapWidth / 2;
int y = centerY - bitmapHeight / 2;
 // 不動的另一部分
 canvas.save();
 canvas.translate(centerX, centerY);
 canvas.rotate(-degreeZ);
 canvas.clipRect(-centerX, -centerY, 0, centerY);
 canvas.rotate(degreeZ);
 canvas.translate(-centerX, -centerY);
 canvas.drawBitmap(bitmap, x, y, paint);
 canvas.restore();
 // 被折起來的另一部分
 canvas.save();
 canvas.translate(centerX,centerY);
 canvas.rotate(-degreeZ);
 canvas.clipRect(0,-centerY,centerX,centerY);
 canvas.rotate(degreeZ);
 canvas.translate(-centerX,-centerY);
 canvas.drawBitmap(bitmap,x,y,paint);
 canvas.restore();
}           

複制

這時候運作代碼的效果就是 沒有效果。因為我們繪制的兩部分一直是中心對稱,雖然我們知道它在動,但是看起來仿佛隻是繪制了一張靜态的圖示。

不着急,這時候就需要給它對折起來的這一半加上

Camera

,讓它 “折起來”。

在代碼裡添加

Camera

經典四步:

camera.save();
 camera.rotateY(degreeY);
 camera.applyToCanvas(canvas);
 camera.restore();           

複制

當然不考慮直接加上這些代碼是不行的,因為我們是”對折”,意味着 折起來的部分也要和 切割線 同步旋轉,

是以這些代碼應該這樣插入進去:

// 被折起來的另一部分
 canvas.save();
 camera.save();
 canvas.translate(centerX, centerY);
 canvas.rotate(-degreeZ);
 camera.rotateY(degreeY);
 camera.applyToCanvas(canvas);
 canvas.clipRect(0, -centerY, centerX, centerY);
 canvas.rotate(degreeZ);
 canvas.translate(-centerX, -centerY); 
 canvas.drawBitmap(bitmap, x, y, paint);
 camera.restore(); 
 canvas.restore();           

複制

這時候再運作代碼:

Android仿Flipboard動畫效果的實作代碼

已然完成百分之80

嘿,原動畫的要求已經完成80%了。現在去加上最後的動畫: 之前不動的一半也被折起來了,這個很簡單修改不動的那一部分的代碼:

// 不動的另一部分
 canvas.save();
 camera.save();
 canvas.translate(centerX, centerY);
 canvas.rotate(-degreeZ);
 canvas.clipRect(-centerX, -centerY, 0, centerY);
 canvas.rotate(degreeZ);
 camera.rotateX(degreeY2);
 camera.applyToCanvas(canvas);
 canvas.translate(-centerX, -centerY);
 canvas.drawBitmap(bitmap, x, y, paint);
 camera.restore();
 canvas.restore();           

複制

運作代碼,ok,完成~

對了,還有個細節,仔細觀察發現

中間旋轉動畫

旋轉速度

越來越

的,是以使用

LinearOutSlowInInterpolator

這個插補器。

關于屬性動畫,還有後面的更多的定制性以及細節優化,比如重寫

onMeasure()

,動畫屬性抽離就不說了,整體思路以及一些細節思考我感覺都講出來了,不知道是不是隻有我一個人懂的那種。好懶啊,想睡覺。想下班,想吃雞。

Android仿Flipboard動畫效果的實作代碼

完成版

最後推薦一下大佬

扔物線

的學習網站

hencoder

:http://hencoder.com/ 不僅免費,而且賊6~

雖然我猜也沒多少人能堅持看到這裡,好慘,捂臉。

項目碼雲位址: https://gitee.com/alucode/AsFilpBoard

完整代碼:

public class FlipBoardView extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap bitmap;
private Camera camera = new Camera();
int degreeZ;
int degreeY;
int degreeY2;
//中間旋轉動畫
private ObjectAnimator animator = ObjectAnimator.ofInt(this, "degreeZ", 0, 270);
//第一段折起動畫
private ObjectAnimator animator1 = ObjectAnimator.ofInt(this, "degreeY", 0, -45);
//最後一段折起動畫
private ObjectAnimator animator2 = ObjectAnimator.ofInt(this, "degreeY2", 0, -45);
AnimatorSet animatorSet;
public FlipBoardView(Context context) {
super(context);
}
public FlipBoardView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FlipBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
//糊臉修正,給camera 做z軸距離适配,避免繪制糊臉
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float newZ = -displayMetrics.density * 6;
camera.setLocation(0, 0, newZ);
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.maps);
animator.setDuration(1000);
animator.setStartDelay(500);
animator.setInterpolator(new LinearOutSlowInInterpolator());
animator1.setDuration(800);
animator1.setStartDelay(500);
animator1.setInterpolator(new LinearInterpolator());
animator2.setDuration(500);
animator2.setStartDelay(500);
animator2.setInterpolator(new LinearInterpolator());
animatorSet = new AnimatorSet();
animatorSet.playSequentially(animator1, animator, animator2);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
degreeZ = 0;
degreeY = 0;
degreeY2 = 0;
animatorSet.start();
}
});
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
animatorSet.start();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
animatorSet.end();
}
@SuppressWarnings("unused")
public void setDegreeZ(int degreeZ) {
this.degreeZ = degreeZ;
invalidate();
}
@SuppressWarnings("unused")
public void setDegreeY(int degreeY) {
this.degreeY = degreeY;
invalidate();
}
public void setDegreeY2(int degreeY2) {
this.degreeY2 = degreeY2;
invalidate();
}
/**
* 原理:從折 線分為兩部分繪制,其實是繪制了兩個bitmap 一個動一個不動 然後截取拼湊
*
* @param canvas ca
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int x = centerX - bitmapWidth / 2;
int y = centerY - bitmapHeight / 2;
canvas.save();
camera.save();
canvas.translate(centerX, centerY);
canvas.rotate(-degreeZ);
camera.rotateY(degreeY);
camera.applyToCanvas(canvas);
canvas.clipRect(0, -centerY, centerX, centerY);
canvas.rotate(degreeZ);
canvas.translate(-centerX, -centerY);
canvas.drawBitmap(bitmap, x, y, paint);
camera.restore();
canvas.restore();
// 不動的另一部分
canvas.save();
camera.save();
canvas.translate(centerX, centerY);
canvas.rotate(-degreeZ);
canvas.clipRect(-centerX, -centerY, 0, centerY);
canvas.rotate(degreeZ);
camera.rotateX(degreeY2);
camera.applyToCanvas(canvas);
canvas.translate(-centerX, -centerY);
canvas.drawBitmap(bitmap, x, y, paint);
camera.restore();
canvas.restore();
}
/**
* onMeasure() 日常重寫
* @param widthMeasureSpec w
* @param heightMeasureSpec h
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = getPaddingLeft() + bitmap.getWidth() + getPaddingRight();
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = getPaddingTop() + bitmap.getHeight() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
}           

複制

總結

以上所述是小編給大家介紹的Android仿Flipboard動畫效果的實作代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回複大家的。在此也非常感謝大家對ZaLou.Cn網站的支援!