效果如下:
Camera3DView.gif
思路是利用camera对两张图片分别做旋转处理,代码如下
代码语言:javascript复制/**
* 使用camera实现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;
//旋转方向
private int orientation = VERTICAL;
//旋转轴,所有旋转操作无外乎两个玩意,一个是旋转轴,一个是旋转角度
//VERTICAL时使用rotateY,HORIZONTAL时使用rotateX
private float rotatePivotX, rotatePivotY;
//旋转角度
private float degress;
//最大旋转角度
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);
}
}
/**
* 旋转一面后,调用重置索引
*/
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;//循环切换
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方向旋转轴随degress的变大不断下移
rotatePivotX = degress / maxDegress * viewWidth;
} else {
//y方向旋转轴随degress的变大不断右移
rotatePivotY = degress / maxDegress * viewHeight;
}
//刷新
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
//VERTICAL时使用rotateY,HORIZONTAL时使用rotateX
if (orientation == VERTICAL) {
//如果是前进,则画当前图,后退则画上一张图,注释用的是前进情况
matrix.reset();
camera.save();
//旋转角度 0 - -maxDegress
camera.rotateX(-degress);
camera.getMatrix(matrix);
camera.restore();
//绕着图片top旋转
matrix.preTranslate(-viewWidth / 2f, 0);
//旋转轴向下平移,则图片也向下平移
matrix.postTranslate(viewWidth / 2f, rotatePivotY);
//如果是前进,则画当前图,后退则画上一张图,因为后退时,这里画的是动画下方出来的图片,而下方的图片是前一张图
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
matrix, mPaint);
//在处理下一张图片
matrix.reset();
camera.save();
//旋转角度 maxDegress - 0
camera.rotateX(maxDegress - degress);
camera.getMatrix(matrix);
camera.restore();
//绕着图片bottom旋转
matrix.preTranslate(-viewWidth / 2f, -viewHeight);
//旋转轴向下平移,则图片也向下平移
matrix.postTranslate(viewWidth / 2f, rotatePivotY);
//如果是前进,则画下一张图,后退则画当前图,后退时,这边代码画的是动画上方的图片,上方的图片是当前图片
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
matrix, mPaint);
} else {
//如果是前进,则画当前图,后退则画上一张图,注释用的是前进情况
matrix.reset();
camera.save();
//旋转角度 0 - maxDegress
camera.rotateY(degress);
camera.getMatrix(matrix);
camera.restore();
//绕着图片left旋转
matrix.preTranslate(0, -viewHeight / 2);
//旋转轴向右平移,则图片也向右平移
matrix.postTranslate(rotatePivotX, viewHeight / 2);
//如果是前进,则画当前图,后退则画上一张图,因为后退时,这里画的是动画右方出来的图片,而右方的图片是前一张图
canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
matrix, mPaint);
//在处理下一张图片
matrix.reset();
camera.save();
//旋转角度 -maxDegress - 0
camera.rotateY(-maxDegress degress);
camera.getMatrix(matrix);
camera.restore();
//绕着图片right旋转
matrix.preTranslate(-viewWidth, -viewHeight / 2f);
//旋转轴向右平移,则图片也向右平移
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;
}
}