Android之SurfaceView学习-----using SurfaceView class创建一个view

本文详细介绍了Android中的SurfaceView组件,包括其特点、应用场景及如何在实际项目中使用。重点讲解了SurfaceView与SurfaceHolder的关系,以及如何利用多线程进行高效绘图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


转自:http://www.cnblogs.com/xuling/archive/2011/06/06/android.html

Android之SurfaceView学习(一)

首先我们先来看下官方API对SurfaceView的介绍

SurfaceView的API介绍

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().

The Surface will be created for you while the SurfaceView's window is visible; you should implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.

One of the purposes of this class is to provide a surface in which a secondary thread can render in to the screen. If you are going to use it this way, you need to be aware of some threading semantics:

•All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread. 
•You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed(). 
对应的中文翻译
SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
        surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
        你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。
        surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
        surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
        1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
        2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

 

接下来呢,说说自己对它的理解
1、定义

可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。

它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。

2、实现

首先继承SurfaceView并实现SurfaceHolder.Callback接口
使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始(Surface—表面,这个概念在 图形编程中常常被提到。基本上我们可以把它当作显存的一个映射,写入到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)结束锁定画图,并提交改变,将图形显示。


3、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);
// 结束锁定画图,并提交改变。
4、实例

这里的例子实现了一个矩形和一个计时器

View Code 
 package xl.test;
 
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 
 public class ViewTest extends Activity {
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(new MyView(this));
     }
     //视图内部类
     class MyView extends SurfaceView implements SurfaceHolder.Callback
     {
         private SurfaceHolder holder;
         private MyThread myThread; 
         public MyView(Context context) {
             super(context);
             // TODO Auto-generated constructor stub
             holder = this.getHolder();
             holder.addCallback(this);
             myThread = new MyThread(holder);//创建一个绘图线程
         }
 
         @Override
         public void surfaceChanged(SurfaceHolder holder, int format, int width,
                 int height) {
             // TODO Auto-generated method stub
             
         }
 
         @Override
         public void surfaceCreated(SurfaceHolder holder) {
             // TODO Auto-generated method stub
             myThread.isRun = true;
             myThread.start();
         }
 
         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {
             // TODO Auto-generated method stub
             myThread.isRun = false;
         }
         
     }
     //线程内部类
     class MyThread extends Thread
     {
         private SurfaceHolder holder;
         public boolean isRun ;
         public  MyThread(SurfaceHolder holder)
         {
             this.holder =holder; 
             isRun = true;
         }
         @Override
         public void run()
         {
             int count = 0;
             while(isRun)
             {
                 Canvas c = null;
                 try
                 {
                     synchronized (holder)
                     {
                         c = holder.lockCanvas();//锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
                         c.drawColor(Color.BLACK);//设置画布背景颜色
                         Paint p = new Paint(); //创建画笔
                         p.setColor(Color.WHITE);
                         Rect r = new Rect(100, 50, 300, 250);
                         c.drawRect(r, p);
                         c.drawText("这是第"+(count++)+"秒", 100, 310, p);
                         Thread.sleep(1000);//睡眠时间为1秒
                     }
                 }
                 catch (Exception e) {
                     // TODO: handle exception
                     e.printStackTrace();
                 }
                 finally
                 {
                     if(c!= null)
                     {
                         holder.unlockCanvasAndPost(c);//结束锁定画图,并提交改变。
 
                     }
                 }
             }
         }
     }
 }
下面这个是另一个博客中写的,感觉使用线程的时候,用runnable+thread的形式好一写,转自: http://rayleung.iteye.com/blog/420410
代码如下:
package com.ray.test;  
/* 
 * SurfaceView的示例程序 
 * 演示其流程 
 */  
import android.app.Activity;  
import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.RectF;  
import android.os.Bundle;  
import android.view.SurfaceHolder;  
import android.view.SurfaceView;  
  
