效果如下:

Camera3DView.gif
思路是利用camera對兩張圖片分別做旋轉(zhuǎn)處理,代碼如下
/**
* 使用camera實現(xiàn)3d效果的自定義控件
*/
public class Camera3DView extends View {
//存放bitmap資源文件id的集合
private List<Integer> bitmapResourceIds;
//用于3d變換
private Camera camera;
//用于變換的矩陣
private Matrix matrix;
//view的寬高
private float viewWidth, viewHeight;
//是否繪制完畢
private boolean isDrawFinished = false;
public static final int VERTICAL = LinearLayout.VERTICAL;
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
//旋轉(zhuǎn)方向
private int orientation = VERTICAL;
//旋轉(zhuǎn)軸,所有旋轉(zhuǎn)操作無外乎兩個玩意,一個是旋轉(zhuǎn)軸,一個是旋轉(zhuǎn)角度
//VERTICAL時使用rotateY,HORIZONTAL時使用rotateX
private float rotatePivotX, rotatePivotY;
//旋轉(zhuǎn)角度
private float degress;
//最大旋轉(zhuǎn)角度
private float maxDegress = 90;
//當前圖片索引
private int currentIndex;
//下個顯示圖片索引
private int nextIndex;
//上個顯示圖片索引
private int preIndex;
private Paint mPaint;
//是否前進
private boolean isForward = true;
private ValueAnimator valueAnimator;
private boolean isAnimatiorRunning;
public Camera3DView(Context context) {
this(context, null);
}
public Camera3DView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public Camera3DView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
bitmapResourceIds = new ArrayList<>();
camera = new Camera();
matrix = new Matrix();
mPaint = new Paint();
isDrawFinished = false;
}
/**
* 添加圖片
*
* @param resId
*/
public void addResId(int resId) {
bitmapResourceIds.add(resId);
if (isDrawFinished) {
invalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewWidth = w;
viewHeight = h;
isDrawFinished = true;
initBitmaps();
resetIndex();
}
private void initBitmaps() {
for (int i = 0; i < bitmapResourceIds.size(); i++) {
int resID = bitmapResourceIds.get(i);
getBitmapScale(resID, viewWidth, viewHeight);
}
}
/**
* 旋轉(zhuǎn)一面后,調(diào)用重置索引
*/
public void resetIndex() {
int listSize = bitmapResourceIds.size();
if (isForward) {//前進或者向下
currentIndex++;
if (currentIndex > listSize - 1) {
currentIndex = 0;
}
} else {
currentIndex--;
if (currentIndex < 0)
currentIndex = listSize - 1;
}
nextIndex = currentIndex + 1;
preIndex = currentIndex - 1;
if (nextIndex > listSize - 1)
nextIndex = 0;//循環(huán)切換
if (preIndex < 0)
preIndex = listSize - 1;
this.degress = 0;
rotatePivotX = 0;
rotatePivotY = 0;
isForward = true;
invalidate();
}
public void setDegress(int degress) {
this.degress = degress;
//VERTICAL時使用rotateY,HORIZONTAL時使用rotateX
if (orientation == HORIZONTAL) {
//x方向旋轉(zhuǎn)軸隨degress的變大不斷下移
rotatePivotX = degress / maxDegress * viewWidth;
} else {
//y方向旋轉(zhuǎn)軸隨degress的變大不斷右移
rotatePivotY = degress / maxDegress * viewHeight;
}
//刷新
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
//VERTICAL時使用rotateY,HORIZONTAL時使用rotateX
if (orientation == VERTICAL) {
//如果是前進,則畫當前圖,后退則畫上一張圖,注釋用的是前進情況
matrix.reset();
camera.save();
//旋轉(zhuǎn)角度 0 - -maxDegress
camera.rotateX(-degress);
camera.getMatrix(matrix);
camera.restore();
//繞著圖片top旋轉(zhuǎn)
matrix.preTranslate(-viewWidth / 2f, 0);
//旋轉(zhuǎn)軸向下平移,則圖片也向下平移
matrix.postTranslate(viewWidth / 2f, rotatePivotY);
//如果是前進,則畫當前圖,后退則畫上一張圖,因為后退時,這里畫的是動畫下方出來的圖片,而下方的圖片是前一張圖
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
matrix, mPaint);
//在處理下一張圖片
matrix.reset();
camera.save();
//旋轉(zhuǎn)角度 maxDegress - 0
camera.rotateX(maxDegress - degress);
camera.getMatrix(matrix);
camera.restore();
//繞著圖片bottom旋轉(zhuǎn)
matrix.preTranslate(-viewWidth / 2f, -viewHeight);
//旋轉(zhuǎn)軸向下平移,則圖片也向下平移
matrix.postTranslate(viewWidth / 2f, rotatePivotY);
//如果是前進,則畫下一張圖,后退則畫當前圖,后退時,這邊代碼畫的是動畫上方的圖片,上方的圖片是當前圖片
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
matrix, mPaint);
} else {
//如果是前進,則畫當前圖,后退則畫上一張圖,注釋用的是前進情況
matrix.reset();
camera.save();
//旋轉(zhuǎn)角度 0 - maxDegress
camera.rotateY(degress);
camera.getMatrix(matrix);
camera.restore();
//繞著圖片left旋轉(zhuǎn)
matrix.preTranslate(0, -viewHeight / 2);
//旋轉(zhuǎn)軸向右平移,則圖片也向右平移
matrix.postTranslate(rotatePivotX, viewHeight / 2);
//如果是前進,則畫當前圖,后退則畫上一張圖,因為后退時,這里畫的是動畫右方出來的圖片,而右方的圖片是前一張圖
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
matrix, mPaint);
//在處理下一張圖片
matrix.reset();
camera.save();
//旋轉(zhuǎn)角度 -maxDegress - 0
camera.rotateY(-maxDegress + degress);
camera.getMatrix(matrix);
camera.restore();
//繞著圖片right旋轉(zhuǎn)
matrix.preTranslate(-viewWidth, -viewHeight / 2f);
//旋轉(zhuǎn)軸向右平移,則圖片也向右平移
matrix.postTranslate(rotatePivotX, viewHeight / 2f);
//如果是前進,則畫下一張圖,后退則畫當前圖,后退時,這邊代碼畫的是動畫左方的圖片,左方的圖片是當前圖片
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
matrix, mPaint);
}
}
/**
* 獲取縮放圖片
*
* @param resId
* @param width
* @param height
* @return
*/
private Bitmap getBitmapScale(int resId, float width, float height) {
if (ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId)) != null) {
return ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId));
}
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
Bitmap bitmapDst = Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, false);
bitmap.recycle();
ImageCache.getInstance().addBitmapToMemoryCache(String.valueOf(resId)
, bitmapDst);
return bitmapDst;
}
public void next() {
createAnimator();
if (!isAnimatiorRunning) {
isForward = true;
isAnimatiorRunning = true;
valueAnimator.start();
}
}
public void pre() {
createAnimator();
if (!isAnimatiorRunning) {
isForward = false;
isAnimatiorRunning = true;
valueAnimator.reverse();
}
}
private void createAnimator() {
if (valueAnimator == null) {
valueAnimator = ValueAnimator.ofFloat(0, 1f);
valueAnimator.setDuration(500);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degress = maxDegress * animation.getAnimatedFraction();
setDegress((int) degress);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
resetIndex();
isAnimatiorRunning = false;
}
});
}
}
public void setOrientation(int orientation) {
this.orientation = orientation;
}
}