Android智能座舱,视频播放场景,通过多指滑屏退回桌面,闪屏问题的另一种解法

一、背景

Android智能座舱,视频播放场景,通过多指滑屏退回桌面,会出现闪屏问题。

二、问题定位以及修改方案

通过录屏,然后一帧一帧播放的方式,发现闪屏是因为,多指的情况下,不仅触发了退回桌面的动作,还触发了调节视频app的亮度,以及视频的快进和快退的动作,几种UI刷新叠加起来,造成了闪屏的现象。

除了调整手势识别的方法解决闪屏的问题,还有一种方式,就是退出页面的时候截屏,卡住UI的刷新,也能解决闪屏的问题。

1. 步骤一

通过如下函数takeScreenshot,截取主屏的bitmap,但是通过SurfaceControl.captureDisplay获取到的bitmap对应的buffer是SurfaceControl.ScreenshotHardwareBuffer,属于hadware bitmaps,无法通过software rendering,会报如下错误:

Software rendering doesn't support hardware bitmaps

必须通过如下方式,将hardware bitmap转成soft bitmap:

Bitmap softwareBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, true);

java 复制代码
    private Bitmap takeScreenshot(int displayId) {
        if (displayId != 0) {
            return null;
        }
        // Take the screenshot
        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
        final SurfaceControl.DisplayCaptureArgs captureArgs =
                new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
                        .build();
        final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                SurfaceControl.captureDisplay(captureArgs);
        Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
        if (screenShot == null) {
            Log.e(TAG, "Failed to take fullscreen screenshot");
            return null;
        }

        // Optimization
        screenShot.setHasAlpha(false);
        Bitmap softwareBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, true);
        if (screenShot != null) {
            screenShot.recycle();
            screenShot = null;
        }

        return softwareBitmap;
    }

2.步骤二

通过函数createScreenshotLayer,将takeScreenshot 获取到的software bitmap,生成对应的SurfaceControl ,也就是底层的Layer数据。

java 复制代码
    private SurfaceControl createScreenshotLayer(Bitmap bitmap) {
        Log.w(TAG, "launchHomeFromFivePointerCatch: createScreenshotLayer bitmap : " + bitmap + ", w = " + bitmap.getWidth()
            + " ,h = " + bitmap.getHeight());
        // 创建 SurfaceControl 图层
        SurfaceControl surfaceControl = new SurfaceControl.Builder()
                .setName("ScreenshotLayer")
                .setBufferSize(bitmap.getWidth(), bitmap.getHeight())
                .setFormat(PixelFormat.TRANSLUCENT)
                .build();

        Surface surface = new Surface(surfaceControl);
        Canvas canvas = surface.lockCanvas(null);
        canvas.drawBitmap(bitmap, 0, 0, null);
        surface.unlockCanvasAndPost(canvas);

        return surfaceControl;
    }

3. 步骤三

通过playExitAnimation实现冻屏或者刷帧的动作,最后测试发现,冻屏比较适合,也相对简单。刷帧的方式,因为不能很好的掌握UI的调整和显示的时间,无法正确确定刷帧的起始和结束的时机。

java 复制代码
    private void playExitAnimation(SurfaceControl surfaceControl) {
        Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation surfaceControl : " + surfaceControl);
        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();

        方案1:冻屏
        /*
        // 设置动画属性
        /transaction.setMatrix(surfaceControl, 0.5f, 0, 0, 0.5f); // 缩放到 50%
        /transaction.setAlpha(surfaceControl, 0.0f); // 设置透明度为 0
        transaction.setLayer(surfaceControl, Integer.MAX_VALUE);
        transaction.show(surfaceControl);
        transaction.apply();

        // 延时销毁图层
        mHandler.postDelayed(() -> {
            transaction.remove(surfaceControl).apply();
        }, 500); // 动画持续 500ms
        */



        float startScale = 1.0f;
        float endScale = 0.5f;
        float startAlpha = 1.0f;
        float endAlpha = 0.0f;
        int duration = 500;
        long startTime = System.currentTimeMillis();

         方案2:通过handler 刷帧
        /*mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (!surfaceControl.isValid()) {
                    return;
                }
                long elapsedTime = System.currentTimeMillis() - startTime;
                if (elapsedTime > duration) return;

                float progress = (float) elapsedTime / duration;
                float currentScale = startScale + progress * (endScale - startScale);
                float currentAlpha = startAlpha + progress * (endAlpha - startAlpha);

                Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation progress : -----------" + progress
                    + ", currentScale = " + currentScale + ", currentAlpha = " + currentAlpha);

                //SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
                transaction.setMatrix(surfaceControl, currentScale, 0, 0, currentScale); // 设置缩放
                transaction.setAlpha(surfaceControl, currentAlpha); // 设置透明度
                transaction.setLayer(surfaceControl, Integer.MAX_VALUE);
                transaction.show(surfaceControl);
                transaction.apply();

                mHandler.post(this);
            }
        });*/

 方案3:通过Choreographer刷帧
        Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            long startTime = System.nanoTime();
            @Override
            public void doFrame(long frameTimeNanos) {
                if (!surfaceControl.isValid()) {
                    return;
                }
                float progress = (frameTimeNanos - startTime) / (float) (duration * 1_000_000); // 计算进度
                if (progress > 1.0f) return;
                float currentScale = startScale + progress * (endScale - startScale);
                float currentAlpha = startAlpha + progress * (endAlpha - startAlpha);

                Log.w(TAG, "launchHomeFromFivePointerCatch: playExitAnimation progress : -----------" + progress
                    + ", currentScale = " + currentScale + ", currentAlpha = " + currentAlpha);

                //transaction.setMatrix(surfaceControl, currentScale, 0, 0, currentScale); // 设置缩放
                transaction.setAlpha(surfaceControl, currentAlpha); // 设置透明度
                transaction.setLayer(surfaceControl, Integer.MAX_VALUE);
                transaction.show(surfaceControl);
                transaction.apply();

                Choreographer.getInstance().postFrameCallback(this); // 下一帧
            }
        });


        mHandler.postDelayed(() -> {
            //SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
            transaction.hide(surfaceControl); // 隐藏图层
            transaction.remove(surfaceControl); // 移除图层
            transaction.apply();
            surfaceControl.release(); // 释放资源
        }, duration);



    }
相关推荐
佛系小嘟嘟14 分钟前
Android Studio不显示需要的tag日志解决办法《All logs entries are hidden by the filter》
android·ide·android studio
mariokkm20 分钟前
Django一分钟:django中收集关联对象关联数据的方法
android·django·sqlite
长亭外的少年38 分钟前
如何查看 Android 项目的依赖结构树
android
深海呐3 小时前
Android 从本地选择视频,用APP播放或进行其他处理
android·音视频·从本地选择视频,用app播放·从本地选择视频,并拿到信息·跳转到本地视频列表
深海呐3 小时前
Android Google登录接入
android·google登录接入·android 谷歌登录接入·google登录·android google
daiyang123...3 小时前
MySQL【知识改变命运】11
android·数据库·mysql
8931519603 小时前
Android开发教程案例源码分享-匹配动画多个头像飘动效果
android·android开发·android教程·kotlin教程·android头像飘动动画·android匹配动画·android多个头像飘动动画
老码沉思录4 小时前
Android开发实战班 - 网络编程 - WebSocket 实时通信
android·网络·websocket
江上清风山间明月4 小时前
Android 14 screenrecord录制视频失败的原因分析
android·视频·大小·失败·录制·screenrecord·0kb
keeng20084 小时前
Compose学习记录(3): ViewModel数据驱动更新组件
android