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



    }
相关推荐
天天爱吃肉821817 小时前
【比亚迪璇玑架构深度解析:重新定义智能电动汽车的“整车智能”】
数据库·人工智能·嵌入式硬件·架构·汽车
00后程序员张17 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
非凡ghost18 小时前
简朴App(PlainApp):开源、隐私保护的手机管理工具
学习·智能手机·生活·软件需求
柳岸风19 小时前
Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
android·ide·android studio
编程乐学19 小时前
安卓原创--基于 Android 开发的菜单管理系统
android
whatever who cares21 小时前
android中ViewModel 和 onSaveInstanceState 的最佳使用方法
android
毕设源码-钟学长21 小时前
【开题答辩全过程】以 Android的传统中医诊断管理系统为例,包含答辩的问题和答案
android
脚踏实地,坚持不懈!21 小时前
Android,Jetpack Compose,坦克大战游戏案例Demo
android·游戏
yzpyzp1 天前
kotlin的函数前面增加suspend关键字的作用
android·开发语言·kotlin
jiet_h1 天前
Android Kotlin ObjectAnimator 和 ValueAnimator 全面解析
android·开发语言·kotlin