SurfaceView的使用(在子线程中画视图,播放fig动态图片,作为相机的预览画面)
SurfaceView,表面视图的使用,可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图视图。
它的特性是:可以在子线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,
从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出来
一句话 就是用来展示画面的
例子1:在主屏幕中画矩形和文字
import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Bundle; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * 表面视图SurfaceView的使用,在子线程中向屏幕画视图 */ public class MainActivity extends AppCompatActivity { private MyThread myThread;//画东西的线程 private SurfaceView surfaceView;//所画的东西就显示在这个视图上 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //找到表面视图,得到处理者,添加回调 surfaceView = (SurfaceView) findViewById(R.id.surfaceView); SurfaceHolder holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { Log.i("tag", "surfaceCreated: 创建"); myThread = new MyThread(holder); myThread.flag = true; myThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("tag", "尺寸发生改变了的时候调用,第一次创建也会调用"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("tag", "销毁了,activity从可见变为不可见就会调用"); myThread.flag = false;//改变标签来停止线程 } }); } class MyThread extends Thread{ private SurfaceHolder holder; public boolean flag; public MyThread(SurfaceHolder holder) { super(); this.holder = holder; } @Override public void run() { super.run(); int count=1; Canvas canvas = null;//画板 while(flag){ synchronized (holder) { canvas = holder.lockCanvas(null); canvas.drawColor(Color.BLACK);//设置画布背景颜色,这样每次都让画板清空了 Paint p = new Paint(); //创建画笔 p.setColor(Color.WHITE); p.setStyle(Paint.Style.STROKE);//只画轮廓 Rect r = new Rect(100, 50, 300, 250); canvas.drawRect(r, p); p.setTextSize(30); canvas.drawText("这是第" + (count++) + "秒", 100, 310, p); SystemClock.sleep(50); holder.unlockCanvasAndPost(canvas);//结束锁定画图,并提交改变。 } } Log.i("tag", "run: 结束"); } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <SurfaceView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/surfaceView" /> </LinearLayout>
例2:利用SufaceView在屏幕上播放动gif态图片 ,布局文件同上
import android.graphics.Canvas; import android.graphics.Movie; import android.os.Bundle; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.io.IOException; /** * 表面视图SurfaceView的使用,播放gif动态图片 */ public class MainActivity extends AppCompatActivity { /** * 在SurfaceView上画视图的线程 */ private MyThread myThread; /** * 显示所画内容的控件 */ private SurfaceView surfaceView; /** * 将gif图片分成一帧一帧资源的对象 */ private Movie movie; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //找到表面视图 surfaceView = (SurfaceView) findViewById(R.id.surfaceView); SurfaceHolder holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { try { Log.i("tag", "主线程运行的,创建表面视图"); myThread = new MyThread(holder); myThread.flag = true; //解析gif动态图片为一帧帧的资源 movie = Movie.decodeStream(getResources().getAssets().open("activie.gif")); myThread.start();//开启线程播放gif图片 } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("tag", "主线程中运行的,改变了,第一次创建也会调用"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { try { Log.i("tag", "主线程中运行的,销毁了,activity从可见变为不可见就会调用"); myThread.flag = false;//改变标签来停止线程 myThread.join();//代表让myThread线程执行完,主线程才能得到运行 } catch (InterruptedException e) { e.printStackTrace(); } } }); } class MyThread extends Thread{ private SurfaceHolder holder;//表面视图的处理者 public boolean flag;//控制线程运行的标记 public MyThread(SurfaceHolder holder) { super("自定义线程名"); this.holder = holder; } @Override public void run() { Canvas canvas;//画板 long startTime = SystemClock.uptimeMillis();//开始播放的时间 while(flag){ synchronized (holder) { Log.i("tag", "run: 状态"+flag); //锁定画板 canvas = holder.lockCanvas(null); //获得当前时间 long currentTime = SystemClock.uptimeMillis();; //获得gif播放的总时间 int duration = movie.duration(); //计算当前应该播放的位置,取余可以实现循环播放 int time = (int) ((currentTime - startTime)%duration); movie.setTime(time);//播放这个时间的帧 //退出当前页面时,获取不到canvas对象,如果不判断容易空指针 if(canvas != null ){ //将这帧图片画到画板上,0,0代表起点位置 movie.draw(canvas, 0, 0); holder.unlockCanvasAndPost(canvas);//解除锁定画板 } } } Log.i("tag", "run: 结束"); } } }
例三:利用SurfaceView作为摄像头的预览画面 这里为在自己的应用中拍照,调用系统相机见: 相机、录像与相册的调用
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.Camera; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.ImageView; /** * 表面视图SurfaceView的使用,播放gif动态图片 */ public class MainActivity extends AppCompatActivity { /** * 摄像头画面的显示 */ private SurfaceView surfaceView; /** * 摄像头对象 */ private Camera camera; /** * 显示拍下的图片 */ private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.imageView); //找到表面视图 surfaceView = (SurfaceView) findViewById(R.id.surfaceView); SurfaceHolder holder = surfaceView.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { try { Log.i("tag", "主线程运行的,创建表面视图"); //打开摄像头,0代表前置摄像头,1代表后置 camera = Camera.open(); //设置预览参数,与surfaceView绑定 camera.setPreviewDisplay(holder); camera.setDisplayOrientation(90);//90度显示 camera.startPreview();//开启预览 } catch (Exception e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("tag", "主线程中运行的,改变了,第一次创建也会调用"); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("tag", "主线程中运行的,销毁了,activity从可见变为不可见就会调用"); if(camera != null){ camera.stopPreview();//停止预览 camera.release();//释放资源 camera = null; } } }); } /** * 按钮的单击事件,按下拍照 * 需要打开摄像头权限:<uses-permission android:name="android.permission.CAMERA"/> */ public void btnOnClick(View v){ //参数说明: 1.开门回调 2.原图片回调 3.jpg格式图片回调 camera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { //拍照成功的回调,data为图片二进制的数据 surfaceView.setVisibility(View.GONE);//关闭预览的显示,会执行surfaceDestroyed()方法 Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length); imageView.setImageBitmap(bitmap); imageView.setVisibility(View.VISIBLE);//显示 } }); } @Override protected void onDestroy() { super.onDestroy(); if(camera != null){ camera.stopPreview();//停止预览 camera.release();//释放资源 } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--预览显示--> <SurfaceView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/surfaceView" /> <!--显示拍下的图片 --> <ImageView android:id="@+id/imageView" android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:onClick="btnOnClick" android:text="按下拍照" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
效果图: