前言
android中3D效果大部分是用
android.graphics.Camera
来实现的,本篇讲述Camera
的一个实例来简单了解其使用
效果展示
实现
布局文件activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.siti.test_lsn23_3dview.MainActivity">
<com.siti.test_lsn23_3dview.My3DView
android:id="@+id/my"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="30dp"
/>
<SeekBar
android:id="@+id/seek"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
/>
</LinearLayout>
- 布局文件很简单,上面是实现3D翻转的自定义
view
,下面是一个SeekBar
MainActivity
主要代码:
private My3DView myView;
private SeekBar seekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView = (My3DView) findViewById(R.id.my);
seekBar = (SeekBar) findViewById(R.id.seek);
seekBar.setMax(90);
seekBar.setKeyProgressIncrement(1);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
myView.setRotateDegree(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
-
Activity
代码也很简单,通过监听SeekBar
的值改变,传0~90
给自定义myView
,调用其方法setRotateDegree()
来改变翻转状态
自定义My3DView
代码:
public class My3DView extends View {
Paint mPaint;//画笔
Bitmap b1;//图1
Bitmap b2;//图2
Camera camera;//实现翻转的核心类
Matrix matrix;//存储变化后的矩阵
int currentDegree;//当前翻转角度
float axisY;//旋转轴的Y坐标
int viewWidth;//控件宽
int viewHeight;//控件高
public My3DView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* 初始化
*/
private void init() {
camera = new Camera();
matrix = new Matrix();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
b1 = BitmapFactory.decodeResource(getResources(), R.drawable.img1);
b2 = BitmapFactory.decodeResource(getResources(), R.drawable.img2);
axisY = 0;
currentDegree = 0;
}
/**
* 测量大小,根据控件大小按比例缩放图片,使得图片填充整个控件
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = getMeasuredWidth();
viewHeight = getMeasuredHeight();
if(viewWidth != 0 && viewHeight != 0) {
b1 = scaleBitmap(b1);
b2 = scaleBitmap(b2);
}
}
/**
* 按比例缩放图片
* @param origin
* @return
*/
private Bitmap scaleBitmap(Bitmap origin) {
if(origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) viewWidth) / width;
float scaleHeight = ((float) viewHeight) / height;
Matrix mt = new Matrix();
mt.postScale(scaleWidth, scaleHeight);
Bitmap newBitmap = Bitmap.createBitmap(origin, 0, 0,
width, height, mt, false);
return newBitmap;
}
/**
* 绘制,实现翻转效果
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
//绘制下面那张图片
camera.save();
camera.rotateX(-currentDegree);//设置旋转角度
camera.getMatrix(matrix);//获取到旋转后的矩阵
camera.restore();
matrix.preTranslate(-viewWidth / 2, 0);//绕自己的Top旋转
matrix.postTranslate(viewWidth / 2, axisY);//旋转完后移动图片到轴线下面
canvas.drawBitmap(b1, matrix, mPaint);
//绘制上面那张图片
camera.save();
camera.rotateX(90-currentDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth / 2, -viewHeight);//绕自己的bottom旋转
matrix.postTranslate(viewWidth / 2, axisY);//旋转完后移动图片到轴线上面
canvas.drawBitmap(b2, matrix, mPaint);
}
/**
* 设置当前翻转角度,并重新绘制
* @param degree
*/
public void setRotateDegree(int degree) {
currentDegree = degree;
axisY = degree / 90f * viewHeight;
invalidate();
}
}
- 从调用
setRotateDegree(int degree)
为入口,axisY = degree / 90f * viewHeight
计算两张图片的旋转轴的Y坐标,也就是两张图的相交线的Y坐标。invalidate()
重新绘制,调用onDraw()
方法 -
onDraw()
方法中,Camera
旋转要注意需要先将矩阵平移至坐标中心点,这也是使用Camera
使用需要注意的地方 - 特别需要留意
Camera
变换的坐标系和绘制坐标系是不一样的,最后附上Camera
坐标系