您的位置:首页 > 职场人生

Android面试题(29)-surfaceView与TextureView

2018-03-08 16:50 435 查看

SurfaceView:

模板代码:
public class SurfaceViewText extends SurfaceView implements SurfaceHolder.Callback,Runnable{
private SurfaceHolder surfaceHolder;
private boolean isDrawing;

public SurfaceViewText(Context context) {
super(context);
//初始化
init();
}

private void init() {
surfaceHolder = getHolder();//获取Surface管理对象,SurfaceHolder
surfaceHolder.addCallback(this);//注册SurfaceHolder
setFocusable(true);//设置SurfaceView可获取焦点
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);//保持屏幕常亮
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing =true;
new Thread(this).start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isDrawing =false;
}

@Override
public void run() {
while (isDrawing){
drawUI();
}
}

private void drawUI() {
Canvas canvas=surfaceHolder.lockCanvas();
try {
drawView(canvas);
}catch (Exception e){
e.printStackTrace();
}finally {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}

private void drawView(Canvas canvas) {
//画一些东西

}
}
例子:触摸板
public class SurfaceViewText extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder surfaceHolder;
private boolean isDrawing;
private Canvas canvas;
private Paint paint;
private Path path;

private float mLastX, mLastY;//上次的坐标
public static final int TIME_IN_FRAME = 30;

public SurfaceViewText(Context context) {
super(context);
//初始化
init();
}

private void init() {
surfaceHolder = getHolder();//获取Surface管理对象,SurfaceHolder
surfaceHolder.addCallback(this);//注册SurfaceHolder
setFocusable(true);//设置SurfaceView可获取焦点
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);//保持屏幕常亮

//设置画笔
paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
paint.setColor(Color.RED);
paint.setStrokeWidth(10f);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);//设置线段连接处样式,圆弧
paint.setStrokeCap(Paint.Cap.ROUND);//画笔笔刷类型,影响画
10ab8
笔始末端
//初始化路径
path = new Path();
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing = true;
new Thread(this).start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isDrawing = false;
}

@Override
public void run() {
while (isDrawing) {
/**取得更新之前的时间**/
long startTime = System.currentTimeMillis();
synchronized (surfaceHolder){
drawUI();
}
/**取得更新结束的时间**/
long endTime = System.currentTimeMillis();

/**计算出一次更新的毫秒数**/
int diffTime = (int)(endTime - startTime);

/**确保每次更新时间为30帧**/
while(diffTime <=TIME_IN_FRAME) {
diffTime = (int)(System.currentTimeMillis() - startTime);
/**线程等待**/
Thread.yield();
}
}
}

private void drawUI() {
try {
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
canvas.drawPath(path, paint);
} catch (Exception e) {
e.printStackTrace();
} finally {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
float xStart = event.getRawX();
float yStart = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = xStart;
mLastY = yStart;
path.moveTo(mLastX, mLastY);
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(xStart - mLastX);
float dy = Math.abs(yStart - mLastY);
if (dx >= 3 || dy >= 3) {
path.quadTo(mLastX, mLastY, (mLastX + xStart) / 2, (mLastY + yStart) / 2);
}
mLastX = xStart;
mLastY = yStart;
break;
case MotionEvent.ACTION_UP:

break;
}
return true;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, 300);
} else if (wSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, hSpecSize);
} else if (hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(wSpecSize, 300);
}
}
}
SurfaceView中采用了双缓冲机制,保证了UI界面的流畅性,同时SurfaceView不在主线程中绘制,而是另开辟一个线程去绘制,所以它不妨碍UI线程;
SurfaceView继承于View,他和View主要有以下三点区别:
(1)View底层没有双缓冲机制,SurfaceView有;
(2)view主要适用于主动更新,而SurfaceView适用与被动的更新,如频繁的刷新
(3)view会在主线程中去更新UI,而SurfaceView则在子线程中刷新;

SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()

TextsureView:

TextureView 适用于 Android 4.0 和之后的版本,在很多的情况下可以顺便作为 SurfaceView 的替代品来使用。TextureView 的行为更像传统的 View,可以对绘制在它上面的内容实现动画和变换。但要求运行它的环境是硬件加速的,这可能会导致某些应用程序的兼容性问题。应用程序在 SDK 为 11或以上的版本时,默认启动了硬件加速。(如果需要禁用硬件加速可在 AndroidManifest.xml 文件中的 <activity> 或整个 <application> 标签中添加 android:hardwareAccelerated="false",即可。)
例子:利用TextureView播放视频
public class TextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {

private MediaPlayer mediaPlayer;

@Bind(R.id.texture)
TextureView textureView;
@Bind(R.id.video_image)
ImageView video_image;
private Surface surface;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_surface);
ButterKnife.bind(this);
textureView.setSurfaceTextureListener(this);//设置监听,实现四个方法
}

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
System.out.println("onSurfaceTextureAvailable被执行");
surface = new Surface(surfaceTexture);
new PlayerVideo().start();
}

@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
System.out.println("onSurfaceTextureSizeChanged被执行");
}

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
System.out.println("onSurfaceTextureDestroyed被执行");
surfaceTexture=null;
surface=null;
mediaPlayer.stop();
mediaPlayer.release();
return true;
}

@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {

}

class PlayerVideo extends Thread{
@Override
public void run() {
MPermissionUtils.requestPermissionsResult(TextureViewActivity.this, 1, new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, new MPermissionUtils.OnPermissionListener() {
@Override
public void onPermissionGranted() {
File file=new File(Environment.getExternalStorageDirectory()+"/tv.mp4");
if (!file.exists()){
copyFile();
}
mediaPlayer=new MediaPlayer();
try {
mediaPlayer.setDataSource(file.getAbsolutePath());
mediaPlayer.setSurface(surface);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
video_image.setVisibility(View.GONE);
mediaPlayer.start();
}
});
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onPermissionDenied() {
MPermissionUtils.showTipsDialog(TextureViewActivity.this);
}
});

}
}

/**
* 如果sdcard没有文件就复制过去
*/
private void copyFile() {
AssetManager assetManager = this.getAssets();
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open("tv.mp4");
String newFileName = Environment.getExternalStorageDirectory()+"/tv.mp4";
out = new FileOutputStream(newFileName);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
MPermissionUtils.onRequestPermissionsResult(requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}首先,TextureView和SurfaceView都是继承自View类的,但是TextureView在Andriod4.0之后的API中才能使用。SurfaceView可以通过SurfaceHolder.addCallback方法在子线程中更新UI,TextureView则可以通过TextureView.setSurfaceTextureListener在子线程中更新UI,个人认为能够在子线程中更新UI是上述两种View相比于View的最大优势。
    但是,两者更新画面的方式也有些不同,由于SurfaceView的双缓冲功能,可以是画面更加流畅的运行,但是由于其holder的存在导致画面更新会存在间隔,并且,由于holder的存在,SurfaceView也不能进行像View一样的setAlpha和setRotation方法,但是对于一些类似于坦克大战等需要不断告诉更新画布的游戏来说,SurfaceView绝对是极好的选择。但是比如视频播放器或相机应用的开发,TextureView则更加适合。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息