Android开发中的Surface库及用其制作播放器UI的例子

1、Surface
1.1、 就如在C语言编程一样,通过一个文件的句柄,就可以操作文件,获取文件的内容。 同样的,通过Surface就可以获取raw buffer其中的内容。原生缓冲区(raw buffer)存储着当前窗口的像素数据。

1.2、事实上,当得到一个Surface对象时,同时会得到一个Canvas(画布)对象。这一点可以通过查看\frameworks\base\core\java\android\view\Surface.java文件可知道Surface类定义了一个Canvas成员变量

private int mSurfaceControl; private int mSaveCount; private Canvas mCanvas; private int mNativeSurface; private int mSurfaceGenerationId; private String mName;

1.3、 理解Canvas对象,可以把它当做画布,Canvas的方法大多数是设置画布的大小、形状、画布背景颜色等等,要想在画布上面画画,一般要与Paint对象结合使用,顾名思义,Paint就是画笔的风格,颜料的色彩之类的。

// 创建画笔 Paint paint = new Paint(); paint.setColor(Color.RED);// 设置红色 canvas.drawCircle(60, 20, 10, paint);// 画一个圆

1.4、Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。

1.5、Surface实现了Parcelable接口,(implements Parcelable),也就是说Surface对象可以把显示内容的数据写入到 Parcel 中,并且能够从Parcel读回数据。

2、SurfaceView
SurfaceView提供了一个专门用于绘制的surface,这个surface内嵌于。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface在屏幕的正确绘制位置。
surface是Z-ordered的(也就是说在xyz坐标系中,按照Z坐标排序的,Z值大的表面覆盖在Z值小的表面的上方),这表明它总在自己所在窗口的后面。surfaceview在显示窗口处为Surface提供了一个可见区域,通过这个区域,才能看到Surface里面的内容。可以放置一些覆盖图层(overlays)在Surface上面,如Button、Textview之类的。但是,需要注意的是,如果Surface上面有全透明的控件,那么随着Surface的每一次变化,这些全透明的控件就会重新渲染,这样的话,就影响性能与显示的效果。
你可以通过SurfaceHolder这个接口去访问Surface,而执行getHolder()方法可以得到SurfaceHolder接口。
当SurfaceView的窗口可见时,Surface就会被创建,当SurfaceView窗口隐藏时,Surface就会被销毁。当然了,你也可以通过复写surfaceCreated(SurfaceHolder) 和 surfaceDestroyed(SurfaceHolder)  这两个方法来验证一下Surface何时被创建与何时被销毁。
SurfaceView提供了一个运行在渲染线程的surface,若你要更新屏幕,你需要了解以下线程知识。
所有SurfaceView 和 SurfaceHolder.Callback的方法都应该在主线程(UI线程)里面调用,应该要确保渲染进程所访问变量的同步性。
你必须确保只有当Surface有效的时候,(也就是当Surface的生命周期在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之间)才能让渲染进程访问。

2.1、SurfaceView与Surface的联系
简单来说,SurfaceView与Surface的联系就是,Surface是管理显示内容的数据(implementsParcelable),包括存储于数据的交换。而SurfaceView就是把这些数据显示出来到屏幕上面。
两者联系如图所示:

3、SurfaceHolder
SurfaceHolder是控制surface的一个抽象接口,你可以通过SurfaceHolder来控制surface的尺寸和格式,或者修改surface的像素,监视surface的变化等等,SurfaceHolder是SurfaceView的典型接口。
与直接控制SurfaceView来修改surface不同,使用SurfaceHolder来修改surface时,需要注意lockCanvas() 和Callback.surfaceCreated().这两个方法。
SurfaceHolder控制surface的流程所使用的几个方法。

3.1、abstract void    addCallback(SurfaceHolder.Callback callback)
给SurfaceHolder一个回调对象。

3.2、abstract Canvas    lockCanvas(Rect dirty)
锁定画布中的某一个区域,返回的画布对象Canvas(当更新的内容只有一个区域时,同时要追求高效,可以只更
新一部分的区域,而不必更新全部画布区域)

3.3、abstract Canvas    lockCanvas()
锁定画布,返回的画布对象Canvas

3.4、abstract void    removeCallback(SurfaceHolder.Callback callback)
移除回调对象

3.5、abstract void    unlockCanvasAndPost(Canvas canvas)
结束锁定画图,并提交改变。

4、SurfaceHolder.Callback
SurfaceHolder.Callback是监听surface改变的一个接口

4.1、public abstract voidsurfaceChanged(SurfaceHolder holder, int format, int width, int height)
surface发生改变时被调用

