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、实例:

主布局:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.MainActivity"> <Button
android:id="@+id/buffer_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceVie使用缓冲刷新UI几面"/> <Button
android:id="@+id/video_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SurfaceView播放视频"/> </LinearLayout>

主布局的Activity:

 public class MainActivity extends AppCompatActivity implements View.OnClickListener {

     protected Button mBufferBtn;
protected Button mVideoBtn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
initView();
} @Override
public void onClick(View view) {
Intent intent = new Intent();
if (view.getId() == R.id.buffer_btn) {
intent.setClass(this, BufferActivity.class);
} else if (view.getId() == R.id.video_btn) {
intent.setClass(this, VideoActivity.class);
}
startActivity(intent);
} private void initView() {
mBufferBtn = (Button) findViewById(R.id.buffer_btn);
mBufferBtn.setOnClickListener(MainActivity.this);
mVideoBtn = (Button) findViewById(R.id.video_btn);
mVideoBtn.setOnClickListener(MainActivity.this);
}
}
SurfaceVie使用缓冲刷新UI几面:
布局
 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.activity.BufferActivity"> <SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </RelativeLayout>

Activity

 // SurfaceView配合子线程,使用双缓冲刷新UI界面
// SurfaceHolder:用于管理缓冲区Surface的类(生命周期)
public class BufferActivity extends AppCompatActivity implements SurfaceHolder.Callback { protected SurfaceView mSurfaceView;
private SurfaceHolder mHolder; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_buffer);
initView();
initSurfaceHolder();
} // 初始化Surface的管理者
private void initSurfaceHolder() {
mHolder = mSurfaceView.getHolder();
// 添加管理生命周期的接口回调
mHolder.addCallback(this);
} private void initView() {
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
} // 缓冲区创建
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d("1507", "surfaceCreated");
new DrawThread(this).start();
} // 缓冲区内容改变(子线程渲染UI的过程)
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("1507", "surfaceChanged");
} // 缓冲区销毁
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("1507", "surfaceDestroyed");
} // 绘制UI的子线程
private static class DrawThread extends Thread { // private BufferActivity mActivity; // 使用弱引用持有Activity的实例,
// 当Activity销毁时,当前线程会释放Activity,避免Activity无法释放导致的内存泄漏
private WeakReference<BufferActivity> mWeakReference; public DrawThread(BufferActivity activity) {
// mActivity = activity;
mWeakReference = new WeakReference<BufferActivity>(activity);
} @Override
public void run() {
super.run(); // 通过弱引用获取持有的Activity实例
BufferActivity activity = mWeakReference.get(); if (activity == null) {
return;
} // 获取SurfaceView的盖度
int height = activity.mSurfaceView.getHeight(); // 创建画笔
Paint paint = new Paint();
paint.setColor(Color.GREEN);// 画笔颜色
paint.setStrokeWidth(10);// 画笔粗细。注意:Java中设置的尺寸单位都是px
paint.setStyle(Paint.Style.FILL_AND_STROKE);// 设置实心
paint.setAntiAlias(true);// 设置是否抗锯齿 Canvas canvas = null;
for (int i = 0; i < height; i += 5) { // 获取Surface中的画布
canvas = activity.mHolder.lockCanvas();// 锁定画布 // 当SurfaceView随着Activity销毁时,缓冲区Surface也会随着销毁,无法获取缓冲区的画布
if (canvas == null) {
return;
} canvas.drawColor(Color.RED);// 设置画布背景覆盖之前的残影
// 使用画笔在画布上绘制指定形状
canvas.drawCircle(100, i + 50, 50, paint);// 圆心x坐标,圆心y坐标,半径,画笔 // 缓冲区的画布绘制完毕,需要解锁并提交给窗口展示
activity.mHolder.unlockCanvasAndPost(canvas); } }
} }
SurfaceView播放视频:
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.surfaceview.activity.VideoActivity"> <Button
android:id="@+id/play_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/> <net.bwie.surfaceview.widget.MyVideoSurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>

Activity

/**
* 1、获取播放源
* 2、准备SurfaceView
* 3、多媒体交给MediaPlayer处理
*/
public class VideoActivity extends AppCompatActivity implements View.OnClickListener { protected MyVideoSurfaceView mSurfaceView;
protected Button mPlayBtn; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_video);
initView(); } // 运行、可见
@Override
protected void onStart() {
super.onStart();
} // 可交互
@Override
protected void onResume() {
super.onResume();
} private void play() {
String videoPath = Environment.getExternalStorageDirectory().getPath() +
"/VID_20171117_144736.3gp";// 外部存储根路径 mSurfaceView.playVideo(videoPath);
} private void initView() {
mSurfaceView = (MyVideoSurfaceView) findViewById(R.id.surface_view);
mPlayBtn = (Button) findViewById(R.id.play_btn);
mPlayBtn.setOnClickListener(VideoActivity.this);
} @Override
public void onClick(View view) {
if (view.getId() == R.id.play_btn) {
play();
}
}
}

