android.graphics.Camera:3D开发
官方介绍
A camera instance can be used to compute 3D transformations and generate a matrix that can be applied, for instance, on aCanvas。
一个照相机实例可以被用于计算3D变换,生成一个可以被使用的Matrix矩阵,一个实例,用在画布上。
Camera内部机制实际上还是opengl,不过大大简化了使用。
纵向 3D 整体滚动效果
核心代码
private void drawWhole3D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){//纵向
camera.save();
camera.rotateX(-rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewHeight/2,0);
//下面的view绕着自己的top旋转,转完之后显示要往下平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(currWholeBitmap,matrix,paint);
camera.save();
//第二张图片旋转
camera.rotateX(90-rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2,-viewHeight);
//下面的view绕着自己的Bottom旋转,转完之后显示要往上平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(nextWholeBitmap,matrix,paint);
}else {//横向
}
canvas.restore();
}
分析动画
横向3D整体翻滚
核心代码
private void drawWhole3D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){//纵向
camera.save();
camera.rotateX(-rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2,0);
//下面的view绕着自己的top旋转,转完之后显示要往下平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(currWholeBitmap,matrix,paint);
camera.save();
//第二张图片旋转
camera.rotateX(90-rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2,-viewHeight);
//下面的view绕着自己的Bottom旋转,转完之后显示要往上平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(nextWholeBitmap,matrix,paint);
}else {//横向
camera.save();
camera.rotateY(rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0,-viewHeight/2);
matrix.postTranslate(axisX,viewHeight/2);
canvas.drawBitmap(currWholeBitmap,matrix,paint);
camera.save();
//第二张图片旋转
camera.rotateY(-(90-rotateDegree));
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth,-viewHeight/2);
matrix.postTranslate(axisX,viewHeight/2);
canvas.drawBitmap(nextWholeBitmap,matrix,paint);
}
canvas.restore();
}
动画分析 和纵向一样 只不过横向 是绕Y轴旋转
各模块依次滚动
核心原理和上面一样 这里对图片进行了分割处理 后面附上完整的代码
百叶窗效果
尾部逐渐分离再合并效果
整体翻滚2D效果
下面附上源码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".MainActivity"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shap_bg"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp">
<com.twy.sqlitehelper.view.Camera3DView
android:id="@+id/cv_1"
android:layout_width="200dp"
android:layout_height="200dp"/>
</FrameLayout>
<SeekBar
android:id="@+id/atdv_seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Bitmap bitmap1;
private Bitmap bitmap2;
private Camera3DView camera3DView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
camera3DView = findViewById(R.id.cv_1);
SeekBar seekBar = findViewById(R.id.atdv_seek_bar);
seekBar.setMax(100);//0 - 100
seekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);
bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.aa);
bitmap2 = BitmapFactory.decodeResource(getResources(), R.mipmap.bb);
List<Bitmap> list = new ArrayList<>();
list.add(bitmap1);
list.add(bitmap2);
//整体翻滚2D
/*camera3DView.setRollDirection(Camera3DView.Orientation.Horizontal);
camera3DView.setRollMode(Camera3DView.RollMode.Roll2D);*/
//整体翻滚3D
/*camera3DView.setRollDirection(2);
camera3DView.setRollMode(Camera3DView.RollMode.Whole3D);*/
//各模块依次滚动
/*camera3DView.setRollDirection(Camera3DView.Orientation.Horizontal);
camera3DView.setRollMode(Camera3DView.RollMode.RollInTurn);
camera3DView.setPartNumber(10);//设置分割数量*/
//百叶窗
/*camera3DView.setRollDirection(Camera3DView.Orientation.Horizontal);
camera3DView.setRollMode(Camera3DView.RollMode.Jalousie);
camera3DView.setPartNumber(10);*/
//尾部逐渐分离再合并效果
camera3DView.setRollDirection(Camera3DView.Orientation.Horizontal);
camera3DView.setRollMode(Camera3DView.RollMode.SepartConbine);
camera3DView.setPartNumber(10);
camera3DView.addAllBitmap(list);
}
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
camera3DView.setRotateDegree(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
}
Camera3DView.java
package com.twy.sqlitehelper.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Author by twy, Email [email protected], Date on 2019/5/23.
* PS: Not easy to write code, please indicate.
*/
public class Camera3DView extends View {
private Context context;
private Camera camera;
private Matrix matrix;
private List<Bitmap> bitmapList;
private Bitmap[][] bitmaps;
private int partNumber = 5;
private Orientation direction = Orientation.Horizontal;
private float averageWidth,averageHeight;
private float viewWidth,viewHeight;
private int preIndex = 0, currIndex = 0, nextIndex = 0;
private float rotateDegree = 0;
//X方向旋转轴 Y方向旋转轴
private float axisX = 0, axisY = 0;
//滚动模式
private RollMode rollMode = RollMode.Whole3D;
private Paint paint;
//滚动模式
public enum RollMode {
//3D整体滚动 尾部逐渐分离再合并 各模块依次滚动 百叶窗
Roll2D,Whole3D, SepartConbine, RollInTurn, Jalousie
}
public enum Orientation{
//水平方向 垂直方向
Horizontal,Vertical
}
public Camera3DView(Context context) {
super(context);
init(context);
}
public Camera3DView(Context context,AttributeSet attrs) {
super(context, attrs);
init(context);
}
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
this.context = context;
camera = new Camera();
matrix = new Matrix();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapList = new ArrayList<>();
}
public void addAllBitmap(List<Bitmap> bitmaps){
bitmapList.addAll(bitmaps);
invalidate();
}
public void setPartNumber(int partNumber){
this.partNumber = partNumber;
}
/**
*
* @param rotateDegree 0至100
*/
public void setRotateDegree(float rotateDegree){
switch (rollMode){
case Whole3D:
case Jalousie:
case SepartConbine:
case Roll2D:
this.rotateDegree = rotateDegree/100*(rollMode==RollMode.Jalousie?180:90);
if(Orientation.Vertical==direction){
//Y方向旋转轴
//百叶窗旋转180度,其他旋转90度
axisY = this.rotateDegree/(float)(rollMode==RollMode.Jalousie?180:90)*viewHeight;
}else {
//X方向旋转轴
axisX = this.rotateDegree/(float)(rollMode==RollMode.Jalousie?180:90)*viewWidth;
}
break;
case RollInTurn:
this.rotateDegree = rotateDegree/100*(30*(partNumber-1)+(rollMode==RollMode.Jalousie?180:90));
break;
}
invalidate();
}
public void setRollDirection(Orientation direction) {
this.direction = direction;
}
public void setRollMode(RollMode rollMode){
this.rollMode = rollMode;
}
//对图片进行切割
private void initBitmap() {
initIndex();
switch (rollMode){
case RollInTurn:
case Jalousie:
case SepartConbine:
bitmaps = new Bitmap[bitmapList.size()][partNumber];
averageWidth = viewWidth/partNumber;
averageHeight = viewHeight/partNumber;
Rect rect;
Bitmap partBitmap;
for(int i = 0;i<bitmapList.size();i++){
for(int j = 0;j<partNumber;j++){
if(Orientation.Vertical==direction){//纵向切割
rect = new Rect((int)(j*averageWidth),0,(int)((j+1)*averageWidth),(int) viewHeight);
partBitmap = getPartBitmap(bitmapList.get(i),rect.left,0,rect.width(),rect.height());
}else {//横向切割
rect = new Rect(0,(int)(j*averageHeight),(int) viewWidth,(int)((j+1)*averageHeight));
partBitmap = getPartBitmap(bitmapList.get(i),0,rect.top,rect.width(),rect.height());
}
bitmaps[i][j] = partBitmap;
}
}
break;
}
}
private void initIndex() {
int listSize = bitmapList.size();
nextIndex = currIndex +1;
preIndex = currIndex -1;
if(nextIndex > listSize -1){
nextIndex = 0;//当到了边界,再循环变换
}
if(preIndex < 0){
preIndex = listSize -1;
}
}
private Bitmap getPartBitmap(Bitmap bitmap,int left,int top,int width,int height){
return Bitmap.createBitmap(bitmap,left,top,width,height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = getMeasuredWidth();
viewHeight = getMeasuredHeight();
if (viewWidth != 0 && viewHeight != 0) {
//缩放处理bitmap
for (int i = 0; i < bitmapList.size(); i++) {
bitmapList.set(i, scaleBitmap(bitmapList.get(i)));
}
initBitmap();
}
}
//根据给定的宽和高进行拉伸
private Bitmap scaleBitmap(Bitmap origin) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = viewWidth / width;
float scaleHeight = viewHeight / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
return newBM;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (rollMode){
case Roll2D:
drawWhole3D(canvas,true);
break;
case Whole3D:
drawWhole3D(canvas,false);
break;
case RollInTurn:
drawRollInTurn(canvas);
break;
case Jalousie:
drawJalousie(canvas);
break;
case SepartConbine:
drawSepartConbine(canvas);
break;
}
}
/**
* 整体翻滚
* @param canvas
* @param draw2D true 2D效果 false 3D效果
*/
private void drawWhole3D(Canvas canvas,boolean draw2D) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(Orientation.Vertical==direction){//纵向
camera.save();
if(draw2D){
camera.rotateX(0);
}else {
camera.rotateX(-rotateDegree);
}
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2,0);
//下面的view绕着自己的top旋转,转完之后显示要往下平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(currWholeBitmap,matrix,paint);
camera.save();
//第二张图片旋转
if(draw2D){
camera.rotateX(0);
}else {
camera.rotateX(90-rotateDegree);
}
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2,-viewHeight);
//下面的view绕着自己的Bottom旋转,转完之后显示要往上平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(viewWidth/2,axisY);
canvas.drawBitmap(nextWholeBitmap,matrix,paint);
}else {//横向
camera.save();
if(draw2D){
camera.rotateX(0);
}else {
camera.rotateY(rotateDegree);
}
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0,-viewHeight/2);
//下面的view绕着自己的top旋转,转完之后显示要往下平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(axisX,viewHeight/2);
canvas.drawBitmap(currWholeBitmap,matrix,paint);
camera.save();
//第二张图片旋转
if(draw2D){
camera.rotateX(0);
}else {
camera.rotateY(-(90-rotateDegree));
}
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth,-viewHeight/2);
//下面的view绕着自己的Bottom旋转,转完之后显示要往上平移(rotateDegree/90)*viewWidth;
matrix.postTranslate(axisX,viewHeight/2);
canvas.drawBitmap(nextWholeBitmap,matrix,paint);
}
canvas.restore();
}
/**
* 依次翻滚
*
* @param canvas
*/
private void drawRollInTurn(Canvas canvas) {
for (int i = 0; i < partNumber; i++) {
Bitmap currBitmap = bitmaps[currIndex][i];
Bitmap nextBitmap = bitmaps[nextIndex][i];
float tDegree = rotateDegree - i * 30;
if (tDegree < 0)
tDegree = 0;
if (tDegree > 90)
tDegree = 90;
canvas.save();
if (Orientation.Vertical==direction) {
float tAxisY = tDegree / 90f * viewHeight;
if (tAxisY > viewHeight)
tAxisY = viewHeight;
if (tAxisY < 0)
tAxisY = 0;
camera.save();
camera.rotateX(-tDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-currBitmap.getWidth(), 0);
matrix.postTranslate(currBitmap.getWidth() + i * averageWidth, tAxisY);
canvas.drawBitmap(currBitmap, matrix, paint);
camera.save();
camera.rotateX((90 - tDegree));
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-nextBitmap.getWidth(), -nextBitmap.getHeight());
matrix.postTranslate(nextBitmap.getWidth() + i * averageWidth, tAxisY);
canvas.drawBitmap(nextBitmap, matrix, paint);
} else {
float tAxisX = tDegree / 90f * viewWidth;
if (tAxisX > viewWidth)
tAxisX = viewWidth;
if (tAxisX < 0)
tAxisX = 0;
camera.save();
camera.rotateY(tDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -currBitmap.getHeight() / 2);
matrix.postTranslate(tAxisX, currBitmap.getHeight() / 2 + i * averageHeight);
canvas.drawBitmap(currBitmap, matrix, paint);
//
camera.save();
camera.rotateY(tDegree - 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-nextBitmap.getWidth(), -nextBitmap.getHeight() / 2);
matrix.postTranslate(tAxisX, nextBitmap.getHeight() / 2 + i * averageHeight);
canvas.drawBitmap(nextBitmap, matrix, paint);
}
canvas.restore();
}
}
/**
* 百叶窗翻页
*
* @param canvas
*/
private void drawJalousie(Canvas canvas) {
for (int i = 0; i < partNumber; i++) {
Bitmap currBitmap = bitmaps[currIndex][i];
Bitmap nextBitmap = bitmaps[nextIndex][i];
canvas.save();
//注意 百叶窗的翻转方向和其他模式是相反的 横向的时候纵翻 纵向的时候横翻
if (Orientation.Vertical==direction) {
if (rotateDegree < 90) {
camera.save();
camera.rotateY(rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-currBitmap.getWidth() / 2, -currBitmap.getHeight() / 2);
matrix.postTranslate(currBitmap.getWidth() / 2+ i * averageWidth, currBitmap.getHeight() / 2 );
canvas.drawBitmap(currBitmap, matrix, paint);
} else {
camera.save();
camera.rotateY(180 - rotateDegree);
camera.getMatrix(matrix);
camera.restore();
//前乘矩阵---先平移矩阵,再进行变换
matrix.preTranslate(-nextBitmap.getWidth() / 2, -nextBitmap.getHeight() / 2);
//变换完之后,再平移
matrix.postTranslate(nextBitmap.getWidth() / 2+ i * averageWidth, nextBitmap.getHeight() / 2);
canvas.drawBitmap(nextBitmap, matrix, paint);
}
} else {
if (rotateDegree < 90) {
camera.save();
camera.rotateX(rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-currBitmap.getWidth() / 2, -currBitmap.getHeight() / 2);
matrix.postTranslate(currBitmap.getWidth() / 2, currBitmap.getHeight() / 2+i * averageHeight);
canvas.drawBitmap(currBitmap, matrix, paint);
} else {
camera.save();
camera.rotateX(180 - rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-nextBitmap.getWidth() / 2, -nextBitmap.getHeight() / 2);
matrix.postTranslate(nextBitmap.getWidth() / 2 , nextBitmap.getHeight() / 2+ i * averageHeight);
canvas.drawBitmap(nextBitmap, matrix, paint);
}
}
canvas.restore();
}
}
/**
* 纵向 头部接合 尾部分离效果
* degree 0->90 往下翻滚 或者 往右翻滚 90->0往上翻滚 或者往翻滚
*
* @param canvas
*/
private void drawSepartConbine(Canvas canvas) {
for (int i = 0; i < partNumber; i++) {
Bitmap currBitmap = bitmaps[currIndex][i];
Bitmap nextBitmap = bitmaps[nextIndex][i];
canvas.save();
if (Orientation.Vertical==direction) {
camera.save();
camera.rotateX(-rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-currBitmap.getWidth() / 2, 0);
matrix.postTranslate(currBitmap.getWidth() / 2 + i * averageWidth, axisY);
canvas.drawBitmap(currBitmap, matrix, paint);
camera.save();
camera.rotateX((90 - rotateDegree));
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-nextBitmap.getWidth() / 2, -nextBitmap.getHeight());
matrix.postTranslate(nextBitmap.getWidth() / 2 + i * averageWidth, axisY);
canvas.drawBitmap(nextBitmap, matrix, paint);
} else {
camera.save();
camera.rotateY(rotateDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -currBitmap.getHeight() / 2);
matrix.postTranslate(axisX, currBitmap.getHeight() / 2 + i * averageHeight);
canvas.drawBitmap(currBitmap, matrix, paint);
camera.save();
camera.rotateY(rotateDegree - 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-nextBitmap.getWidth(), -nextBitmap.getHeight() / 2);
matrix.postTranslate(axisX, nextBitmap.getHeight() / 2 + i * averageHeight);
canvas.drawBitmap(nextBitmap, matrix, paint);
}
canvas.restore();
}
}
}