安卓应用中实现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。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。