1 TextureView 概述
SurfaceView和View的区别简单概述。 已经讲解了SurfaceView和View的区别,SurfaceView会有自己独立的Surface,虽然它被添加到了View 结构树中,但是它其实不在view 树结构中,不能像普通的View一样可以平移,缩放,旋转等操作。同时SurfaceView不能放在类似RecyclerView或ScrollView中,一些View中的特性也无法使用。
其实SurfaceView是可以应用平移,缩放,旋转等动画的,但是由于其内部在独立的Surface上进行绘制,所以对SurfaceView进行这些动画并不会达到我们要的效果。
注意:上面说了SurfaceView不支持平移,缩放,旋转等动画,但是当我们利用SurfaceView测试这些不支持的动画时,如果使用的是7.0 甚至更高版本的Android系统,会发现SurfaceView也支持平移,缩放的动画操作。
代码示例:
public class Main24Activity extends AppCompatActivity {
private SurfaceView mSurfaceView;
private MediaPlayer mMediaPlayer;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mProgressBar;
private Button btn1;
private Button btn2;
private Button btn3;
private Button btn4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main24);
mSurfaceView = findViewById(R.id.surfaceView);
mProgressBar = findViewById(R.id.progressBar);
btn1 = findViewById(R.id.btn1);
btn2 = findViewById(R.id.btn2);
btn3 = findViewById(R.id.btn3);
btn4 = findViewById(R.id.btn4);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//属性动画
mSurfaceView.animate().scaleX(0.5f).scaleY(0.5f);
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSurfaceView.animate().translationXBy(-10).translationYBy(-10);
}
});
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSurfaceView.animate().rotationBy(10);
}
});
btn4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSurfaceView.animate().alphaBy(0.1f);
}
});
/**
* http://movie.ks.js.cn/flv/other/1_0.flv
*/
String uri = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";
mMediaPlayer = new MediaPlayer();
try {
// mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(uri);
// mMediaPlayer.setDataSource(this, Uri.parse(uri));
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SHCallBack());
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
System.out.println("===========video=================" + width + " " + height + " " + mp.getVideoWidth() + " " + mp.getVideoHeight());
//尺寸动态变化
if (mp != null && width > 0 && height > 0 && mp.getVideoHeight() > 0 && mp.getVideoWidth() > 0) {
int videoWidth = mp.getVideoWidth();
int videoHeight = mp.getVideoHeight();
WindowManager wm = (WindowManager) Main24Activity.this.getSystemService(Context.WINDOW_SERVICE);
int screenwidth = wm.getDefaultDisplay().getWidth();
float size = (videoHeight * 1.0f) / (videoWidth * 1.0f);
int surfaceHeight = (int) (screenwidth *size);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mSurfaceView.getLayoutParams();
// layoutParams.width = width;
layoutParams.height = surfaceHeight;
mSurfaceView.setLayoutParams(layoutParams);
}
}
});
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mProgressBar.setVisibility(View.INVISIBLE);
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
private class SHCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
mMediaPlayer.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="100dp" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<LinearLayout
android:id="@+id/btncontainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="bottom|left" >
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"/>
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn3"/>
<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn4"/>
</LinearLayout>
</FrameLayout>
android 5.0上的效果,播放的flv是链接
在android 7.0上的表现:播放的MP4视频,支持平移缩放
当放入ScrollView,RecycleView等ViewGroup中时无法显示
<ScrollView
android:layout_width="match_parent"
android:layout_height="100dp">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="100dp" />
</ScrollView>
2 TextureView
TextureView在api14提供,已经有了VideoView和SurfaceView为什么还需要TextureView呢,由于VideoView是继承SurfaceView而来,所以SurfaceView有的那些缺点VideoView也有,开发中SurfaceView最主要的限制就是不能像普通view一样平移,旋转,缩放等动画操作,要想缩放大小只能改变SurfaceView的大小,视频会一直铺满SurfaceView。TextureView没有自己独立的Surface所以可以克服SUrfaceView的缺点。
TextureView能展示一个内容流,这个内容流可以是一个video或者一个OpenGL场景。TextureView必须在硬件加速的窗口中使用,如果在软件渲染模式下则什么都不显示。
不像SurfaceView,TextureVIew更像一个普通view,它没有自己的独立Surface,所以上面也说了TextureView可以进行平移,旋转,缩放等动画操作。
TextureView内部利用SurfaceTexture渲染内容,TextureView创建完成不代表可以使用,需要内部的SurfaceTexture准备就绪也就是SurfaceTexture要等TextureView attached to a window后才能开始工作。可以通过给TextureView设置SurfaceTextuListener监听器来监听准备就绪的回调。
注意只有一个生产者能够使用TextureView,例如当你利用TextureView展示Camera预览图像时,你不能使用lockCanvas()在TextureView上绘制内容。
SurfaceTexture作为数据通道,把从数据源(MediaPlayer)中获取到的图像帧数据转为GL外部纹理,交给TextureVeiw作为View heirachy中的一个硬件加速层来显示,从而实现视频播放功能。
TextureView的优缺点:
优点:支持移动、旋转、缩放等动画,支持截图
缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程。
SurfaceTextureListener 接口
TextureView.SurfaceTextureListener:
- onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
TextureView的 SurfaceTexture准备完成 - onSurfaceTextureDestroyed(SurfaceTexture surface)
SurfaceTexture将被销毁时调用 - onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
SurfaceTexture 缓存的尺寸改变时调用 - onSurfaceTextureUpdated(SurfaceTexture surface)
SurfaceTexture利用SurfaceTexture.updateTexImage()更新时调用
代码示例:
public class Main25Activity extends AppCompatActivity {
private TextureView mTextureView;
private MediaPlayer mMediaPlayer;
private Surface mSurface;
private ProgressBar mProgressBar;
private Button btn1;
private Button btn2;
private Button btn3;
private Button btn4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main25);
mTextureView = findViewById(R.id.surfaceView);
mProgressBar = findViewById(R.id.progressBar);
btn1 = findViewById(R.id.btn1);
btn2 = findViewById(R.id.btn2);
btn3 = findViewById(R.id.btn3);
btn4 = findViewById(R.id.btn4);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//属性动画
mTextureView.animate().scaleX(0.5f).scaleY(0.5f);
}
});
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTextureView.animate().translationXBy(10).translationYBy(10);
}
});
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTextureView.animate().rotationBy(10);
}
});
btn4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTextureView.animate().alphaBy(0.1f);
}
});
mMediaPlayer = new MediaPlayer();
try {
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
//TextureView,5.0以前在主线程渲染,在5.0以后有独立的渲染线程,所以5.0的系统需要自己开启线程。
//必须在onSurfaceTextureAvailable回调后才能开启线程。
new Thread(new MediaThread()).start();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private class MediaThread implements Runnable{
@Override
public void run() {
try {
/**
* rtmp://live.hkstv.hk.lxdns.com/live/hks
rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov
mms://space.hngd.gov.cn/live1
http://movie.ks.js.cn/flv/other/1_0.flv
http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8
*/
String uri = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";
mMediaPlayer.setSurface(mSurface);
mMediaPlayer.setDataSource(uri);
// mMediaPlayer.setDataSource(this, Uri.parse(uri));
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
System.out.println("===========video=================" + width + " " + height + " " + mp.getVideoWidth() + " " + mp.getVideoHeight());
//尺寸动态变化
if (mp != null && width > 0 && height > 0 && mp.getVideoHeight() > 0 && mp.getVideoWidth() > 0) {
int videoWidth = mp.getVideoWidth();
int videoHeight = mp.getVideoHeight();
WindowManager wm = (WindowManager) Main25Activity.this.getSystemService(Context.WINDOW_SERVICE);
int screenwidth = wm.getDefaultDisplay().getWidth();
float size = (videoHeight * 1.0f) / (videoWidth * 1.0f);
int surfaceHeight = (int) (screenwidth *size);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mTextureView.getLayoutParams();
// layoutParams.width = width;
layoutParams.height = surfaceHeight;
mTextureView.setLayoutParams(layoutParams);
}
}
});
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mProgressBar.setVisibility(View.INVISIBLE);
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="100dp" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<LinearLayout
android:id="@+id/btncontainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="bottom|left" >
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"/>
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn3"/>
<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn4"/>
</LinearLayout>
</FrameLayout>
硬件加速
<activity
android:name=".Main25Activity"
android:screenOrientation="portrait"
android:hardwareAccelerated="true">
</activity>
其他说明:(参考其他文章)
-
android 7.0上的surfaceview的性能比TextureView更有优势,支持平移、缩放操作。
-
TextureView增加了额外1~3帧的延迟显示画面更新
-
TextureView总是使用GL合成,而SurfaceView可以使用硬件overlay后端,占用更少的内存。
-
TextureView的内部缓冲队列导致比SurfaceView使用更多的内存