问题:MTK android 8.1 Launcher3 横屏状态长按图标 弹框位置居中分析.
现象:竖屏状态下正常,横屏状态下,长按最右边的apk图标弹出的信息框会显示在屏幕中间的位置.
期望:参考竖屏,应该显示在apk上方或者下方,方向偏向左边.
分析:
1.从Launcher3长按开始分析.
Luancher.java
@Override
public boolean onLongClick(View v) {
if (!isDraggingEnabled()) return false;
if (isWorkspaceLocked()) return false;
if (mState != State.WORKSPACE) return false;
boolean ignoreLongPressToOverview =
mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
if (v instanceof Workspace) {
if (!mWorkspace.isInOverviewMode()) {
if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
showOverviewMode(true);
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return true;
} else {
return false;
}
} else {
return false;
}
}
CellLayout.CellInfo longClickCellInfo = null;
View itemUnderLongClick = null;
if (v.getTag() instanceof ItemInfo) {
ItemInfo info = (ItemInfo) v.getTag();
longClickCellInfo = new CellLayout.CellInfo(v, info);
itemUnderLongClick = longClickCellInfo.cell;
mPendingRequestArgs = null;
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
if (!mDragController.isDragging()) {
if (itemUnderLongClick == null) {
// User long pressed on empty space
if (mWorkspace.isInOverviewMode()) {
mWorkspace.startReordering(v);
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.OVERVIEW);
} else {
if (ignoreLongPressToOverview) {
return false;
}
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
showOverviewMode(true);
}
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
} else {
final boolean isAllAppsButton =
!FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
longClickCellInfo.cellX, longClickCellInfo.cellY));
if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
// User long pressed on an item
mWorkspace.startDrag(longClickCellInfo, new DragOptions());
}
}
}
return true;
}
经过打Log,确认跑了这个代码 mWorkspace.startDrag(longClickCellInfo, new DragOptions());
startDrag()-->beginDragShared()
if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
PopupContainerWithArrow popupContainer = PopupContainerWithArrow
.showForIcon((BubbleTextView) child);
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
}
调用了PopupContainerWithArrow.java的showForIcon().
showForIcon()-->populateAndShow().
populateAndShow()中有两个方法orientAboutIcon()和addArrowView()相关.
private void orientAboutIcon(BubbleTextView icon, int arrowHeight) {
int width = getMeasuredWidth();
int height = getMeasuredHeight() + arrowHeight;
DragLayer dragLayer = mLauncher.getDragLayer();
dragLayer.getDescendantRectRelativeToSelf(icon, mTempRect);
Rect insets = dragLayer.getInsets();
// Align left (right in RTL) if there is room.
int leftAlignedX = mTempRect.left + icon.getPaddingLeft();
int rightAlignedX = mTempRect.right - width - icon.getPaddingRight();
int x = leftAlignedX;
boolean canBeLeftAligned = leftAlignedX + width + insets.left
< dragLayer.getRight() - insets.right;
boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
x = rightAlignedX;
}
mIsLeftAligned = x == leftAlignedX;
if (mIsRtl) {
x -= dragLayer.getWidth() - width;
}
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight();
iconWidth *= icon.getScaleX();
Resources resources = getResources();
int xOffset;
if (isAlignedWithStart()) {
// Aligning with the shortcut icon.
int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
int shortcutPaddingStart = resources.getDimensionPixelSize(
R.dimen.popup_padding_start);
xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
} else {
// Aligning with the drag handle.
int shortcutDragHandleWidth = resources.getDimensionPixelSize(
R.dimen.deep_shortcut_drag_handle_size);
int shortcutPaddingEnd = resources.getDimensionPixelSize(
R.dimen.popup_padding_end);
xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
}
x += mIsLeftAligned ? xOffset : -xOffset;
// Open above icon if there is room.
int iconHeight = icon.getIcon() != null
? icon.getIcon().getBounds().height()
: icon.getHeight();
int y = mTempRect.top + icon.getPaddingTop() - height;
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
if (!mIsAboveIcon) {
y = mTempRect.top + icon.getPaddingTop() + iconHeight;
}
// Insets are added later, so subtract them now.
if (mIsRtl) {
x += insets.right;
} else {
x -= insets.left;
}
y -= insets.top;
mGravity = 0;
if (y + height > dragLayer.getBottom() - insets.bottom) {
// The container is opening off the screen, so just center it in the drag layer instead.
mGravity = Gravity.CENTER_VERTICAL;
// Put the container next to the icon, preferring the right side in ltr (left in rtl).
int rightSide = leftAlignedX + iconWidth - insets.left;
int leftSide = rightAlignedX - iconWidth - insets.left;
if (!mIsRtl) {
if (rightSide + width < dragLayer.getRight()) {
x = rightSide;
mIsLeftAligned = true;
} else {
x = leftSide;
mIsLeftAligned = false;
}
} else {
if (leftSide > dragLayer.getLeft()) {
x = leftSide;
mIsLeftAligned = false;
} else {
x = rightSide;
mIsLeftAligned = true;
}
}
mIsAboveIcon = true;
}
setX(x);
setY(y);
}
在orientAboutIcon()经过一些方法的计算,最终确认setX(x)和setY(y).打印这两个值,未发现异常.均是长按图标的位置.
但是实际效果却是x,y的值不对.即显示位置不对,那么应该用其他地方重新设置了位置坐标x,y.经过搜索setX和setY.
PopupContainerWithArrow.java中,有enforceContainedWithinScreen()方法,很明显是这里修改了x,y的值.
private void enforceContainedWithinScreen(int left, int right) {
DragLayer dragLayer = mLauncher.getDragLayer();
if (getTranslationX() + left < 0 ||
getTranslationX() + right > dragLayer.getWidth()) {
// If we are still off screen, center horizontally too.
mGravity |= Gravity.CENTER_HORIZONTAL;
}
if (Gravity.isHorizontal(mGravity)) {
setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
}
if (Gravity.isVertical(mGravity)) {
setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
}
}
经过打印Log:getTranslationX() + right>dragLayer.getWidth().
屏蔽enforceContainedWithinScreen()的setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
长按位置正确,但是方向不对.且屏蔽的改法是有风险的,当显示的信息框超出屏幕显示位置的时候,有部分不显示.这个跟屏幕的分辨率,Launcher桌面布局有关,所以不能在这里修改.
那么在这个判断之前,应该做的是让信息框整体不超过屏幕分辨率,如同竖屏那样.在apk图标的左边.
那么修改的地方应在populateAndShow()的两个方法orientAboutIcon()和addArrowView().
查看orientAboutIcon()方法,mIsLeftAligned这个变量经过翻译是:左对齐.
打印Log,显示横屏状态下,这个值一直为true.而在竖屏时,长按最右边的apk按钮是false,其他为true.
分析上面的判断,根据弹出框的大小,位置,显示屏的大小判断的.
原有的代码逻辑没有变动,我在这里增加了一个判断.
}
x += mIsLeftAligned ? xOffset : -xOffset;
+ //Add Begin :Modify the long click icon to display the
+ //information position.
+ int shortcutItemWidth =
+ resources.getDimensionPixelSize(R.dimen.bg_popup_item_width);
+ if( x + shortcutItemWidth > dragLayer.getWidth()){
+ mIsLeftAligned = false;
+ x = x - shortcutItemWidth + xOffset;
+ }
+ //Add End
+
// Open above icon if there is room.
int iconHeight = icon.getIcon() != null
? icon.getIcon().getBounds().height()
这个判断为:x + shortcutItemWidth > dragLayer.getWidth().点击apk的位置的x值加上弹出框的宽度大于屏幕显示宽度时:
左对齐mIsLeftAligned为false,即为右对齐.同时需要重新调整x的位置.x = x - shortcutItemWidth + xOffset;
经过测试,问题解决.
如有更好的修改方法,欢迎交流.