Android T多屏多显——应用双屏间拖拽移动功能(更新中)

功能以及显示效果简介

需求:在双屏显示中,把启动的应用从其中一个屏幕中移动到另一个屏幕中。
操作:通过双指按压应用使其移动,如果移动的距离过小,我们就不移动到另一屏幕,否则移动到另一屏。
请添加图片描述

功能分析

多屏中移动应用至另一屏本质就是Task的移动。
从窗口层级结构的角度来说,就是把Display1中的DefaultTaskDisplayArea上的Task,移动到Display2中的DefaultTaskDisplayArea上。
容器结构简化树状图如下所示:
在这里插入图片描述

窗口层级结构简化树状图如下所示:
在这里插入图片描述

关键代码知识点

移动Task至另一屏幕

代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    /*** Move root task with all its existing content to specified display.** @param rootTaskId Id of root task to move.* @param displayId  Id of display to move root task to.* @param onTop      Indicates whether container should be place on top or on bottom.*/void moveRootTaskToDisplay(int rootTaskId, int displayId, boolean onTop) {//根据displayId获取DisplayContentfinal DisplayContent displayContent = getDisplayContentOrCreate(displayId);if (displayContent == null) {throw new IllegalArgumentException("moveRootTaskToDisplay: Unknown displayId="+ displayId);}//调用moveRootTaskToTaskDisplayArea方法moveRootTaskToTaskDisplayArea(rootTaskId, displayContent.getDefaultTaskDisplayArea(),onTop);}

入参说明:
rootTaskId需要移动的Task的Id。可以通过Task中getRootTaskId()方法获取。
displayId需要移动到对应屏幕的Display的Id。可以通过DisplayContent中的getDisplayId()方法获取。
onTop移动后的Task是放在容器顶部还是底部。true表示顶部,false表示底部。
代码解释:
这个方法首先通过getDisplayContentOrCreate方法根据displayId获取DisplayContent,然后调用moveRootTaskToTaskDisplayArea方法进行移动。
其中传递参数displayContent.getDefaultTaskDisplayArea(),表示获取DisplayContent下面的DefaultTaskDisplayArea。

    /*** Move root task with all its existing content to specified task display area.** @param rootTaskId      Id of root task to move.* @param taskDisplayArea The task display area to move root task to.* @param onTop           Indicates whether container should be place on top or on bottom.*/void moveRootTaskToTaskDisplayArea(int rootTaskId, TaskDisplayArea taskDisplayArea,boolean onTop) {//获取Taskfinal Task rootTask = getRootTask(rootTaskId);if (rootTask == null) {throw new IllegalArgumentException("moveRootTaskToTaskDisplayArea: Unknown rootTaskId="+ rootTaskId);}final TaskDisplayArea currentTaskDisplayArea = rootTask.getDisplayArea();if (currentTaskDisplayArea == null) {throw new IllegalStateException("moveRootTaskToTaskDisplayArea: rootTask=" + rootTask+ " is not attached to any task display area.");}if (taskDisplayArea == null) {throw new IllegalArgumentException("moveRootTaskToTaskDisplayArea: Unknown taskDisplayArea=" + taskDisplayArea);}if (currentTaskDisplayArea == taskDisplayArea) {throw new IllegalArgumentException("Trying to move rootTask=" + rootTask+ " to its current taskDisplayArea=" + taskDisplayArea);}//把获取到的task重新挂载到了新display的taskDisplayArearootTask.reparent(taskDisplayArea, onTop);// Resume focusable root task after reparenting to another display area.//窗口或任务reparent之后,恢复焦点,激活相关任务的活动,并更新活动的可见性,以确保窗口管理器和用户界面的状态一致和正确。rootTask.resumeNextFocusAfterReparent();// TODO(multi-display): resize rootTasks properly if moved from split-screen.}

根据前面传递的TaskId获取到Task,在通过rootTask.reparent(taskDisplayArea, onTop);方法,把这个Task重新挂载到了新display的taskDisplayArea上。然后使用rootTask.resumeNextFocusAfterReparent();方法更新窗口焦点显示。

  • rootTask.reparent(taskDisplayArea, onTop);
    代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

     void reparent(TaskDisplayArea newParent, boolean onTop) {if (newParent == null) {throw new IllegalArgumentException("Task can't reparent to null " + this);}if (getParent() == newParent) {throw new IllegalArgumentException("Task=" + this + " already child of " + newParent);}//通过调用 canBeLaunchedOnDisplay 方法检查任务是否可以在新父区域所在的显示设备上启动。if (canBeLaunchedOnDisplay(newParent.getDisplayId())) {//实际执行reparent的操作。reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);//如果Task是一个叶子Task(即没有子Task的Task)if (isLeafTask()) {//调用新父区域的 onLeafTaskMoved 方法来通知新父区域叶子Task已经移动。newParent.onLeafTaskMoved(this, onTop);}} else {Slog.w(TAG, "Task=" + this + " can't reparent to " + newParent);}}
    

    其中reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);实际执行reparent的操作。这里根据 onTop 的值来决定任务应该被放置在新父区域的顶部还是底部。我们再看看这方法的具体实现。
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    void reparent(WindowContainer newParent, int position) {if (newParent == null) {throw new IllegalArgumentException("reparent: can't reparent to null " + this);}if (newParent == this) {throw new IllegalArgumentException("Can not reparent to itself " + this);}final WindowContainer oldParent = mParent;if (mParent == newParent) {throw new IllegalArgumentException("WC=" + this + " already child of " + mParent);}// Collect before removing child from old parent, because the old parent may be removed if// this is the last child in it.//记录reparent的容器(this)相关信息,这里的this指的是移动的Task,newParent是新的TaskDisplayAreamTransitionController.collectReparentChange(this, newParent);// The display object before reparenting as that might lead to old parent getting removed// from the display if it no longer has any child.//获取之前的DisplayContent和新的DisplayContentfinal DisplayContent prevDc = oldParent.getDisplayContent();final DisplayContent dc = newParent.getDisplayContent();//设置 mReparenting 为 true,表示正在执行reparent操作。//然后从旧父容器中移除当前容器,并将其添加到新父容器的指定位置。//最后,将 mReparenting 设置为 false,表示reparent操作完成。mReparenting = true;oldParent.removeChild(this);newParent.addChild(this, position);mReparenting = false;// Relayout display(s)//标记新父容器对应的显示内容为需要布局。//如果新父容器和旧父容器的显示内容不同,//则触发显示内容改变的通知,并标记旧显示内容也需要布局。//最后,调用layoutAndAssignWindowLayersIfNeeded方法确保显示内容按需进行布局和窗口层级的分配。dc.setLayoutNeeded();if (prevDc != dc) {onDisplayChanged(dc);prevDc.setLayoutNeeded();}getDisplayContent().layoutAndAssignWindowLayersIfNeeded();// Send onParentChanged notification here is we disabled sending it in setParent for// reparenting case.//处理窗口容器在父容器变更时的各种逻辑onParentChanged(newParent, oldParent);//处理窗口容器在不同父容器之间同步迁移的逻辑onSyncReparent(oldParent, newParent);}
    
  • rootTask.resumeNextFocusAfterReparent();
    代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

        void resumeNextFocusAfterReparent() {//调整焦点adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,true /* moveDisplayToTop */);//恢复当前焦点任务的顶部活动mRootWindowContainer.resumeFocusedTasksTopActivities();// Update visibility of activities before notifying WM. This way it won't try to resize// windows that are no longer visible.//更新activities的可见性mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,!PRESERVE_WINDOWS);}
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/819648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

振弦式渗压计的安装与防护:在水工建筑物中的关键应用

振弦式渗压计,作为一种高效的孔隙水压力或液体液位测量工具,广泛应用于水工建筑物、基岩内、测压管、钻孔、堤坝、管道和压力容器内。其安装和防护工作至关重要,直接关系到测量数据的准确性和仪器的使用寿命。本文将重点探讨振弦式渗压计在填…

RabbitMQ实战教程(1)

RabbitMQ 一、RabbitMQ介绍 1.1 现存问题 服务调用:两个服务调用时,我们可以通过传统的HTTP方式,让服务A直接去调用服务B的接口,但是这种方式是同步的方式,虽然可以采用SpringBoot提供的Async注解实现异步调用&…

Ue不消耗輸入

1、介紹 我們都知道ue裏面使用輸入時,都是在PlayerController裏面進行獲取, 使用官方的操作映射,軸映射,以及目前最新的增强型輸入 但是我們發現了一個問題 那就是輸入會被消耗 就是儅我鼠標按在一個按鈕上時 你另一個地方接受…

Elastic安装后 postman对elasticsearch进行测试

一、创建索引和mapping //id 字段自增id //good_sn 商品SKU //good_name 商品名称 //good_introduction 商品简介 //good_descript 商品详情 PUT http://IP:9200/shop { "mappings":{ "good":{ "properties":{ …

LabVIEW光学探测器板级检测系统

LabVIEW光学探测器板级检测系统 特种车辆乘员舱的灭火抑爆系统广泛采用光学探测技术来探测火情。光学探测器作为系统的关键部件,其探测灵敏度、响应速度和准确性直接关系到整个系统的运行效率和安全性。然而,光学探测器在长期使用过程中可能会因为灰尘污…

牛客Linux高并发服务器开发学习第一天

Linux开发环境搭建 安装Xshell 7远程连接虚拟机的Ubuntu 安装Xftp 7可以传输文件(暂时还没使用) 安装VMware Tools可以直接从Windows系统向虚拟机Linux系统拖拽文件实现文件交互。 安装CScode可以远程连接Linux系统进行代码的编写。(Windows系统与Linxu系统公钥…

ActiveMQ 任意文件上传漏洞复现

一、使用弱口令登陆 ​ 访问 http://ip:8161/admin/ 进入admin登陆页面,使用弱口令登陆,账号密码皆为 admin,登陆成功后,headers中会出现验证信息 ​ 如: Authorization: Basic YWRtaW46YWRtaW4 # 二、利用PUT协议上…

STL体系结构与各容器基本介绍

STL体系结构与各容器基本介绍 STL体系结构基本容器序列式关联式&#xff08;查找更快&#xff09;其他&#xff08;不常用&#xff09;使用分配器 STL体系结构 六大模块 容器算法迭代器适配器仿函数分配器 基本容器 序列式 array c11新标准array<类型&#xff0c;大小&…

Linux-管道

目录 无名管道关闭未使用的管道文件描述符 管道对应的内存大小与shell命令进行通信&#xff08;popen&#xff09;命名管道FIFO创建FIFO文件打开FIFO文件 无名管道 管道是最早出现的进程间通信的手段。 管道的作用是在有亲缘关系的进程之间传递消息。所谓有亲缘关系&#xff…

MySQL中的SQL高级语句[二]

使用语言 MySQL 使用工具 Navicat Premium 16 代码能力快速提升小方法&#xff0c;看完代码自己敲一遍&#xff0c;十分有用 拖动表名到查询文件中就可以直接把名字拉进来以下是使用脚本方法&#xff0c;也可以直接进行修改中括号&#xff0c;就代表可写可不写 有些地方的代…

IO——标准IO

1.1概念 标准IO&#xff1a;是在C库中定义的一组专门用于输入输出的函数。 1.2特点 &#xff08;1&#xff09;通过缓冲机制减少系统调用&#xff0c;提高效率 &#xff08;2&#xff09;围绕流操作&#xff0c;用FILE*描述 &#xff08;3&#xff09;标准IO默认打开三个流&a…

PCIe错误定义与分类

前言&#xff1a; PCI总线中定义两个边带信号&#xff08;PERR#和SERR#&#xff09;来处理总线错误。其中PERR#主要对应的是普通数据奇偶校检错误&#xff08;Parity Error&#xff09;&#xff0c;而SERR#主要对应的是系统错误&#xff08;System Error&#xff09;。具体如下…

数据结构复习指导之绪论(算法的概念以及效率的度量)

文章目录 绪论&#xff1a; 2.算法和算法评价 知识总览 2.1算法的基本概念 知识点回顾与重要考点 2.2算法效率的度量 知识总览 1.时间复杂度 2.空间复杂度 知识点回顾与重要考点 归纳总结 绪论&#xff1a; 2.算法和算法评价 知识总览 2.1算法的基本概念 算法( Al…

【现代C++】模块的使用

C20引入了模块的概念&#xff0c;这是一个重要的新特性&#xff0c;旨在替代传统的预处理器和头文件机制。模块旨在提高编译速度、改善代码封装性、减少名称污染&#xff0c;并提供更好的工具支持。下面详细介绍模块的关键概念和使用方法&#xff1a; 1. 模块的基本概念 模块…

openGauss学习笔记-263 openGauss性能调优-TPCC性能调优测试指导-前置软件安装

文章目录 openGauss学习笔记-263 openGauss性能调优-TPCC性能调优测试指导-前置软件安装263.1 安装jdk263.2 安装numactl263.3 安装ant263.4 安装htop工具 openGauss学习笔记-263 openGauss性能调优-TPCC性能调优测试指导-前置软件安装 本章节主要介绍openGauss数据库内核基于…

谷歌浏览器的开发者插件vue-devtools

在这里我留下一个git地址用来下载插件包&#xff0c;首先在自己喜欢的位置创建一个新的文件夹&#xff0c;起一个自己喜欢的文件夹名字&#xff0c;下载到包后&#xff0c;然后点进文件夹里下载依赖&#xff0c;npm install,下载后如下面这个样子 git clone https://gitee.com…

【投稿优惠-EI稳定检索】2024年人工智能、自然语言处理与机器学习国际会议(ICAINLPML 2024)

2024 International Conference on Artificial Intelligence, Natural Language Processing and Machine Learning (ICAINLPML 2024) 网址&#xff1a;www.icainlpml.com 邮箱: ainlpmlsub-conf.com ●会议简介 2024年人工智能、自然语言处理与机器学习国际会议将邀请全球人…

Jackson 2.x 系列【24】Spring Web 集成

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. Spring Web3. Jackson2ObjectMapperBuilder4. Jackson2ObjectMapperFa…

比例控制器H5773282、H8135950、H3390627、H6079948

名称&#xff1a;BEUEC数字比例放大器、伺服比例控制器、伺服比例阀放大板&#xff0c;订货代号&#xff1a;H5773282、H8135950、H3390627、H6079948、H6108848、H6700353、H8851035、H1688388、H9549313、H3264103、H1182967&#xff0c;输入指令可选10V、4-20mA&#xff0c;…

Session缓存、Hibernate处理对象的状态了解

Session接口 Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口&#xff0c;它提供了基本的保存&#xff0c;更新&#xff0c;删除和查询的方法。 Session是有一个缓存, 又叫Hibernate的一级缓存 session缓存是由一系列的Java集合构成的。当一个对象被加入到…