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



    }
相关推荐
私人珍藏库4 小时前
【Android】聆听岛[特殊字符]聚合全网音乐[特殊字符]免费听歌下载神器[特殊字符] 聚合音乐平台|无损母带下载|歌词封面同步|免费无广告听歌工具
android·人工智能·工具·软件·多功能
YF02115 小时前
Android触摸机制与自定义 View 实战
android·app
Dabei5 小时前
Android TV 焦点处理详解:遥控器与空鼠
android·前端
悠哉清闲6 小时前
裁剪SurfaceView
android
常利兵6 小时前
Android字体字重设置全攻略:XML黑科技+Kotlin动态实现,告别.ttf臃肿
android·xml·科技
therese_100866 小时前
安卓-IPC
android
沙粒07 小时前
Mac 使用 scrcpy 局域网无线投屏指南
android
过期动态7 小时前
MySQL中的约束
android·java·数据库·spring boot·mysql
牛蛙点点申请出战8 小时前
IconFontViewer -- 一个可以在 Android Studio 中实时预览 IconFont 的插件
android·前端·intellij idea
努力努力再努力wz9 小时前
【MySQL 进阶系列】拒绝滥用root:从 mysql.user 到权限校验,带你彻底理解用户管理与授权机制!
android·c语言·开发语言·数据结构·数据库·c++·mysql