当前位置: 首页 > news >正文

aosp14分屏分割线区域部分深入剖析-framework实战干货

背景:

原来在学习分屏课程期间,当时没有对分屏分割线的区域部分进行详细介绍。
本篇文章就针对这个块的知识进行详细的补充讲解,首先可以通过下图所示分割线情况,这样有一个初步的认识
在这里插入图片描述

简单说分屏情况下分割线是可以拖拉到不同的档位区域,而且竖屏和横屏的分割线区域还有差别,比如竖屏可以有5分档,而横屏只有3分档。

注意:这个5分档,3分档一般来说包含边际的两个分档,但是边际的分档一般看不见,所以说真的可以见分档线其实都需要减2,比如竖屏可见是3个分割线,横屏1各分割线
在这里插入图片描述

剖析分割线构建SnapTarget源码

代码位置:
frameworks/base/core/java/com/android/internal/policy/DividerSnapAlgorithm.java

这个DividerSnapAlgorithm是专门管理分割线位置相关的算法类
分割线的代表类SnapTarget:

/*** Represents a snap target for the divider.*/public static class SnapTarget {public static final int FLAG_NONE = 0;/** If the divider reaches this value, the left/top task should be dismissed. */public static final int FLAG_DISMISS_START = 1;/** If the divider reaches this value, the right/bottom task should be dismissed */public static final int FLAG_DISMISS_END = 2;/** Position of this snap target. The right/bottom edge of the top/left task snaps here. */public final int position;/*** Like {@link #position}, but used to calculate the task bounds which might be different* from the stack bounds.*/public final int taskPosition;public final int flag;public boolean isMiddleTarget;/*** Multiplier used to calculate distance to snap position. The lower this value, the harder* it's to snap on this target*/private final float distanceMultiplier;public SnapTarget(int position, int taskPosition, int flag) {this(position, taskPosition, flag, 1f);}public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) {this.position = position;this.taskPosition = taskPosition;this.flag = flag;this.distanceMultiplier = distanceMultiplier;}}

几个重要参数:

position 即代表分割线位置

flag 主要代表是来针对区分边际分割线,主要有FLAG_DISMISS_START(上)和FLAG_DISMISS_END(下),其他都是FLAG_NONE

distanceMultiplier 这个属于一个放大因子,就是针对首尾分割线的有到达距离的放大,让首尾分割线不会因为不小心误触很容易进入

具体计算分割线方法如下:

private void calculateTargets(boolean isHorizontalDivision, int dockedSide) {mTargets.clear();int dividerMax = isHorizontalDivision? mDisplayHeight: mDisplayWidth;int startPos = -mDividerSize;if (dockedSide == DOCKED_RIGHT) {startPos += mInsets.left;}//首先添加SnapTarget.FLAG_DISMISS_START的SnapTargetmTargets.add(new SnapTarget(startPos, startPos, SnapTarget.FLAG_DISMISS_START,0.35f));switch (mSnapMode) {//这里需要根据mSnapMode来分别创建分割情况case SNAP_MODE_16_9://正常竖屏就是SNAP_MODE_16_9,一般会创建3个分割线addRatio16_9Targets(isHorizontalDivision, dividerMax);break;case SNAP_FIXED_RATIO:addFixedDivisionTargets(isHorizontalDivision, dividerMax);break;case SNAP_ONLY_1_1://正常横屏就是SNAP_ONLY_1_1,所以只有中间一个分割线addMiddleTarget(isHorizontalDivision);break;case SNAP_MODE_MINIMIZED:addMinimizedTarget(isHorizontalDivision, dockedSide);break;}//最后添加SnapTarget.FLAG_DISMISS_END的SnapTargetmTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));}

参数isHorizontalDivision,代表是当前分割线是横着显示还是竖着显示,所以这里注意不是指的横竖屏幕,刚好是相反的,竖屏分割线是横着显示isHorizontalDivision =true,横屏isHorizontalDivision=false。

mSnapMode这个变量是在DividerSnapAlgorithm构造时候从res中config获取的
在这里插入图片描述通过搜索dockedStackDividerSnapMode发现如下:在这里插入图片描述竖屏值一般是0,横屏值一般是2

在这里插入图片描述
先看看简单的SNAP_ONLY_1_1的实现方法直接就是addMiddleTarget

    private void addMiddleTarget(boolean isHorizontalDivision) {int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));}public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,int displayWidth, int displayHeight, int dividerSize) {int start = isHorizontalDivision ? insets.top : insets.left;int end = isHorizontalDivision? displayHeight - insets.bottom: displayWidth - insets.right;return start + (end - start) / 2 - dividerSize / 2;}

这里可以看出来就是通过calculateMiddlePosition计算出来中间位置positon,再用这个positon构建对应的SnapTarget添加到mTargets。

同理看看竖屏的3个snap的计算方法addRatio16_9Targets

