如果做游戏或者视频相关开发的时候,将会用到SurfaceView。对于SurfaceView,首先需要了解一下它自己的位置:
extends View
java.lang.Object |
↳ |
android.view.View |
|
↳ |
android.view.SurfaceView |
由手册可知:
SurfaceView是View类的继承类,这个View里内嵌了一个专门用于绘制的Surface,这个可以类似的理解成为一个在View里的Canvas。你可以控制这个Surface的格式和尺寸。Surfaceview类则控制这个Surface在屏幕上的正确位置。
《Android高级编程》里这样说:
在一般情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如按钮单击或者文本输入)。
对于一个View的onDraw()方法,不能够满足将其移动到后台线程中去。因为从后台线程修改一个GUI元素会被显式地禁止的。
当需要快速地更新View的UI,或者当前渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。
1. 何时应该使用SurfaceView
SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。
SurfaceView封装的Surface支持所有标准的Canvas方法进行绘图,同时也支持完全的OpenGL ES 库。
使用OpenGL,你可以在Surface上绘制任何支持2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。
对于显示动态的3D图像来说,例如,那些使用Google Earth 功能的应用程序,或者那些提供沉浸体验的交互式游戏,Surface特别有用。它还是实时显示摄像头预览的最佳选择。
2. 创建一个新的SurfaceView控件
创建一个新的SurfaceView控件需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。
SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它SurfaceHolder对象的引用,其中包含了当前有效的Surface。
一个典型的SurfaceView 设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。
3. 使用SurfaceView创建3D控件
Android完全支持OpenGL ES 3D 渲染框架,其中包含了对设备的硬件加速的支持。SurfaceView控件提供了一个表面,可以在它上面渲染你的OpenGL场景。
那么我们在使用的时候可以这样使用:
被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
可以直接从内存或硬件设备比如相机等取得图像数据,是个非常重要的绘图容器。
它的特性是:可以在主线程之外的线程中向屏幕绘图。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。
如何去使用一个SurfaceView:
首先继承SurfaceView并实现SurfaceHolder.Callback接口。因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始。可以被直接复制到显存从而显示出来,这使得显示速度会非常快,而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。
需要重写的方法
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在surface的大小发生改变时激发
(2)public void surfaceCreated(SurfaceHolder holder){}
//在创建时激发,一般在这里调用画图的线程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {}
//销毁时激发,一般在这里将画图的线程停止、释放。
整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
关于SurfaceHolder:
这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。
下面给出一个应用的实例,本实例实现的是相机的预览和拍照功能,其中,为了节省存储空间,当退出应用程序时,拍照文件会自动删除,这里把它作为临时文件保存。
下面是它的实现截图:
由于是在模拟器中运行,所以显示的是无内存卡。
下面给出实现本实例的源码:
1.首先,我们需要做权限的声明
- <uses-permission android:name="android.permission.CAMERA"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2.给出布局文件
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@drawable/white"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:id="@+id/myTextView1"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textColor="@drawable/blue"
- android:text="@string/hello"
- />
- <SurfaceView
- android:id="@+id/mSurfaceView1"
- android:visibility="visible"
- android:layout_width="320px"
- android:layout_height="240px">
- </SurfaceView>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <Button
- android:id="@+id/myButton1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/str_button1"/>
- <Button
- android:id="@+id/myButton2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/str_button2"/>
- <Button
- android:id="@+id/myButton3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/str_take_picture"/>
- </LinearLayout>
- <ImageView
- android:id="@+id/myImageView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
- </LinearLayout>
3.主程序代码
- package irdc.ex07_15;
-
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.util.List;
-
- import android.app.Activity;
-
-
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.PixelFormat;
-
-
- import android.hardware.Camera;
-
-
- import android.hardware.Camera.PictureCallback;
- import android.hardware.Camera.ShutterCallback;
- import android.os.Bundle;
- import android.util.DisplayMetrics;
- import android.util.Log;
- import android.view.SurfaceHolder;
- import android.view.SurfaceView;
- import android.view.View;
- import android.view.Window;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.TextView;
- import android.widget.Toast;
-
-
- public class EX07_15 extends Activity implements SurfaceHolder.Callback
- {
-
- private Camera mCamera01;
- private Button mButton01, mButton02, mButton03;
-
-
- private ImageView mImageView01;
- private TextView mTextView01;
- private static String TAG = "HIPPO_DEBUG";
- private SurfaceView mSurfaceView01;
- private SurfaceHolder mSurfaceHolder01;
-
-
-
- private boolean bIfPreview = false;
-
-
- private String strCaptureFilePath = "/sdcard/camera_snap.jpg";
-
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.main);
-
-
- if(!checkSDCard())
- {
-
- mMakeTextToast
- (
- getResources().getText(R.string.str_err_nosd).toString(),
- true
- );
- }
-
-
- DisplayMetrics dm = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(dm);
-
-
-
-
-
-
-
-
- mTextView01 = (TextView) findViewById(R.id.myTextView1);
- mImageView01 = (ImageView) findViewById(R.id.myImageView1);
-
-
- mSurfaceView01 = (SurfaceView) findViewById(R.id.mSurfaceView1);
-
-
- mSurfaceHolder01 = mSurfaceView01.getHolder();
-
-
- mSurfaceHolder01.addCallback(EX07_15.this);
-
-
-
-
-
-
-
-
- mSurfaceHolder01.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-
- mButton01 = (Button)findViewById(R.id.myButton1);
- mButton02 = (Button)findViewById(R.id.myButton2);
- mButton03 = (Button)findViewById(R.id.myButton3);
-
-
- mButton01.setOnClickListener(new Button.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
-
-
-
- initCamera();
- }
- });
-
-
- mButton02.setOnClickListener(new Button.OnClickListener()
- {
- @Override
- public void onClick(View arg0)
- {
-
-
-
- resetCamera();
- }
- });
-
-
- mButton03.setOnClickListener(new Button.OnClickListener()
- {
- @Override
- public void onClick(View arg0)
- {
-
-
-
- if(checkSDCard())
- {
-
- takePicture();
- }
- else
- {
-
- mTextView01.setText
- (
- getResources().getText(R.string.str_err_nosd).toString()
- );
- }
- }
- });
- }
-
-
- private void initCamera()
- {
- if(!bIfPreview)
- {
-
- try
- {
-
-
-
-
-
-
-
- mCamera01 = Camera.open();
- }
- catch(Exception e)
- {
- Log.e(TAG, e.getMessage());
- }
- }
-
- if (mCamera01 != null && !bIfPreview)
- {
- try
- {
- Log.i(TAG, "inside the camera");
- mCamera01.setPreviewDisplay(mSurfaceHolder01);
-
- Camera.Parameters parameters = mCamera01.getParameters();
-
- parameters.setPictureFormat(PixelFormat.JPEG);
-
- List<Camera.Size> s=parameters.getSupportedPreviewSizes();
-
- try
- {
- if(s!=null)
- {
- for(int i=0;i<s.size();i++)
- {
- Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));
- }
- }
- parameters.setPreviewSize(320, 240);
-
-
-
- s=parameters.getSupportedPictureSizes();
- try
- {
- if(s!=null)
- {
- for(int i=0;i<s.size();i++)
- {
- Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));
- }
- }
-
- parameters.setPictureSize(512, 384);
-
-
- mCamera01.setParameters(parameters);
-
- mCamera01.setPreviewDisplay(mSurfaceHolder01);
-
- mCamera01.startPreview();
-
- bIfPreview = true;
- Log.i(TAG, "startPreview");
- }
- catch (Exception e)
- {
- Log.i(TAG, e.toString());
- e.printStackTrace();
- }
- }
- catch (Exception e)
- {
- Toast.makeText
- (
- EX07_15.this,
- "initCamera error.",
- Toast.LENGTH_LONG
- ).show();
- e.printStackTrace();
- }
- }
- catch (IOException e)
- {
-
- mCamera01.release();
- mCamera01 = null;
- Log.i(TAG, e.toString());
- e.printStackTrace();
- }
- }
- }
-
-
- private void takePicture()
- {
- if (mCamera01 != null && bIfPreview)
- {
-
- mCamera01.takePicture(shutterCallback, rawCallback, jpegCallback);
- }
- }
-
-
- private void resetCamera()
- {
- if (mCamera01 != null && bIfPreview)
- {
- mCamera01.stopPreview();
-
-
-
-
-
- Log.i(TAG, "stopPreview");
- bIfPreview = false;
- }
- }
-
- private ShutterCallback shutterCallback = new ShutterCallback()
- {
- public void onShutter()
- {
-
- }
- };
-
- private PictureCallback rawCallback = new PictureCallback()
- {
- public void onPictureTaken(byte[] _data, Camera _camera)
- {
-
- }
- };
-
- private PictureCallback jpegCallback = new PictureCallback()
- {
- public void onPictureTaken(byte[] _data, Camera _camera)
- {
-
-
-
- Bitmap bm = BitmapFactory.decodeByteArray(_data, 0, _data.length);
-
-
- File myCaptureFile = new File(strCaptureFilePath);
- try
- {
- BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
-
- bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
-
-
- bos.flush();
-
-
- bos.close();
-
-
- mImageView01.setImageBitmap(bm);
-
-
- resetCamera();
-
-
- initCamera();
- }
- catch (Exception e)
- {
- Log.e(TAG, e.getMessage());
- Log.e(TAG, e.toString());
- }
- }
- };
-
-
- private void delFile(String strFileName)
- {
- try
- {
- File myFile = new File(strFileName);
- if(myFile.exists())
- {
- myFile.delete();
- }
- }
- catch (Exception e)
- {
- Log.e(TAG, e.toString());
- e.printStackTrace();
- }
- }
-
- public void mMakeTextToast(String str, boolean isLong)
- {
- if(isLong==true)
- {
- Toast.makeText(EX07_15.this, str, Toast.LENGTH_LONG).show();
- }
- else
- {
- Toast.makeText(EX07_15.this, str, Toast.LENGTH_SHORT).show();
- }
- }
-
- private boolean checkSDCard()
- {
-
- if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder surfaceholder, int format, int w, int h)
- {
-
- Log.i(TAG, "Surface Changed1");
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder surfaceholder)
- {
-
- Log.i(TAG, "Surface Changed2");
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder surfaceholder)
- {
-
-
- try
- {
- delFile(strCaptureFilePath);
- mCamera01.stopPreview();
- mCamera01.release();
- mCamera01 = null;
- Log.i(TAG, "Surface Destroyed");
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- }
-
- @Override
- protected void onPause()
- {
-
- try
- {
- resetCamera();
- mCamera01.release();
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- super.onPause();
- }
-
- }
转自:http://blog.csdn.net/dlutbrucezhang/article/details/8571715