仿华为车机UI--图标从Workspace拖动到Hotseat同时保留图标在原来位置

基于Android13 Launcher3,原生系统如果把图标从Workspace拖动到Hotseat里则Workspace就没有了,需求是执行拖拽动作后,图标同时保留在原位置。

实现效果如下:

99fdd6f2bacc40e586ebec33272195eb.gif

实现思路:

1.如果在workspace中拖动,则保留原来“改变图标位置”的功能

2.如果拖拽到Hotseat,则添加到Hotseat的同时也在原来位置复制一个一样的View 

3.每次改变都要存到数据库以保存当前状态

思路非常简单,但是要找适当的位置添加适当的代码不简单。需要读懂原生代码。

分析了源代码后,发现应该参考Workspace.java的onDrop与onDropExternal代码来实现。

step1: 读懂代码后添加注释(中文为添加的注释)和添加onDropToHotseat()的调用

@Overridepublic void onDrop(final DragObject d, DragOptions options) {mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);CellLayout dropTargetLayout = mDropToLayout;Log.d("drop","onDrop!");// We want the point to be mapped to the dragTarget.if (dropTargetLayout != null) {mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);}boolean droppedOnOriginalCell = false;boolean snappedToNewPage = false;boolean resizeOnDrop = false;Runnable onCompleteRunnable = null;if (d.dragSource != this || mDragInfo == null) {//从别的地方(AllApp等)拖到worksapce的 startfinal int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],(int) mDragViewVisualCenter[1] };onDropExternal(touchXY, dropTargetLayout, d);Log.d("drop","onDropExternal!");//从别的地方(AllApp等)拖到worksapce的 end} else {//从workspace拖到workspace startfinal View cell = mDragInfo.cell;boolean droppedOnOriginalCellDuringTransition = false;if (dropTargetLayout != null && !d.cancelled) {//有地方可放且没有取消拖动 start// Move internallyboolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);Log.d("drop","hasMovedIntoHotseat :"+hasMovedIntoHotseat);int container = hasMovedIntoHotseat ?LauncherSettings.Favorites.CONTAINER_HOTSEAT :LauncherSettings.Favorites.CONTAINER_DESKTOP;int screenId = (mTargetCell[0] < 0) ?mDragInfo.screenId : getIdForScreen(dropTargetLayout);int spanX = mDragInfo != null ? mDragInfo.spanX : 1;int spanY = mDragInfo != null ? mDragInfo.spanY : 1;// First we find the cell nearest to point at which the item is// dropped, without any consideration to whether there is an item there.mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter(mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);// If the item being dropped is a shortcut and the nearest drop// cell also contains a shortcut, then create a folder with the two shortcuts.if (createUserFolderIfNecessary(cell, container,dropTargetLayout, mTargetCell, distance, false, d)|| addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,distance, d, false)) {mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);return;}// Aside from the special case where we're dropping a shortcut onto a shortcut,// we need to find the nearest cell location that is vacantItemInfo item = d.dragInfo;int minSpanX = item.spanX;int minSpanY = item.spanY;if (item.minSpanX > 0 && item.minSpanY > 0) {minSpanX = item.minSpanX;minSpanY = item.minSpanY;}droppedOnOriginalCell = item.screenId == screenId && item.container == container&& item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];droppedOnOriginalCellDuringTransition = droppedOnOriginalCell && mIsSwitchingState;// When quickly moving an item, a user may accidentally rearrange their// workspace. So instead we move the icon back safely to its original position.boolean returnToOriginalCellToPreventShuffling = !isFinishedSwitchingState()&& !droppedOnOriginalCellDuringTransition && !dropTargetLayout.isRegionVacant(mTargetCell[0], mTargetCell[1], spanX, spanY);int[] resultSpan = new int[2];if (returnToOriginalCellToPreventShuffling) {mTargetCell[0] = mTargetCell[1] = -1;} else {//让目的地Layout重新布局一下顺序,腾出drop的位置mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],(int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);}boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;// if the widget resizes on dropif (foundCell && (cell instanceof AppWidgetHostView) &&(resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {resizeOnDrop = true;item.spanX = resultSpan[0];item.spanY = resultSpan[1];AppWidgetHostView awhv = (AppWidgetHostView) cell;WidgetSizes.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],resultSpan[1]);}if (foundCell) {//目的地有空出位置 startint targetScreenIndex = getPageIndexForScreenId(screenId);int snapScreen = getLeftmostVisiblePageForIndex(targetScreenIndex);// On large screen devices two pages can be shown at the same time, and snap// isn't needed if the source and target screens appear at the same timeif (snapScreen != mCurrentPage && !hasMovedIntoHotseat) {snapToPage(snapScreen);snappedToNewPage = true;}final ItemInfo info = (ItemInfo) cell.getTag();/*这一段实现把cell从原来的父View中remove掉,添加到目的layout里去 start*/if (hasMovedLayouts) {// Reparent the view  这段非常关键,重新安排父View startLog.d("drop","drop to different layout!!");//表示放在了不同的layout里CellLayout parentCell = getParentCellLayoutForView(cell);if (parentCell != null) {parentCell.removeView(cell);//如果注释这句,就会报错,说明view不能有两个parent} else if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {d.dragView.detachContentView(/* reattachToPreviousParent= */ false);} else if (FeatureFlags.IS_STUDIO_BUILD) {throw new NullPointerException("mDragInfo.cell has null parent");}addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],info.spanX, info.spanY);//假如我们在此调用getParentCellLayoutForView(cell);得到的parentCell就是目的layout了// Reparent the view  这段非常关键,重新安排父View end//added by Kevin.Ye for create keep dragObject when dropped to Hotseatif(hasMovedIntoHotseat)onDropToHotseat(d);//end}/*这一段实现把cell从原来的父View中remove掉,添加到目的layout里去 end*/// update the item's position after drop/*把目的地位置,作为这个cell的位置 start*/CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();lp.cellX = lp.tmpCellX = mTargetCell[0];lp.cellY = lp.tmpCellY = mTargetCell[1];lp.cellHSpan = item.spanX;lp.cellVSpan = item.spanY;lp.isLockedToGrid = true;/*把目的地位置,作为这个cell的位置 end*/if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&cell instanceof LauncherAppWidgetHostView) {//这段处理的是widgetfinal CellLayout cellLayout = dropTargetLayout;// We post this call so that the widget has a chance to be placed// in its final locationfinal LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE&& !options.isAccessibleDrag) {onCompleteRunnable = () -> {if (!isPageInTransition()) {AppWidgetResizeFrame.showForWidget(hostView, cellLayout);}};}}//更新到数据库里去mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,lp.cellX, lp.cellY, item.spanX, item.spanY);} else {//没有找到位置drop startif (!returnToOriginalCellToPreventShuffling) {onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId);}if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {d.dragView.detachContentView(/* reattachToPreviousParent= */ true);}// If we can't find a drop location, we return the item to its original positionCellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();mTargetCell[0] = lp.cellX;mTargetCell[1] = lp.cellY;CellLayout layout = (CellLayout) cell.getParent().getParent();layout.markCellsAsOccupiedForView(cell);}//没有找到位置 end} else {//处理取消drag start// When drag is cancelled, reattach content view back to its original parent.if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {d.dragView.detachContentView(/* reattachToPreviousParent= */ true);}//处理取消drag end}final CellLayout parent = (CellLayout) cell.getParent().getParent();if (d.dragView.hasDrawn()) {//拖动View已经绘图 statif (droppedOnOriginalCellDuringTransition) {//过渡的过程中拖到原来的位置 start// Animate the item to its original position, while simultaneously exiting// spring-loaded mode so the page meets the icon where it was picked up.final RunnableList callbackList = new RunnableList();final Runnable onCompleteCallback = onCompleteRunnable;mLauncher.getDragController().animateDragViewToOriginalPosition(/* onComplete= */ callbackList::executeAllAndDestroy, cell,SPRING_LOADED.getTransitionDuration(mLauncher, true /* isToState */));mLauncher.getStateManager().goToState(NORMAL, /* delay= */ 0,onCompleteCallback == null? null: forSuccessCallback(() -> callbackList.add(onCompleteCallback)));mLauncher.getDropTargetBar().onDragEnd();parent.onDropChild(cell);return;}//过渡的过程中拖到原来的位置 endfinal ItemInfo info = (ItemInfo) cell.getTag();boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;if (isWidget) {//桌面小组件drop到新位置int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :ANIMATE_INTO_POSITION_AND_DISAPPEAR;animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);} else {//图标动画移动到新位置,但如果没有drop到目的地,则会回到原来的位置int duration = snappedToNewPage ? ADJACENT_SCREEN_DROP_DURATION : -1;mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,this);}} else {d.deferDragViewCleanupPostAnimation = false;cell.setVisibility(VISIBLE);}parent.onDropChild(cell);mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,onCompleteRunnable == null ? null : forSuccessCallback(onCompleteRunnable));mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId).log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);}if (d.stateAnnouncer != null && !droppedOnOriginalCell) {d.stateAnnouncer.completeAction(R.string.item_moved);}}

