SurfaceView获取本地视频播放
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获取本地视频播放的更多相关文章
- VideoView获取本地视频播放
主布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...
- Win10《芒果TV》更新v3.5.0夏至版:会员尊享蓝光画质,关联本地视频播放
在Win10秋季创意者更新前夕,Win10版<芒果TV>全平台同步更新夏至版v3.5.0,新增会员蓝光画质,关联本地视频播放,进一步提升使用体验. Win10版<芒果TV>V3 ...
- android获取本地图片并显示图片
import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content ...
- Linux编程获取本地IP
#include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netine ...
- Java获取本地IP地址
import java.net.InetAddress; import java.net.UnknownHostException; public class IpTest { public stat ...
- Android开发之获取本地视频和获取自拍视频
1.获取本地所有视频 public void getLoadMedia() { Cursor cursor = UILApplication.instance.getApplicationContex ...
- 获取本地IP地址信息
2012-06-05 /// <summary> /// 获取本地IP地址信息 /// </summary> void G ...
- 用angular实时获取本地localStorage数据,实现一个模拟后台数据登入的效果
研究了一上午,终于做出了,实时获取本地localStorage来模拟注册登入~~~ <!DOCTYPE html><html><head lang="en&qu ...
- Android之获取本地图片并压缩方法
这两天在做项目时,做到上传图片功能一块时,碰到两个问题,一个是如何获取所选图片的路径,一个是如何压缩图片,在查了一些资料和看了别人写的后总算折腾出来了,在此记录一下. 首先既然要选择图片,我们就先要获 ...
随机推荐
- Oracle 11g安装时针对不同操作系统所需的依赖包查询地址
http://docs.oracle.com/cd/E11882_01/install.112/e24326/ 点击连接,出现页面,往下滑动:)
- 在Ubuntu 16.04上利用Jexus+.Net Core+Linux版SQL Server部署ZKEACMS.Core
百度传课视频地址:https://chuanke.baidu.com/v5849090-223278-1498090.html 一.SQL Server on Linux的安装 官方文档:https: ...
- Git for Windows之基础环境搭建与基础操作
一.安装Git工具 下载地址:Git For Windows 下载完后,安装,全程Next. 二.全局配置 1.配置本地用户名,用于提交代码 2.配置邮箱 三.创建本地Git项目仓库 1.建立代码仓库 ...
- 【Canal源码分析】配置项
本文讲解canal中的一些配置含义. 一.配置加载图 二.配置文件canal.properties 2.1 common参数定义 比如可以将instance.properties的公用参数,抽取放置到 ...
- Go常量与运算符
常量的定义 常量的值在编译时就已经确定 常量的定义格式与变量基本相同 等号右侧必须是常量或者常量表达式 常量表达式中的函数必须是内置函数 package main import ( "fmt ...
- CRM项目完成实现
这两天我们都在写项目的接口与实现的! 今天主要是遇到的问题: 当我在添加新的班级的时候,班级的老师 我需要去判断这个老师是否存在,如果不存在的话,我就要把这个老师添加到学校的员工表中! 我会先判断这个 ...
- 第一次项目上Linux服务器(七:——Tomcat+ngnix+域名的简单配置)
1.准备工作 安装好jdk+tomcat+nginx相关配置,请参考历史博客 2.修改Nginx配置文件 修改配置文件如下: 3.修改Tomcat配置文件 修改配置文件如下: 配置文件连接: ngni ...
- ArcGIS紧凑型切片读取与应用2-webgis动态加载紧凑型切片(附源码)
1.前言 上篇主要讲了一下紧凑型切片的的解析逻辑,这一篇主要讲一下使用openlayers动态加载紧凑型切片的web地图服务. 2.代码实现 上篇已经可以通过切片的x.y.z得对应的切片图片,现在使用 ...
- Navicat备份、还原mysql数据库
注:本文为原创,转载请附带链接:https://www.cnblogs.com/stm32stm32
- POJ 2419 Forests(模拟)
题目链接: https://cn.vjudge.net/problem/POJ-2419 题目描述: If a tree falls in the forest, and there's nobody ...