4.2、public abstract voidsurfaceCreated(SurfaceHolder holder)
在surface创建时被调用,一般在这个方法里面开启渲染屏幕的线程。

4.3、public abstract voidsurfaceDestroyed(SurfaceHolder holder)
销毁时被调用,一般在这个方法里将渲染的线程停止。

附上上述所说几种的联系方法

SurfaceHolder = SurfaceView.getHolder(); Surface = SurfaceHolder.getSurface(); Canvas =SurfaceHolder.LockCanvas(Rect dirty) Canvas =Surface.lockCanvas(Rect dirty)

5、DEMO:通过SurfaceView以及SurfaceHolder进行视频播放
使用AudioView进行视频播放的时候,是不是很不爽,千篇一律的模式,恶心吧。这里,我们可以通过一些方式对MediaPlayer进行包装。而所用到的正是SurfaceView以及SurfaceHolder。
最终效果图:

我们提供了四个按钮,可以进行播放控制。

布局文件media.xml代码:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <SurfaceView android:id="@+id/surfaceView1" android:layout_width="320px" android:layout_height="160px"></SurfaceView> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageButton android:id="@+id/button_play" android:src="@drawable/play" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_pause" android:src="@drawable/pause" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_stop" android:src="@drawable/stop" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> <ImageButton android:id="@+id/button_reset" android:src="@drawable/reset" android:onClick="buttonClick" android:layout_width="wrap_content" android:layout_height="wrap_content"></ImageButton> </LinearLayout> </LinearLayout>

activity代码:

