android SurfaceView与SurfaceHolder使用解析
寒到要死
7年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/f2170ad47ea192e7492a3128c82f7724.jpg"></p> <p>surfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface,可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。</p> <h2>SurfaceView分析</h2> <ol> <li>surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。</li> <li>surfaceview提供了一个可见区域,只有在这个可见区域内 surface 部分内容才可见,可见区域外的部分不可见。</li> <li>surface 的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。</li> <li>注意,如果 surface 上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件之间的透明效果,这会影响性能。</li> <li>SurfaceView类的目的在于提供一个可以使用绘图线程渲染到屏幕的surface。</li> <li> <p>所有的SurfaceView和surfaceHolder.Callback方法都应该在主线程调用,所以对于绘图线程使用的变量应该做同步处理。</p> <p>从android N开始,SurfaceView窗口的位置与其他View同步更新,意味着对屏幕上的SurfaceView做转换和缩放时,不会再出现重影。</p> </li> <li> <p>一定要保证绘图线程仅在surface可用时对其访问,即在SurfaceHolder.Callback.surfaceCreated()与SurfaceHolder.Callback.surfaceDestroyed()之间。</p> </li> </ol> <h2>SurfaceHolder简介</h2> <p>SurfaceHolder是一个持有surface的抽象接口,可以控制surface的大小、格式、编辑、监听surface改变,一般通过SurfaceView实现。</p> <p>在SurfaceHolder的方法中,下面几个经常用到:</p> <ol> <li>abstract void addCallback(SurfaceHolder.Callback callback);// 给SurfaceView当前的持有者一个回调对象。</li> <li>abstract Canvas lockCanvas();// 锁定画布,一般在锁定后就可以通过其返回的画布对象进行画图操作。</li> <li>abstract Canvas lockCanvas(Rect dirty);// 锁定画布的某个区域进行画图等。因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。</li> <li>abstract void unlockCanvasAndPost(Canvas canvas);// 结束锁定画图,并提交改变。</li> </ol> <h2>SurfaceView搭配SurfaceHolder进行绘图</h2> <ol> <li>当SurfaceView的窗口可见时,创建surface;需要实现SurfaceCreated(SurfaceHolder)和surfaceDestoryed(SurfaceHolder)接口,监测窗口显示、隐藏时surface的创建和销毁。</li> <li>SurfaceView中没有提供直接获取surface的方法,需要通过surface的包装类SurfaceHolder来操作surface,获取SurfaceHolder可以使用SurfaceV的getHolder()方法;</li> <li>在Surface上绘图时,需要在绘图线程中通过SurefaceHolder的lockCanvas()方法获取surface区域,由于是在绘图线程工作,所以绘制完毕后需要调用SurfaceHolder 的 unlockCanvasAndPost()方法解锁 Canvas,并且让 UI 线程把 Surface 上面的东西绘制到 View 的 Canvas 上面。</li> </ol> <pre> <code class="language-java">public classGameUIextendsSurfaceViewimplementsSurfaceHolder.Callback{ private SurfaceHolder holder; private RenderThread renderThread; private boolean isDraw = false;// 控制绘制的开关 publicGameUI(Context context){ super(context); holder = this.getHolder(); holder.addCallback(this); renderThread = new RenderThread(); } @Override publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intwidth,intheight){ } @Override publicvoidsurfaceCreated(SurfaceHolder holder){ isDraw = true; renderThread.start(); } @Override publicvoidsurfaceDestroyed(SurfaceHolder holder){ isDraw = false; } /** * 绘制界面的线程 * * @author Administrator * */ private classRenderThreadextendsThread{ @Override publicvoidrun(){ // 不停绘制界面 while (isDraw) { drawUI(); } super.run(); } } /** * 界面绘制 */ publicvoiddrawUI(){ Canvas canvas = holder.lockCanvas(); try { drawCanvas(canvas); } catch (Exception e) { e.printStackTrace(); } finally { holder.unlockCanvasAndPost(canvas); } } privatevoiddrawCanvas(Canvas canvas){ // 在 canvas 上绘制需要的图形 } } </code></pre> <h2>SuraceView搭配SurfaceHolder进行相机预览</h2> <h2>SurfaceView子类代码</h2> <pre> <code class="language-java">/** A basic Camera preview class */ public classCameraPreviewextendsSurfaceViewimplementsSurfaceHolder.Callback{ private SurfaceHolder mHolder; private Camera mCamera; publicCameraPreview(Context context, Camera camera){ super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } publicvoidsurfaceCreated(SurfaceHolder holder){ // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } publicvoidsurfaceDestroyed(SurfaceHolder holder){ // empty. Take care of releasing the Camera preview in your activity. } publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intw,inth){ // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } } </code></pre> <h2>布局代码</h2> <pre> <code class="language-java"><?xml version="1.0" encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/camera_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/button_capture" android:text="Capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout> </code></pre> <h2>activity中调用</h2> <pre> <code class="language-java">public classCameraActivityextendsActivity{ private Camera mCamera; private CameraPreview mPreview; @Override publicvoidonCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an instance of Camera mCamera = getCameraInstance(); // Create our Preview view and set it as the content of our activity. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(mPreview); } } </code></pre> <p> </p> <p>来自:https://wangyantao.github.io/surfaceview/</p> <p> </p>