MyVideoSurfaceView类:

 public class MyVideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

     private SurfaceHolder mHolder;
private MediaPlayer mMediaPlayer; public MyVideoSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs); init();
} private void init() {
// 获取Surface换朝哪个区的持有者
mHolder = getHolder();
mHolder.addCallback(this);
} // 设置播放源
public void playVideo(String path) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
} try {
// 设置播放源
mMediaPlayer.setDataSource(path);
// 设置多媒体的显示部分:使用SurfaceHolder渲染画面
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
} } @Override
public void surfaceCreated(SurfaceHolder holder) { } @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaPlayer.release();
mMediaPlayer = null;
} }

别忘了加权限:

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

 
 

SurfaceView获取本地视频播放的更多相关文章

  1. VideoView获取本地视频播放

    主布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

  2. Win10《芒果TV》更新v3.5.0夏至版:会员尊享蓝光画质,关联本地视频播放

    在Win10秋季创意者更新前夕,Win10版<芒果TV>全平台同步更新夏至版v3.5.0,新增会员蓝光画质,关联本地视频播放,进一步提升使用体验. Win10版<芒果TV>V3 ...

  3. android获取本地图片并显示图片

    import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content ...

  4. Linux编程获取本地IP

    #include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netine ...

  5. Java获取本地IP地址

    import java.net.InetAddress; import java.net.UnknownHostException; public class IpTest { public stat ...

  6. Android开发之获取本地视频和获取自拍视频

    1.获取本地所有视频 public void getLoadMedia() { Cursor cursor = UILApplication.instance.getApplicationContex ...

  7. 获取本地IP地址信息

    2012-06-05    /// <summary>         /// 获取本地IP地址信息         /// </summary>         void G ...

  8. 用angular实时获取本地localStorage数据,实现一个模拟后台数据登入的效果

    研究了一上午,终于做出了,实时获取本地localStorage来模拟注册登入~~~ <!DOCTYPE html><html><head lang="en&qu ...

  9. Android之获取本地图片并压缩方法

    这两天在做项目时,做到上传图片功能一块时,碰到两个问题,一个是如何获取所选图片的路径,一个是如何压缩图片,在查了一些资料和看了别人写的后总算折腾出来了,在此记录一下. 首先既然要选择图片,我们就先要获 ...

随机推荐

  1. NVIDIA GRID 和 NICE DCV 技术用于实现 Linux 和 Windows® 图形加速虚拟桌面

    NVIDIA GRID 和 NICE DCV 技术用于实现 Linux 和 Windows® 图形加速虚拟桌面. NICE DCV: 满足 LINUX 和 WINDOWS 的远程 3D 通过 NICE ...

  2. code=exited,status=1/failure;failed to start LSB:Bring up/down networking

    环境: CentOS 7 vmware 12 操作: 复制可使用的vmware centOS 7系统至新环境 问题: 无法启动网络 查看“systemctl status network" ...

  3. Spring Boot 数据访问集成 MyBatis 与事物配置

    对于软件系统而言,持久化数据到数据库是至关重要的一部分.在 Java 领域,有很多的实现了数据持久化层的工具和框架(ORM).ORM 框架的本质是简化编程中操作数据库的繁琐性,比如可以根据对象生成 S ...

  4. Objective-C 字符串与数值互相转换

    Convert NSString to int NSString *aNumberString = @"123"; int i = [aNumberString intValue] ...

  5. JavaScript -- Window-Focus

    -----034-Window-Focus.html----- <!DOCTYPE html> <html> <head> <meta http-equiv= ...

  6. MySql主从同步和延迟同步

    MySql同步与延迟同步 Mysql同步 一 Mysql主服务器上操作 1 开启服务器上的log_bin功能 # vim/etc/my.cnf 增加一下两行 log_bin=mysql-bin ser ...

  7. 利用nginx解决跨域问题

    访问我的博客 前言 最近遇到了跨域问题,结合之前[微信支付开发本地接收异步通知回调]的经验,利用 Nginx 实现了跨域. 公司之前为了解决跨域问题,用的是 iFrame,反正对于只做后端的我而言,觉 ...

  8. 简易的命令行聊天室程序(Winsock,服务器&客户端)

    代码中使用WinSock2函数库,设计并实现了简单的聊天室功能.该程序为命令行程序.对于服务器和客户端,需要: 服务器:创建监听套接字,并按本地主机绑定:主线程监听并接受来自客户端的请求,并为该客户端 ...

  9. 手机App安全性测试初探

    目前手机App测试还是以发现bug为主,主要测试流程就是服务器接口测试,客户端功能性覆盖,以及自动化配合的性能,适配,压测等,对于App安全性测试貌似没有系统全面统一的标准和流程,其实安全性bug也可 ...

  10. OpenSSL修复加密漏洞、加强Logjam防御

    来源:TechTarget中国作者:Michael Heller翻译:张程程 OpenSSL项目团队为其密码库发布补丁以修复一个严重的漏洞(该漏洞可能允许攻击者解密HTTPS通信),同时强化对Logj ...