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>

效果图:

 

posted @ 2016-10-03 17:51  ts-android  阅读(1382)  评论(0编辑  收藏  举报