SurfaceView 使用教程

2020-05-27  本文已影响0人  kaikoo

概述:

SurfaceView的基本使用
使用记录
因为毕业设计要接触到Android,之前没有接触过,我是个憨憨,东西能用就行

网上很多教程都是单独写了一个类再继承SurfaceView然后再去实现它的接口 但是我看完还是不知道如何使用
这里先介绍怎么使用原生的SurfaceView 然后再介绍继承的要怎么使用

先看最终效果(每次点击图片切换)


点击前
点击后

一. 原生SurfaceView

1.创建SurfaceView控件
在布局文件(activity_main.xml)中,找到SurfaceView控件,并设置好大小位置
再放至一个按钮备用


设置SurfaceView控件

2.在主页面(MainActivity)中,对SurfaceView控件和Button控件进行绑定

public class MainActivity extends AppCompatActivity {
    // 创建变量
    SurfaceView sv01;
    Button btn01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 绑定相关控件
        sv01 = findViewById(R.id.surfaceView01);
        btn01 = findViewById(R.id.button01);
    }
}

3.在主页面(MainActivity)中创建SurfaceHolder (新增代码两行)
(SurfaceHolder是一个用来管理SurfaceView的接口,要对SurfaceView进行操作都要经过他)
因为这里是要操作变量为sv01的surfaceview,所以sv01.getHolder()

public class MainActivity extends AppCompatActivity {
    // 创建变量
    ...
    // 创建SurfaceHolder
    SurfaceHolder holder01;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 绑定相关控件
        ...
        // 获取surfaceview的holder
        holder01 = sv01.getHolder();
    }
}

4.绘制图片
因为在使用中大多数是将拍摄到的画面(Bitmap类型)绘制在SurfaceView控件上,所以我这两就引入两张照片转为Bitmap类型来演示,当然你也可以创建一张空的Bitmap再用Paint类去绘制

先在main/res/drawable放两张照片

引入照片

在主页面(MainActivity)中创建Canvas和Bitmap变量

public class MainActivity extends AppCompatActivity {
    // 创建变量
    ...
    // 创建SurfaceHolder
    ...
    // 创建Canvas和Bitmap
    Canvas canvas01;
    Bitmap bitmap01,bitmap02;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 绑定相关控件
        ...
        // 获取surfaceview的holder
        ...
        // 初始化canvas和bitmap
        canvas01 = new Canvas();
        // R.drawable.图片名-drawable就是刚刚引入图片的那个文件夹
        bitmap01 = BitmapFactory.decodeResource(getResources(),R.drawable.a); 
        bitmap02 = BitmapFactory.decodeResource(getResources(),R.drawable.b);
    }
}

在主页面(MainActivity)中为holder添加callback方法

protected void onCreate(Bundle savedInstanceState) {
        ...
        // 初始化canvas和bitmap
        ...

        holder01.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });
    }

然后就剩下的就是绘制了,我们先在定义一个方法doDraw用来绘制图像
绘制的时候需要先锁定画布,然后绘制完后再解锁画布并提交,前后两句不理解的可以不管,写上就行

public void doDraw(Bitmap bitmap) {
        // 锁定画布
        canvas01 = holder01.lockCanvas();
        // draw something...
         canvas01.drawBitmap(bitmap,0,0,null); // (要绘制的图像,坐标left,坐标top,paint 没有就写null)
        // 解锁画布并提交
        holder01.unlockCanvasAndPost(canvas01);
    }

然后将doDraw方法放进addcallback中

holder01.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                doDraw(bitmap01);
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });

然后就绘制成功了


绘制结果

但是,这里仅仅只能显示一张图如果你从头到尾只显示一张图到这里就完事了
可是如果你要显示多张图片,就需要在绘制方法中加入一个无限循环,因为SurfaceView只能执行一次,如果需要显示多张图片,就不能让SurfaceView的执行停下来

// 创建变量
Bitmap bitmapSta;
boolean isRuning;
public void SvRun(Bitmap bitmap) {
        while (isRuning) {
            doDraw(bitmap);
            isRuning = false; //每次切换完图片 将isRunning状态设置为false
        }
    }

然后将addcallback里的doDraw()换成SvRun()

