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);



    }
相关推荐
每次的天空9 分钟前
Android学习总结之算法篇五(字符串)
android·学习·算法
Gracker1 小时前
Android Weekly #202513
android
张拭心3 小时前
工作九年程序员的三月小结
android·前端
每次的天空3 小时前
Flutter学习总结之Android渲染对比
android·学习·flutter
鸿蒙布道师5 小时前
鸿蒙NEXT开发土司工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
智想天开6 小时前
11.使用依赖注入容器实现松耦合
android
yunteng5217 小时前
音视频(四)android编译
android·ffmpeg·音视频·x264·x265
tangweiguo030519877 小时前
(kotlin) Android 13 高版本 图片选择、显示与裁剪功能实现
android·开发语言·kotlin
匹马夕阳7 小时前
(一)前端程序员转安卓开发分析和规划建议
android·前端
Kika写代码7 小时前
【Android】UI开发:XML布局与Jetpack Compose的全面对比指南
android·xml·ui