Android面试题(29)-surfaceView与TextureView

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则更加适合。

猜你喜欢

转载自blog.csdn.net/pgg_cold/article/details/79483731