holder01.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                // 设置isRuning = true 使能进入循环
                isRuning = true;
                SvRun(bitmap01);
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });

然后给Button按钮添加一个监听函数,每次点击将isRunning设置为true 使能进入循环

        btn01.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                isRuning = true;
                // bitmapSta为当前显示的照片 在最开始赋值为bitmap01即可
                if (bitmapSta != bitmap02) {
                    SvRun(bitmap02);
                    bitmapSta = bitmap02;
                }else {
                    SvRun(bitmap01);
                    bitmapSta = bitmap01;
                }
            }
        });
        holder01.addCallback(new SurfaceHolder.Callback() {
           ...
        });

最终效果

未点击 点击1次 点击2次

原生SurfaceView使用完成

二. 自定义SurfaceView

  1. 创建一个class


    创建一个class

2.继承SurfaceView 实现SurfaceHolder.Callback接口

继承SurfaceView实现相关接口
根据他的提示点击ok就行
然后他会提示你创建构造方法 这里要同时选择3个! 2个我没试过 我只知道选1个在使用控件的时候会报错 按CTRL键同时选就行
构造方法
然后你就会发现还啥都没写就多了这么多方法
这个时候你就可以知道网上的那些文章在讲的是哪个部分了
都是在讲自定义SurfaceView的实现(因为有些方法需要自己去定义,还有的方法必须设置在构造函数里,比如说设置SurfaceView控件背景呈透明色就只能在构造函数里写,用原生的SurfaceView就做不到)

然后方法就跟原生的一样了
先设置布局 这里使用要xml进行布局

布局
image.png
然后切换回图形界面设置布局
布局

然后就跟原生的使用方法一样了
还是在主页面(MainActivity)中,也可以自己在自定义类里写
创建变量&&绑定控件

    MySurfaceView mysv; // MySurfaceView是你创建的类名
    mysv = findViewById(R.id.mysurface); // mysurface 是你刚刚设置的id
    holder02 = mysv.getHolder(); //新创建一个holder 用来获取新控件的holder

这里为了演示就再创建一个SvRun s和doDraw s,功能和原生时是一样的,只是holder不一样

    public void SvRuns(Bitmap bitmap) {
        while (isRuning) {
            doDraws(bitmap); // 注意调用得方法
            isRuning = false;
        }
    }
    public void doDraws(Bitmap bitmap) {
        // 锁定画布(写着就行)
        canvas01 = holder02.lockCanvas();  //这里是holder02
        // draw something...
        canvas01.drawBitmap(bitmap,0,0,null);
        // 解锁画布并提交 (写着就行)
        holder02.unlockCanvasAndPost(canvas01); //这里是holder02
    }

然后为自定义类创建addcallback方法

    // 自定义SurfaceView
    holder02.addCallback(new SurfaceHolder.Callback() {
      @Override
      public void surfaceCreated(SurfaceHolder holder) {
          isRuning = true;
          SvRuns(bitmap01); // 注意调用的方法
      }
      @Override
      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

      }
      @Override
      public void surfaceDestroyed(SurfaceHolder holder) {

      }
    });

然后运行

image.png

再修改一下button的方法
因为每次执行完绘制isRunning就会设置为false,所以要将他设置为true,这里为了偷懒就这样写= =

            public void onClick(View v) {
                isRuning = true;
                if (bitmapSta != bitmap02) {
                    SvRun(bitmap02);
                    isRuning = true; // SvRun执行完isRunning值为false 
                    SvRuns(bitmap02);
                    bitmapSta = bitmap02;
                }else {
                    SvRun(bitmap01);
                    isRuning = true;
                    SvRuns(bitmap01); // SvRun执行完isRunning值为false 
                    bitmapSta = bitmap01;
                }
            }

最终效果

点击前
点击后

最后说一句,还有一种用法是不使用自带的布局(R.layout.activity_main),就是在onCreate函数中的那句setContentView(R.layout.activity_main); 有的用法是写完自定义SurfaceView后,直接舍弃自带布局,将setContentView(R.layout.activity_main); 改为setContentView(new MySurfaceView(this)); 将自定义surfaceview作为唯一布局页面。

github: https://github.com/Kaiokai/Android-SurfaceView

上一篇 下一篇

猜你喜欢

热点阅读