step2:同样在Workspace.java中添加增加的接口 onDropToHotseat(DragObject d)

 

/** Added by Kevin.Ye* when a shortcut was dropped to Hotseat,we create a new one in original position* */private void onDropToHotseat(DragObject d){ItemInfo info = d.dragInfo;WorkspaceItemInfo newItemInfo = null;View view;switch (info.itemType) {case ITEM_TYPE_APPLICATION:case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:if (info instanceof WorkspaceItemFactory) {// Came from all apps -- make a copynewItemInfo = ((WorkspaceItemFactory) info).makeWorkspaceItem(mLauncher);//d.dragInfo = info;}if (info instanceof WorkspaceItemInfo) {// Came from all apps prediction row -- make a copynewItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);//d.dragInfo = info;}view = mLauncher.createShortcut((WorkspaceItemInfo) info);break;case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, mLauncher, (ViewGroup) getChildAt(0),(FolderInfo) info);break;default:throw new IllegalStateException("Unknown item type: " + info.itemType);}//final ContentResolver cr = getContext().getContentResolver();//newItemInfo.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(LauncherSettings.Settings.EXTRA_VALUE);if(newItemInfo == null)return;newItemInfo.id = ItemInfo.NO_ID;//this is very important,otherwise it won't be add new shortcut in database marked by Kevin.YeLog.d("drop","info.id:"+info.id+" newItemInfo.id:"+newItemInfo.id);// Add the item to DB before adding to screen ensures that the container and other// values of the info is properly updated.Log.d("drop","new shortcut container:"+newItemInfo.container+" screenId:"+newItemInfo.screenId+" cellX:"+newItemInfo.cellX+" cellY:"+newItemInfo.cellY);//info.id = ItemInfo.NO_ID;//it is very important as we need to add new one to database not movemLauncher.getModelWriter().addOrMoveItemInDatabase(newItemInfo, newItemInfo.container, newItemInfo.screenId, newItemInfo.cellX, newItemInfo.cellY);addInScreen(view, newItemInfo.container, newItemInfo.screenId, newItemInfo.cellX, newItemInfo.cellY,newItemInfo.spanX, newItemInfo.spanY);}

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【开源免费】基于SpringBoot+Vue.JS高校校园招聘服务系统(JAVA毕业设计)

本文项目编号 T 010 &#xff0c;文末自助获取源码 \color{red}{T010&#xff0c;文末自助获取源码} T010&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

渗透测试靶机---- DC系列 DC-4

渗透测试靶机---- DC系列 DC-4 开启靶机&#xff0c;登录页面&#xff0c;平平无奇 扫描ip 端口&#xff0c;服务等信息 访问80 登录窗&#xff01;&#xff01;&#xff01; 这里说明了admin信息&#xff0c;那么就直接爆破这个admin的密码 密码&#xff1a;happy 登录成功 在…

Mysql之存储引擎概述

文章目录 存储引擎MySQL体系结构存储引擎特点InnoDBMyISAMMemory总结 存储引擎选择 存储引擎 MySQL体系结构 连接层&#xff1a;最上层是一些客户端和链接服务&#xff0c;主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所…

Rust多线程编程概述

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学&#xff0c;之一 -CSDN博客 Rust到底值不值得学&#xff0c;之二-CSDN博客 12.2.1 线程的基本概念 线程&…

随手记:小程序体积超出2M包大小如何优化

小程序的包体积限制是2M&#xff0c;超出包大小如何优化 先简单列出&#xff0c;最近比较忙&#xff0c;后续优化明细&#xff0c;有着急的先留言踢我 1.分包 留几个主要的页面体积小的&#xff0c;剩下的在page.json中拆到subpackages中&#xff0c;简单举个例子 "page…

58、Python之函数高级:不定参数的函数,写出更加通用的装饰器

引言 上一篇文章中&#xff0c;我们见到引入了Python中的装饰器&#xff0c;通过一个简单的案例实现了一个初步的装饰器&#xff0c;但是&#xff0c;这个装饰器其实是有些缺陷。这一篇文章中&#xff0c;我们对上一篇文章中的装饰器进行一个优化升级&#xff0c;从而写出更加…

【王树森】RNN模型与NLP应用(5/9):多层RNN、双向RNN、预训练(个人向笔记)

Stacked RNN&#xff08;多层RNN&#xff09; 1. 原理 多个全连接层可以堆叠&#xff0c;多个卷积层也可以堆叠。同理&#xff1a;RNN也可以堆叠形成多层RNN。 如下图所示&#xff1a;对于每一个时刻的输出 h t h_t ht​&#xff0c;它既会作为下一个时刻的输入&#xff0c;…

ElasticSearch学习笔记(三)RestClient操作文档、DSL查询文档、搜索结果排序

文章目录 前言5 RestClient操作文档5.4 删除文档5.4 修改文档5.5 批量导入文档 6 DSL查询文档6.1 准备工作6.2 全文检索查询6.3 精准查询6.4 地理坐标查询6.5 复合查询6.5.1 相关性算分6.5.2 布尔查询 7 搜索结果处理7.1 排序7.1.1 普通字段排序7.1.2 地理坐标排序 前言 Elast…

Git 下载安装使用基本教程

一、下载 Git&#xff0c;并安装 Git for Windows&#xff0c; 无脑安装。安装完&#xff0c;鼠标右键&#xff0c;会看到这两个东西。 二、Gitee操作 新建仓库 初始化 readme 文件 点击克隆 复制地址 右键 git clone 第一次会需要你输入账号密码 输入后拉取成功。 把想要上传…

【安全生产】叉车安全带报警器有哪些特点?

叉车安全带报警器是用于防止在叉车发生猛烈碰撞或紧急制动时&#xff0c;司机不与方向盘、挡风玻璃等发生二次碰撞或抛出车外&#xff0c;从而造成的严重伤害的一种叉车安全装置、配件。 很多司机在开叉车时经常有不系安全带的习惯&#xff0c;有很多不负责任的人会认为&#…

PHP一键发起灵活定制多功能投票小程序系统源码

​一键发起&#xff0c;灵活定制 —— 多功能投票小程序 &#x1f680;【开篇&#xff1a;告别繁琐&#xff0c;投票新体验】&#x1f680; 还在为组织投票活动而头疼不已吗&#xff1f;繁琐的流程、有限的选项、难以统计的结果...这些都将成为过去式&#xff01;今天&#x…

MyPrint打印设计器(六)svg篇-直线

svg-二阶贝塞尔曲线 介绍一款强大的svg操作库&#xff0c;能够通过简单的代码&#xff0c;实现svg绘制与操纵&#xff0c;实现拖拽等功能 代码仓库 在线体验 代码仓库&#xff1a;github 代码仓库&#xff1a;gitee 实战项目&#xff1a;MyPrint 操作简单&#xff0c;组件丰富…

notepad++将换行替换成空

将多行里的换行置为一行&#xff0c;例如将下面的6行置为3行 crrlH打开替换框&#xff0c; 替换目标为【,\r\n】&#xff0c;替换成空&#xff0c;勾选循环查找和 正则表达式&#xff0c;全部替换即可。 替换后的效果

【AI】Pytorch_模型构建

建议点赞收藏关注&#xff01;持续更新至pytorch大部分内容更完。 本文已达到10w字&#xff0c;故按模块拆开&#xff0c;详见目录导航。 整体框架如下 数据及预处理 模型及其构建 损失函数及优化器 本节目录 模型线性回归逻辑回归LeNetAlexNet 构建模块组织复杂网络初始化网络…

中国各地级市的海拔标准差

海拔标准差是衡量地理测量准确性的重要指标&#xff0c;它通过计算特定地点的海拔测量值与平均海拔之间的偏差来评估数据的可靠性。较小的标准差意味着测量结果较为一致&#xff0c;而较大的标准差则可能指出数据的波动性或测量误差。 计算方法 海拔标准差的计算遵循以下公式…

【pycharm-乱码】简单记录一下都有哪些涉及编码

控制台 路径&#xff1a;setting-》general-》console setting-》editor-》file encodings 路径&#xff1a;setting-》editor->file and code templates #!/user/bin/env python3 # -*- coding: utf-8 -*-setting->tools->ssh terminal

新款14 英寸和16英寸MacBook Pro开始组装生产

据 DigiTimes 报道&#xff0c;苹果的供应链已于 8 月开始批量生产搭载 M4 Pro 和 M4 Max 芯片的下一代 14 英寸和 16 英寸 MacBook Pro 机型。这意味着笔记本电脑的组装工作很可能已经开始。 此信息与显示行业分析师 Ross Young 的说法相符&#xff0c;他声称下一代 14 英寸和…

Edge资源占用优化:调整浏览器设置与关闭自动更新检查

最近&#xff0c;作者在学习过程中&#xff0c;打开任务管理器的时候注意到&#xff0c;即使没有打开浏览器&#xff0c;edge依然有着内存占用较高的情况&#xff1a; 于是就在网上收集了一些后台调优的方法&#xff0c;如果各位朋友有更多优化浏览器资源占用的方法&#xff0c…

智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序(KNN分类器)

智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序&#xff08;KNN分类器&#xff09; 文章目录 一、基本原理原理流程举个例子总结 二、实验结果三、核心代码四、代码获取五、总结 智能优化特征选择|基于鲸鱼WOA优化算法实现的特征选择研究Matlab程序&#x…

【Linux系统】线程的同步 生产消费模型

同步 同步概念 同步&#xff1a;在保证数据安全的前提下&#xff0c;让线程能够按照某种特定的顺序访问临界资源&#xff0c;从而有效避免饥饿问题&#xff0c;叫做同步 竞态条件&#xff1a;因为时序问题&#xff0c;而导致程序异常&#xff0c;我们称之为竞态条件。在线程场…