安卓应用中实现3d效果一般都是用Camera,确切的说应该是android.graphics.Camera,因为还有一个Camera类是用来操作摄像头的。
用android.graphics.Camera(后面直接用Camera)实现了一个3d效果。
效果图如下:

代码如下:
HSprite3d.java。
关键在于加粗的那几行。
package com.hanyeah.display3d;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
public class HSprite3d {
public float x=0;
public float y=0;
public float z=0;
public float rotationX=0;
public float rotationY=0;
public float rotationZ=0;
public boolean visible=true;
public double alpha=1.0;
public String name;
public boolean mouseChildren=true;
public boolean mouseEnabled=true;
protected HSprite3d parent;
private List_childList=new ArrayList();
public interface HRender{
public void execute(Canvas canvas,Paint paint);
}
public HSprite3d.HRender hrender;
public HSprite3d() {
// TODO Auto-generated constructor stub
}
//-------------------------容器方法----------------------------
/**
* [只读 (read-only)] 返回此对象的子项数目。
* @return
*/
public int getNumChildren(){
return _childList.size();
}
/**
* 将一个 HSprite3d 子实例添加到该 HSprite3dContainer 实例中。
* @param child
* @return
*/
public HSprite3d addChild(HSprite3d child){
_childList.remove(child);
_childList.add(child);
child.parent=this;
return child;
}
/**
* 将一个 HSprite3d 子实例添加到该 HSprite3dContainer 实例中。 该子项将被添加到指定的索引位置。
* @param child
* @param index
* @return
*/
public HSprite3d addChildAt(HSprite3d child,int index){
_childList.remove(child);
_childList.add(index, child);
child.parent=this;
return child;
}
/**
* 从 HSprite3dContainer 实例的子列表中删除指定的 child HSprite3d 实例。
* @param child
* @return
*/
public HSprite3d removeChild(HSprite3d child){
if(_childList.remove(child)){
child.parent=null;
}
return child;
}
/**
* 从 HSprite3dContainer 的子列表中指定的 index 位置删除子 HSprite3d。
* @param index
* @return
*/
public HSprite3d removeChildAt(int index){
HSprite3d child=_childList.remove(index);
child.parent=null;
return child;
}
/**
* 确定指定显示对象是否是 HSprite3dContainer 实例的子项。
* @param child
* @return
*/
public boolean contains(HSprite3d child){
return _childList.contains(child);
}
/**
* 返回位于指定索引处的子显示对象实例
* @param index
* @return
*/
public HSprite3d getChildAt(int index){
return _childList.get(index);
}
/**
* 返回具有指定名称的子显示对象。
* @param name
* @return
*/
public HSprite3d getChildByName(String name){
int len = _childList.size();
for(int i=0;i=0;i--){
_childList.get(i).parent=null;
_childList.remove(i);
}
}
//--------------------点击判断--------------------
//--------------------渲染-----------------
/**
* 渲染,只供引擎调用。请不要在自定义类中调用。
*/
public void _render(Canvas canvas,Paint paint,Camera camera){
if(!visible){
return;
}
canvas.save();
camera.save();
int alp=paint.getAlpha();
paint.setAlpha((int)(alp*alpha));
canvas.translate(x,y);
camera.translate(0, 0, z);
camera.rotate(rotationX, rotationY, rotationZ);
camera.applyToCanvas(canvas);
if(hrender!=null){
hrender.execute(canvas,paint);
}
for(int i=0,len=_childList.size();i<len;i++){
_childList.get(i)._render(canvas, paint, camera);
}
paint.setAlpha(alp);
canvas.restore();
camera.restore();
}
}MainActivity.java:
package com.example.cameratest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.hanyeah.display3d.HSprite3d;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView=new MyView(this);
setContentView(myView);
}
class MyView extends SurfaceView implements Callback, Runnable{
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
SurfaceHolder sfh=this.getHolder();
sfh.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
sp=new HSprite3d();
sp.x=300;
sp.y=600;
for(int i=0;i<num;i++){
HSprite3d sp0=new HSprite3d();
_list.add(sp0);
sp0.hrender=new HSprite3d.HRender() {
@Override
public void execute(Canvas canvas,Paint paint) {
// TODO Auto-generated method stub
paint.setColor(0xffff0000);
canvas.drawRect(-150, -400, 150, 0,paint);
}
};
}
Thread th=new Thread(this);
th.start();
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
private float rotationX=0;
private HSprite3d con;
private HSprite3d sp;
private int num=20;
private float n=0;
private float r=1300;
private List_list=new ArrayList();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
long start =System.currentTimeMillis();
try{
//Log.e("hanyeah", "======================");
SurfaceHolder sfh=this.getHolder();
Canvas canvas = sfh.lockCanvas();
canvas.drawColor(Color.BLACK);
/*
rotationX++;
canvas.save();
canvas.translate(200, 300);
Log.e("hanyeah", canvas.getMatrix().toString());
Camera camera=new Camera();
Matrix matrix=new Matrix();
camera.getMatrix(matrix);
camera.rotateX(rotationX);
camera.applyToCanvas(canvas);
Log.e("hanyeah", canvas.getMatrix().toString());
Paint paint=new Paint();
paint.setColor(0xffff0000);
canvas.drawRect(-150, -200, 150, 200,paint);
canvas.restore();
*/
for(int i=0;i<num;i++){
HSprite3d sp0=_list.get(i);
float _n=(n+i*360f/num);
double angle=_n*Math.PI/180;
sp0.x=(float) (r*Math.cos(angle));
sp0.z=(float) (r*Math.sin(angle));
sp0.rotationY=(180-_n+360)%180-90;
}
Collections.sort(_list,new Comparator() {
@Override
public int compare(HSprite3d arg0, HSprite3d arg1) {
// TODO Auto-generated method stub
return arg0.z-arg1.z>0?1:-1;
}
});
sp.removeAllChildren();
for(int i=0;i<num;i++){
sp.addChild(_list.get(i));
}
sp.z=(float) (-1.8*r);
//sp.rotationX=10;
n++;
Camera camera=new Camera();
Paint paint=new Paint();
sp._render(canvas, paint, camera);
sfh.unlockCanvasAndPost(canvas);
}
catch(Exception e){
Log.e("hanyeah_onEnterFrame:", "error:"+e);
}
long end=System.currentTimeMillis();
long delay=(long)(1000/30);
if(end-start<delay){
try {
Thread.sleep(delay-(end-start));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}请注意下面几行。
canvas.translate(x,y); camera.translate(0, 0, z); camera.rotate(rotationX, rotationY, rotationZ); camera.applyToCanvas(canvas);
新建一个camera对象;camera进行3d变换;再将变换后的camera应用于canvas。
其实这里面是有问题的,我们知道2d变化的变换矩阵是3*3的,3d变换的变换矩阵是4*4的,Matrix是3*3的矩阵,只能应用于2d变换,camera能进行3d变换,应该对应一个4*4的矩阵。因此camera.applyToCanvas(canvas)必然会丢失一些信息,虽然我们不知道内部是如何实现的,导致最终的显示效果是不符合物理规律的。从最上面那张效果图也能看出来,实际应该是越大的矩形之间的间距越大,越小的矩形间距越小,可对比:3d照片墙。
因此,用Camera并不能实现真正的3d效果,应用时一定要注意。
网上好多安卓3d照片墙的例子都是用的Gallery+Camera。具体可自行搜索。
之前理解有误。camera.getMatrix(matrix)是将camera应用于matrix,不是根据matrix来设置camera。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。