1、简介
应用程序的视频或者opengl内容往往是显示在一个特别的UI控件中:SurfaceView。SurfaceView的工作方式是创建一个置于应用窗口之后的新窗口。这种方式的效率非常高,因为SurfaceView窗口刷新的时候不需要重绘应用程序的窗口(android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率非常低下,不过满足普通应用界面的需求还是绰绰有余),但是SurfaceView也有一些非常不便的限制。
因为SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()。
为了解决这个问题 Android 4.0中引入了TextureView。它们都可以在另一个独立线程中绘制和渲染,这是和其它View的最大不同。
与SurfaceView相比,TextureView并没有创建一个单独的Surface(表面)用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。另外,Textureview必须在硬件加速开启的窗口中。
2、使用
TextureView的使用非常简单,我们要做的就是获取用于渲染内容的SurfaceTexture(它能捕获一个图像流的一帧来作为OpenGL 的texture也就是纹理。这个图片流主要是来自相机的预览或视频的解码。)。具体做法是先创建TextureView对象,然后实现SurfaceTextureListener接口,代码如下:
public class MainActivity extends AppCompatActivity {
private TextureView mTexture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTexture = (TextureView) findViewById(R.id.textureView);
mTexture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
}
这里我用得是匿名内部类,也可以像点击事件这样的回调一样去实现SurfaceTextureListener的接口。
- onSurfaceTextureAvailable():在SurfaceTexture准备使用时调用。
- onSurfaceTextureDestroyed():当指定SurfaceTexture即将被销毁时调用。如果返回true,则调用此方法后,表面纹理中不会发生渲染。如果返回false,则客户端需要调用release()。大多数应用程序应该返回true。
- onSurfaceTextureSizeChanged():当SurfaceTexture缓冲区大小更改时调用。
- onSurfaceTextureUpdated():当指定SurfaceTexture的更新时调用updateTexImage()。
我们这里用MediaPlayer + TextureView来实现播放视频这个例子,来看看怎么使用:
public class MainActivity extends AppCompatActivity
implements TextureView.SurfaceTextureListener, MediaPlayer.OnPreparedListener{
/**本地视频的路径*/
public String videoPath = Environment.getExternalStorageDirectory().getPath() + "/ht.mp4";
private TextureView textureView;
private MediaPlayer mediaPlayer;
private SurfaceTexture mTexture;
private SurfaceHolder holder;
private Surface surface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureView = (TextureView) findViewById(R.id.textureView);
//注册一个SurfaceTexture,用于监听SurfaceTexure
textureView.setSurfaceTextureListener(this);
}
/**
* 播放视频的入口,当SurfaceTexure可得到时被调用
*/
private void playVideo() {
if (mediaPlayer == null) {
mTexture = textureView.getSurfaceTexture();
surface = new Surface(mTexture);
initMediaPlayer();
}
}
private void initMediaPlayer() {
this.mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(videoPath);
mediaPlayer.setSurface(surface);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setLooping(true);
} catch (IllegalArgumentException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalStateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
@Override
public void onPrepared(MediaPlayer mp) {
try {
if (mp != null) {
mp.start(); //视频开始播放
}
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
super.onResume();
if (textureView.isAvailable()) {
playVideo();
}
}
@Override
protected void onPause() {
super.onPause();
if (mTexture != null) {
mTexture.release(); //停止视频的绘制线程
}
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer =null;
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
playVideo();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
配置MediaPlayer也没什么好讲的,关于Surface的内容大家可以看看这里。
TextureView的各种动画操作也能给我们带来不一样的效果,我们在代码中为它设置旋转:
textureView.setRotation(45.0f);
还有setAnimation()方法设置我们的动画,组合起来也会让我们的TextureView令人耳目一新。
3、与SurfaceView
既然TextureView与SurfaceView一样都可以在其它线程中进行UI更新,并且还有SurfaceView做不到的移动,透明度变化等设置,那么是不是就可以让TextureView代替SurfaceView呢。
这就要先讲讲这个两个View:
SurfaceView提供一个嵌入在视图层次上的专用绘制表面,我们可以控制该表面(Surface)的格式和尺寸。SurfaceView负责将表面放置在屏幕上正确的位置。它的行为多少有些类似于传统桌面系统上的onscreen窗口,比如,X11系统中的XWindow可以是无边框的,嵌入在另一个XWindow中。
SurfaceView存在如下两个缺点:
- 不能应用动画、变换和缩放
- 不能叠加(Overlay)两个SurfaceView
TextureView看似更像一个通用的View,可以应用动画、变换和缩放,就如同TextView。TextureView只能用在硬件加速的窗口。但是,TextureView比SurfaceView更耗内存,而且可能会有1~3帧的延迟。
所以在使用的时候要斟酌两者的不同。
结束语:本文仅用来学习记录,参考查阅。