public class Test extends Activity {  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(new MyView(this));  
    }  
      
    //内部类  
    class MyView extends SurfaceView implements SurfaceHolder.Callback{  
  
        SurfaceHolder holder;  
        public MyView(Context context) {  
            super(context);  
            holder = this.getHolder();//获取holder  
            holder.addCallback(this);  
            //setFocusable(true);  
              
        }  
  
        @Override  
        public void surfaceChanged(SurfaceHolder holder, int format, int width,  
                int height) {  
              
        }  
  
        @Override  
        public void surfaceCreated(SurfaceHolder holder) {  
            new Thread(new MyThread()).start();  
        }  
  
        @Override  
        public void surfaceDestroyed(SurfaceHolder holder) {  
              
        }  
          
        //内部类的内部类  
        class MyThread implements Runnable{  
  
            @Override  
            public void run() {  
                Canvas canvas = holder.lockCanvas(null);//获取画布  
                Paint mPaint = new Paint();  
                mPaint.setColor(Color.BLUE);  
                  
                canvas.drawRect(new RectF(40,60,80,80), mPaint);  
                holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像  
                  
            }  
              
        }  
          
    }  
}  
 



### 如何在 Android Studio 中使用 JavaCamera2View #### 创建项目并配置依赖项 为了能够在应用程序中使用 `JavaCamera2View`,首先需要创建一个新的 Android 项目,并确保项目的最低 SDK 版本设置为 API Level 21 或更高版本。接着,在模块级别的 `build.gradle` 文件中添加必要的权限声明和库依赖。 ```gradle dependencies { implementation 'org.opencv:opencv-android:4.5.3' // OpenCV 库用于图像处理功能[^1] } ``` #### 获取相机权限 由于访问设备摄像头涉及到隐私问题,因此必须请求用户授予应用相应的权限。这可以通过修改 `AndroidManifest.xml` 来实现: ```xml <uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera.autofocus"/> ``` #### 初始化 JavaCamera2View 组件 接下来是在布局文件中定义 `JavaCamera2View` 控件,并通过代码对其进行初始化操作。下面是一个简单的 XML 布局示例: ```xml <!-- res/layout/activity_main.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Camera preview view --> <org.opencv.android.JavaCamera2View android:id="@+id/java_camera_view" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> ``` 随后可以在 Activity 类里获取该控件实例并启动预览服务: ```java // MainActivity.java import org.opencv.android.BaseLoaderCallback; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.InstallService; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 { private static final String TAG = "MainActivity"; private BaseLoaderCallback mOpenCvCallBack; private JavaCamera2View javaCamera2View; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); javaCamera2View = findViewById(R.id.java_camera_view); javaCamera2View.setVisibility(SurfaceView.VISIBLE); javaCamera2View.setCvCameraViewListener(this); mOpenCvCallBack = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status){ case LoaderCallbackInterface.SUCCESS:{ Log.i(TAG, "OpenCV loaded successfully"); javaCamera2View.enableView(); } break; default:{ super.onManagerConnected(status); } break; } } }; if (!OpenCVLoader.initDebug()){ Log.d(TAG, "Internal OpenCV library not found."); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mOpenCvCallBack); }else{ Log.d(TAG,"OpenCV library found inside package. Using it!"); mOpenCvCallBack.onManagerConnected(LoaderCallbackInterface.SUCCESS); } } @Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) { return inputFrame.rgba(); // 返回原始帧数据作为 RGBA 图像矩阵 } @Override protected void onResume() { super.onResume(); if(!OpenCVLoader.initDebug()) { Log.d(TAG, "onResume :: Internal OpenCV library not found."); OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mOpenCvCallBack); } else { Log.d(TAG, "onResume :: OpenCV library found inside package. " +"Using it!"); mOpenCvCallBack.onManagerConnected(LoaderCallbackInterface.SUCCESS); } } @Override protected void onPause(){ super.onPause(); if(javaCamera2View != null) javaCamera2View.disableView(); } @Override public void onDestroy() { super.onDestroy(); if(javaCamera2View!=null) javaCamera2View.disableView(); } } ``` 上述代码展示了如何集成 `JavaCamera2View` 并显示来自后置摄像头的画面流。需要注意的是这里还包含了 OpenCV 的加载逻辑,因为通常情况下我们会配合计算机视觉算法一起工作[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值