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 499216359@qq.com, 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(); } } }