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);//画笔笔刷类型,影响画笔始末端 //初始化路径 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则更加适合。