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

2024-04-16 01:28

本文主要是介绍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);}
    

这篇关于Android T多屏多显——应用双屏间拖拽移动功能(更新中)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/907459

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

如何评价Ubuntu 24.04 LTS? Ubuntu 24.04 LTS新功能亮点和重要变化

《如何评价Ubuntu24.04LTS?Ubuntu24.04LTS新功能亮点和重要变化》Ubuntu24.04LTS即将发布,带来一系列提升用户体验的显著功能,本文深入探讨了该版本的亮... Ubuntu 24.04 LTS,代号 Noble NumBAT,正式发布下载!如果你在使用 Ubuntu 23.

Ubuntu 24.04 LTS怎么关闭 Ubuntu Pro 更新提示弹窗?

《Ubuntu24.04LTS怎么关闭UbuntuPro更新提示弹窗?》Ubuntu每次开机都会弹窗提示安全更新,设置里最多只能取消自动下载,自动更新,但无法做到直接让自动更新的弹窗不出现,... 如果你正在使用 Ubuntu 24.04 LTS,可能会注意到——在使用「软件更新器」或运行 APT 命令时,

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has