Android Framework 常见解决方案(16)android多VirtualDisplay交互方法

1 原理

该文主要介绍了 多个虚拟屏同时显示并可交互 的解决方案。主要解决多个 VirtualDisplay 虚拟屏同时显示的问题 和 输入交互的问题。

2 修改方案(Android Q)

2.1 多辅助显示

2.1.1 Setting中的辅助屏

分析系统级应用Settings中的选项:Simulate secondary displays 选项,点击这里可以看到有overlay的 辅助屏,一般来讲用这个来模拟 显示器用于调试。

进入到开发者模式->DRAWING->Simulate secondary displays。该点击事件的核心逻辑是:向Settings数据库写入value值,然后会触发 OverlayDisplayAdapter的操作:将Overlay显示设备添加到系统的OverlayDevice列表中 。接下来就可以自动创建 辅助屏了,操作方式直接进入adb的命令行,执行如下逻辑,如下所示:

#创建一块辅助屏显示器,如下所示:
$settings put global overlay_display_devices "1920x1080/320,secure"
#创建两块辅助屏显示器,如下所示:
$settings put global overlay_display_devices "1920x1080/320,secure;1920x1080/320,secure"
#创建三块辅助屏显示器也是类似,以此类推,如下所示:
$settings put global overlay_display_devices "1920x1080/320,secure;1920x1080/320,secure;1920x1080/320,secure"
#关闭辅助屏显示器 如下所示:
$settings put global overlay_display_devices “null”

创建出来的辅助屏 效果如下:

2.1.2 辅助屏上启动Activity

保证不同的辅助屏上启动不同应用Activity,代码如下所示:

ActivityOptions options = ActivityOptions.makeBasic();
//... 
//设置标识位,关键代码1 
options.setLaunchDisplayId(apps[index].mVirtualDisplay.getDisplay().getDisplayId());
Intent intent = new Intent();
//设置标识位,关键代码2
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
//这里根据需要设置Component
intent.setComponent(new ComponentName(getItem(position).activityInfo.packageName, getItem(position).activityInfo.name));

startActivity(intent, options.toBundle());

也就是在执行startActivity时候 设置option中的displayId即可。这样可以根据需要在不同的虚拟屏上 启动多个activity。

2.2 实现input事件到各辅助屏上的解决方案

这里实现了一个inputUtil的类,使用反射机制将输入事件分配给各个不同的displayId对应的虚拟屏幕上,参考代码如下所示(注意参考代码只是2个displayId对应的虚拟屏幕的展示,如果需要多个请看懂后 自行调整,难度不大,同时关于算法部分根据需求 将实际屏幕的坐标转换到 辅助屏幕上即可,该部分隐藏)

public class InputUtil {
    private static final String TAG = "InputUtil";

    public static void processEvent(@NonNull Context context, @NonNull MotionEvent event, @NonNull ImageView imageView0ne, @NonNull ImageView imageViewTwo, int displayId) {
        final float THRESHOLD = (float) 0.0001f;
        final float virtual_screen_width = 1920.0f;
        final float virtual_screen_height = 1080.0f;
        float map_x = 0.0f;
        float map_y = 0.0f;
        float event_x = event.getRawX();
        float event_y = event.getRawY();

        int[] positionV1 = new int[2];
        imageView0ne.getLocationOnScreen(positionV1);
        Rect rectV1 = new Rect();
        imageView0ne.getLocalVisibleRect(rectV1);
        //LogUtil.e(TAG, "getLocationOnScreen:" + positionV1[0] + "," + positionV1[1]+"H:"+rectV1.height()+"W:"+rectV1.width());

        int[] positionV2 = new int[2];
        imageViewTwo.getLocationOnScreen(positionV2);
        Rect rectV2 = new Rect();
        imageViewTwo.getLocalVisibleRect(rectV2);
        //LogUtil.e(TAG, "getLocationOnScreen:" + positionV2[0] + "," + positionV2[1]+"H:"+rectV2.height()+"W:"+rectV2.width());
        LogUtil.e(TAG,"StatusBar--H:"+getStatusBarHeight(context)+"W:"+getStatusBarWidth(context));
        //V1 virtual display view rect
        if((event_x > positionV1[0]) && (event_x  positionV1[1]) && (event_y  positionV2[0]) && (event_x  positionV2[1]) && (event_y  0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    private static int getStatusBarWidth(Context context){
        Resources resources = context.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    private static void inputManagerInjectMotionEventReflection(MotionEvent event) {
        try {
            Class ownerClass = Class.forName("android.hardware.input.InputManager");
            Field field = ownerClass.getField("INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH");
            Method[] methods = ownerClass.getDeclaredMethods();
            Method method = null;
            for(Method m:methods) {
                if (m.getName().equalsIgnoreCase("getInstance")) {
                    method = ownerClass.getMethod("getInstance");
                    break;
                }
            }
            InputManager inputManager = (InputManager)method.invoke(ownerClass);
            LogUtil.e(TAG,"method invoke:="+inputManager);
            for(Method m:methods) {
                if (m.getName().equalsIgnoreCase("injectInputEvent")) {
                    method = ownerClass.getMethod("injectInputEvent", android.view.InputEvent.class,int.class);
                    break;
                }
            }
            if(!method.isAccessible()){
                method.setAccessible(true);
            }
            method.invoke(inputManager,event,(Integer)field.get(ownerClass));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    private static void eventSetDisplayIdReflection(MotionEvent event,int displayId){
        try {
            Class ownerClass = Class.forName("android.view.MotionEvent");
            Method[] methods = ownerClass.getDeclaredMethods();
            Method method = null;
            for(Method m:methods) {
                if (m.getName().equalsIgnoreCase("setDisplayId")) {
                    method = ownerClass.getMethod("setDisplayId", int.class);
                    break;
                }
            }
            if(!method.isAccessible()){
                method.setAccessible(true);
            }
            Object a = method.invoke(event,displayId);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(framework,android,常见解决方案,android)