package cn.com.chenzheng_java.media; import android.app.Activity; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; /** * @description 通过SurfaceView/SurfaceHolder实现自己的播放器 * @author chenzheng_java * @since 2011/03/23 * @description 这里进行一下补充说明,我们可以通过mediaplayer添加OnPreparedListener * 以及OnCompletionListener等事件对准备好播放以及播放完成后的操作进行控制。 * 使用SurfaceView以及SurfaceHolder进行视频播放时,结构是这样的: * 1、首先,我们从布局文件中获取一个surfaceView * 2、通过surfaceView.getHolder()方法获取与该容器想对应的surfaceHolder * 3、对srufaceHolder进行一些默认的设置,如addCallback()和setType() * 4、通过mediaPlayer.setDisplay()方法将视频播放与播放容器链接起来 */ public class MyMediaPlayerActivity extends Activity { MediaPlayer mediaPlayer ; // 播放器的内部实现是通过MediaPlayer SurfaceView surfaceView ;// 装在视频的容器 SurfaceHolder surfaceHolder;// 控制surfaceView的属性(尺寸、格式等)对象 boolean isPause ; // 是否已经暂停了 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.media); surfaceView = (SurfaceView) findViewById(R.id.surfaceView1); /** * 获取与当前surfaceView相关联的那个的surefaceHolder */ surfaceHolder = surfaceView.getHolder(); /** * 注册当surfaceView创建、改变和销毁时应该执行的方法 */ surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("通知", "surfaceHolder被销毁了"); if(mediaPlayer!=null) mediaPlayer.release(); } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i("通知", "surfaceHolder被create了"); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i("通知", "surfaceHolder被改变了"); } }); /** * 这里必须设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS哦,意思 * 是创建一个push的'surface',主要的特点就是不进行缓冲 */ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } /*** * @param targetButton 被用户点击的按钮 */ public void buttonClick(View targetButton){ int buttonId = targetButton.getId(); switch (buttonId) { case R.id.button_play: play(); break; case R.id.button_pause: pause(); break; case R.id.button_reset: reset(); break; case R.id.button_stop: stop(); break; default: break; } } /** * 播放 */ private void play(){ mediaPlayer = new MediaPlayer(); // 设置多媒体流类型 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置用于展示mediaPlayer的容器 mediaPlayer.setDisplay(surfaceHolder); try { mediaPlayer.setDataSource("/data/jinsha.3gp"); mediaPlayer.prepare(); mediaPlayer.start(); isPause = false; } catch (Exception e) { Log.i("通知", "播放过程中出现了错误哦"); } } /** * 暂停 */ private void pause(){ Log.i("通知", "点击了暂停按钮"); if(isPause==false){ mediaPlayer.pause(); isPause=true; }else{ mediaPlayer.start(); isPause=false; } } /** * 重置 */ private void reset(){ Log.i("通知", "点击了reset按钮"); // 跳转到视频的最开始 mediaPlayer.seekTo(0); mediaPlayer.start(); } /** * 停止 */ private void stop(){ Log.i("通知", "点击了stop按钮"); mediaPlayer.stop(); mediaPlayer.release(); } }

时间: 2024-09-13 09:23:44

Android开发中的Surface库及用其制作播放器UI的例子的相关文章

Android开发中的Surface库及用其制作播放器UI的例子_Android

1.Surface1.1. 就如在C语言编程一样,通过一个文件的句柄,就可以操作文件,获取文件的内容. 同样的,通过Surface就可以获取raw buffer其中的内容.原生缓冲区(raw buffer)存储着当前窗口的像素数据. 1.2.事实上,当得到一个Surface对象时,同时会得到一个Canvas(画布)对象.这一点可以通过查看\frameworks\base\core\java\android\view\Surface.java文件可知道Surface类定义了一个Canvas成员变量

Android开发中常用的库总结(持续更新)

这篇文章用来收集Android开发中常用的库,都是实际使用过的.持续更新... 原文地址:http://www.cnblogs.com/JohnTsai/p/4162390.html 1.消息提示的小红点 微信,微博消息提示的小红点. 开源库地址:https://github.com/stefanjauker/BadgeView 使用示例: BadgeView badge = new BadgeView(getActivity()); badge.setTargetView(myView); b

Android开发中使用Volley库发送HTTP请求的实例教程_Android

Android Volley 是Google开发的一个网络lib,可以让你更加简单并且快速的访问网络数据.Volley库的网络请求都是异步的,你不必担心异步处理问题. Volley的优点: 请求队列和请求优先级 请求Cache和内存管理 扩展性性强 可以取消请求 下载和编译volley.jar需要安装git,ant,android sdk clone代码: git clone https://android.googlesource.com/platform/frameworks/volley

Android开发中使用Volley库发送HTTP请求的实例教程

Android Volley 是Google开发的一个网络lib,可以让你更加简单并且快速的访问网络数据.Volley库的网络请求都是异步的,你不必担心异步处理问题. Volley的优点: 请求队列和请求优先级 请求Cache和内存管理 扩展性性强 可以取消请求 下载和编译volley.jar 需要安装git,ant,android sdk clone代码: git clone https://android.googlesource.com/platform/frameworks/volley

可视化工具-Android开发一个本地题库,在录入题库信息时,怎么将图片信息导入到sqlite中。

问题描述 Android开发一个本地题库,在录入题库信息时,怎么将图片信息导入到sqlite中. 希望录入数据时通过可视化工具(sqlite expert)或者 excel 导入图片.. 实在是没什么思路....希望大家指点一下. 类似的效果 解决方案 小白不懂,还是要帮顶~~ 解决方案二: 虽然没真实往数据库里面存过图片,但还是不建议直接往数据库里面存图片,建议是把图片保存在网络上,这样,在启动APP或者安装之后,可以进行下载. 下载之后保存到本地,然后将路径存入本地数据库(同时将网络和本地路

Android开发中实现IOS风格底部选择器(支持时间 日期 自定义)_Android

本文Github代码链接 https://github.com/AndroidMsky/AndoirdIOSPicker 先上图吧: 这是笔者最近一个项目一直再用的一个选择器库,自己也在其中做了修改,并决定持续维护下去. 先看使用方法: 日期选择: private void showDateDialog(List<Integer> date) { DatePickerDialog.Builder builder = new DatePickerDialog.Builder(this); bui

RxJava入门指南及其在Android开发中的使用示例_Android

RxJava的GitHub主页,部署部分就没什么好说的了~ https://github.com/ReactiveX/RxJava 基础RxJava最核心的两个东西是Observables(被观察者,事件源)和Subscribers(观察者).Observables发出一系列事件,Subscribers处理这些事件.这里的事件可以是任何你感兴趣的东西(触摸事件,web接口调用返回的数据...) 一个Observable可以发出零个或者多个事件,知道结束或者出错.每发出一个事件,就会调用它的Sub

Android开发中GridView用法示例

本文实例讲述了Android开发中GridView用法.分享给大家供大家参考,具体如下: Android的GridView控件用于把一系列的空间组织成一个二维的网格显示出来,应用的比较多的就是组合图片显示.下面我就详细讲一个例子. 首先写一个类继承BaseAdapter 1. Java代码 package com.yarin.android.Examples_04_19; import android.content.Context; import android.view.View; impo

RxJava入门指南及其在Android开发中的使用示例

RxJava的GitHub主页,部署部分就没什么好说的了~ https://github.com/ReactiveX/RxJava 基础 RxJava最核心的两个东西是Observables(被观察者,事件源)和Subscribers(观察者).Observables发出一系列事件,Subscribers处理这些事件.这里的事件可以是任何你感兴趣的东西(触摸事件,web接口调用返回的数据...) 一个Observable可以发出零个或者多个事件,知道结束或者出错.每发出一个事件,就会调用它的Su