private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) {int start = isHorizontalDivision ? mInsets.top : mInsets.left;int end = isHorizontalDivision? mDisplayHeight - mInsets.bottom: mDisplayWidth - mInsets.right;int startOther = isHorizontalDivision ? mInsets.left : mInsets.top;int endOther = isHorizontalDivision? mDisplayWidth - mInsets.right: mDisplayHeight - mInsets.bottom;float size = 9.0f / 16.0f * (endOther - startOther);int sizeInt = (int) Math.floor(size);int topPosition = start + sizeInt;int bottomPosition = end - sizeInt - mDividerSize;//注意这里计算了topPosition,bottomPosition两个位置,这里就是3分割线的上下分割线位置addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
}private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,int bottomPosition, int dividerMax) {maybeAddTarget(topPosition, topPosition - getStartInset());addMiddleTarget(isHorizontalDivision);maybeAddTarget(bottomPosition,dividerMax - getEndInset() - (bottomPosition + mDividerSize));}

这里addRatio16_9Targets核心就是计算出来了topPosition,bottomPosition,然后再使用addNonDismissingTargets方法进行构建SnapTarget。

核心看看这里的topPosition,bottomPosition是怎么计算的,刚好也可以解释为啥这里名字有16_9这个字符,其实本质就是计算个topPosition位置,拿竖屏来举例的话,这里因为想要屏幕宽度固定的,为了美观程度,所以一般topPosition这个区域要固定为一个16:9区域

在这里插入图片描述

addNonDismissingTargets方法主要就是有了topPosition,bottomPosition,在额外加上个middle,这样整体3个SnapTarget就构造成功。

创建后的分割线SnapTarget都会被放入到mTargets这个集合中去。

寻找合适分割线算法

上面已经分析出了分割线的几个SnapTarget,这些SnapTarget都是有固定位置的,那么接下来分析一下分割线如何到对应的SnapTarget。

外部传递一个比例值情况
一般这种情况是在启动分屏时候,需要设置好一个上下分屏比例ratio,主要通过如下方法:

/** Updates divide position and split bounds base on the ratio within root bounds. */
public void setDivideRatio(float ratio) {final int position = isLandscape()? mRootBounds.left + (int) (mRootBounds.width() * ratio): mRootBounds.top + (int) (mRootBounds.height() * ratio);final DividerSnapAlgorithm.SnapTarget snapTarget =mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}

可以看到这里会先根据ratio计算出一个位置position,但是这个position并不是直接的SnapTarget的position,需要把这个position传递到calculateNonDismissingSnapTarget方法计算出SnapTarget,然后在使用SnapTarget的position。
即比如传递ratio进去是0.25,那么计算的position就是2960 * 0.25 = 740,但是正常的top的SnapTarget的position一般是810+84 = 894。
看看这里的calculateNonDismissingSnapTarget方法

   public SnapTarget calculateNonDismissingSnapTarget(int position) {SnapTarget target = snap(position, false /* hardDismiss */);//主要调用snapif (target == mDismissStartTarget) {return mFirstSplitTarget;} else if (target == mDismissEndTarget) {return mLastSplitTarget;} else {return target;}}

看看snap方法:

    private SnapTarget snap(int position, boolean hardDismiss) {if (shouldApplyFreeSnapMode(position)) {return new SnapTarget(position, position, SnapTarget.FLAG_NONE);}int minIndex = -1;float minDistance = Float.MAX_VALUE;int size = mTargets.size();for (int i = 0; i < size; i++) {SnapTarget target = mTargets.get(i);float distance = Math.abs(position - target.position);if (hardDismiss) {distance /= target.distanceMultiplier;}if (distance < minDistance) {//这里会计算传递进来position和SnapTarget.position的距离minIndex = i;minDistance = distance;}}return mTargets.get(minIndex);}

这里snap方法本质就是根据传递进来的position,与mTargets集合的所有position进行比较距离,离谁最近就选谁。

更多framework详细代码和资料参考如下链接
投屏专题部分:

https://mp.weixin.qq.com/s/IGm6VHMiAOPejC_H3N_SNg

hal+perfetto+surfaceflinger

https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
其他课程七件套专题:在这里插入图片描述
点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw

视频试看:
https://www.bilibili.com/video/BV1wc41117L4/

参考相关链接:
https://blog.csdn.net/zhimokf/article/details/137958615

更多framework假威风耗:androidframework007


http://www.mrgr.cn/news/51168.html

相关文章:

  • 微前端架构的思考 :专注于多框架的并存可能并不是唯一的方向 — 探讨新前端的分层式微前端架构
  • 《深度强化学习》-王树森:章节习题
  • 机器学习—— 机器学习运维(MLOps)
  • h2数据库模拟mysql进行单元测试遇到的问题
  • 基于SSM党建工作小秘书管理系统的设计
  • RAG书籍《大模型RAG实战》出版
  • 每日新闻掌握【2024年10月15日 星期二】
  • 低代码策略量化平台更新|大模型agents生态的一些思考
  • 视频格式在线转换,五种超实用的视频格式转换工具!
  • 【scene_manager_msgs】ROS2 自定义消息、服务的包
  • 用AI搞流量 | AI做好运壁纸号, 3个月涨粉6.4W
  • 通过SSH登录Linux系统并设置免密码登录
  • nvm安装,node多版本管理
  • MySQL创建和管理表
  • react18中实现简易增删改查useReducer搭配useContext的高级用法
  • 使用豆包MarsCode 来处理 Excel 的数据吧!
  • 4种鼓励创业创新的方法
  • GEE 批量删除 Assets 资产文件夹
  • 能源水务储能的可视化需求真多,我都忙不过来了。
  • 蓝紫激光模组常见的基本特性