Kuix 焦点跳转分析

    先从一个案例入手我有一个界面,上面是列表,下面是一排按钮,列表的内容比较多,我希望按右键可以直接跳转到按钮,实际上一开始确实ok,后来换了一套Skin,按右键死活都不跳到下面的按钮了,转回原来的skin,又可以正常跳转.一开始拼命比对两个skin的样式表,没有发现明显的差异,相同的代码怎么可能造成不通的运行结果,答案就是代码造成的.

    对Kuix用的比较多的人会发现,Kuix的焦点跳转有点奇怪的地方,按左右键移动到屏幕最右边的控件后,会自动跳到下一个容器里面的控件,而且不是第一个控件,是离当前控件距离最近的控件.这理解起来是有一些道理的,因为多数手机是没有触摸屏的,依靠按键快速跳动的话,这种算法比较适用于普通人的视觉习惯,但是这个算法却存在不少问题,在特定的时候就会跳出来.

 

	/**
	 * Returns the previous or next focusable {@link Widget} according to the
	 * <code>forward</code> parameter.
	 * 
	 * @return The previous or next focusable {@link Widget}
	 */
	public Widget getOtherFocus(Widget root, Widget focusedWidget, Widget nearestFocusableWidget, boolean forward, Alignment direction, boolean checkItself, boolean checkChild, boolean checkParent) {
		boolean isVisible = isVisible();
		if (checkItself && root != this && !isFocused() && isFocusable() && isVisible) {
			if (focusedWidget == null || direction == null) {
				return this;
			}
			if (isNearest(focusedWidget, nearestFocusableWidget, direction)) {
				nearestFocusableWidget = this;
			}
		} else if (checkChild && isVisible) {
			// Children scan
			Widget childWidget = forward ? child : lastChild;
			if (childWidget != null) {
				nearestFocusableWidget = childWidget.getOtherFocus(root, focusedWidget, nearestFocusableWidget, forward, direction, true, true, false);
				if ((focusedWidget == null || direction == null) && nearestFocusableWidget != null) {
					return nearestFocusableWidget;
				}
			}
		}
		if (root != this) {
			// Brother scan
			Widget otherWidget = forward ? next : previous;
			if (otherWidget != null) {
				nearestFocusableWidget = otherWidget.getOtherFocus(root, focusedWidget, nearestFocusableWidget, forward, direction, true, true, false);
				if ((focusedWidget == null || direction == null) && nearestFocusableWidget != null) {
					return nearestFocusableWidget;
				}
			}
			// Parent scan
			if (checkParent && parent != null && isVisible) {
				nearestFocusableWidget = parent.getOtherFocus(root, focusedWidget, nearestFocusableWidget, forward, direction, true, false, true);
				if ((focusedWidget == null || direction == null) && nearestFocusableWidget != null) {
					return nearestFocusableWidget;
				}
			}
		}
		return nearestFocusableWidget;
	}

 这个是跳转焦点的函数,即使debug进去的话也相当难理解,因为是递归调用的,而且一般会嵌套相当多层,基本上也是按照正常的逻辑思维,现在同一容器中查找,目标控件是容器的话再查找里面的子控件,同级容器没有的话再转到上一级容器,而这个函数最主要的目标则是isNearest函数,判断两个控件的距离是不是最近的.

 

	private boolean isNearest(Widget originWidget, Widget nearestWidget, Alignment direction) {
		if (originWidget != null && direction != null) {
			int origX = originWidget.getDisplayX() + originWidget.visualCenterX;
			int origY = originWidget.getDisplayY() + originWidget.visualCenterY;
			int dx = getDisplayX() + visualCenterX - origX;
			int dy = getDisplayY() + visualCenterY - origY;
			if (nearestWidget != null) {
				int nearestDx = nearestWidget.getDisplayX() + nearestWidget.visualCenterX - origX;
				int nearestDy = nearestWidget.getDisplayY() + nearestWidget.visualCenterY - origY;
				if (direction.isTop() || direction.isBottom()) {
					if (Math.abs(dy) <= Math.abs(nearestDy) && dy != 0 && (direction.isBottom() && dy > 0 || direction.isTop() && dy < 0)) {
						if (Math.abs(dx) <= Math.abs(nearestDx)) {
							return Math.abs(dx) + Math.abs(dy) < Math.abs(nearestDx) + Math.abs(nearestDy);
						}
					}
				} else {
					if (Math.abs(dx) <= Math.abs(nearestDx) && dx != 0 && (direction.isRight() && dx > 0 || direction.isLeft() && dx < 0)) {
						if (Math.abs(dy) <= Math.abs(nearestDy)) {
							return Math.abs(dx) + Math.abs(dy) < Math.abs(nearestDx) + Math.abs(nearestDy);
						}
					}
				}
			} else {
				if (direction.isTop() || direction.isBottom()) {
					return dy != 0 && (direction.isBottom() && dy > 0 || direction.isTop() && dy < 0);
				} else {
					return dx != 0 && (direction.isRight() && dx > 0 || direction.isLeft() && dx < 0);
				}
			}
		}
		return false;
	}

    这个函数其实也不是太好理解,原意是根据两个控件的坐标差来判断,由于函数里面只是比较两个控件,所以实际上只是比较到某个方向上逐渐"靠近"过来的控件,一旦距离超过临界值0,就认为是最近的控件,实际上会造成一个问题,就是一旦"下一行"的最后一个控件中心比当前控件小(在比较的方向上),这个跳转就不成立.这就解释我上面碰到的"诡异"的问题.换了skin后,按钮的中心变化了,中心x坐标比列表的中心小,导致上面的跳转不能成立,实际上一旦下一列的最后一个按钮中心x坐标小于等于焦点控件的中心x坐标,跳转就会失败.

    解决方法:1 偷懒,增加下面列按钮的中心x坐标 2 修改Widget代码

    这个代码实际上还会造成一个问题,在屏幕较小的手机,而列表项为多行的文本时,按向下键移动到屏幕上最后一列时,滚动条不会滚动到下一列,而是跳转到下面的按钮栏了,滚动条下面的列表信息永远看不到了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值