InputManager inject Mouse Move 事件

本文深入解析了MyGUI中鼠标移动事件的处理流程,从InputManager的injectMouseMove方法入手,详细阐述了如何通过LayerManager确定鼠标焦点所在的Widget,以及涉及的ILayerItem接口实现。

前面几篇主要介绍了MyGUI类的静态关系,还有一些粗糙的分析。

先看下关于Widget关于Inputmanager

注入鼠标移动事件时一些列流程。

	bool InputManager::injectMouseMove(int _absx, int _absy, int _absz)
	{
		// запоминаем позицию
		mMousePosition.set(_absx, _absy);

		// вычисляем прирост по колеса
		int relz = _absz - mOldAbsZ;
		mOldAbsZ = _absz;

		// проверка на скролл
		if (relz != 0)
		{
			bool isFocus = isFocusMouse();
			if (mWidgetMouseFocus != nullptr) mWidgetMouseFocus->onMouseWheel(relz);
			return isFocus;
		}

		if (mIsWidgetMouseCapture)
		{
			if (mWidgetMouseFocus != nullptr)
			{
				if (mLayerMouseFocus != nullptr)
				{
					IntPoint point = mLayerMouseFocus->getPosition(_absx, _absy);
					mWidgetMouseFocus->onMouseDrag(point.left, point.top);
				}
			}
			else
				mIsWidgetMouseCapture = false;
			return true;
		}

		Widget* old_mouse_focus = mWidgetMouseFocus;

		// ищем активное окно
		Widget* item = LayerManager::getInstance().getWidgetFromPoint(_absx, _absy);

		// ничего не изменилось
		if (mWidgetMouseFocus == item)
		{
			bool isFocus = isFocusMouse();
			if (mWidgetMouseFocus != nullptr)
			{
				if (mLayerMouseFocus != nullptr)
				{
					IntPoint point = mLayerMouseFocus->getPosition(_absx, _absy);  获取鼠标focus点-〉ILayer-〉ILayerItem-〉ILayerItem-〉Widget
					mWidgetMouseFocus->onMouseMove(_absx, _absy);
				}
			}
			return isFocus;
		}

		if (item)
		{
			// поднимаемся до рута
			Widget* root = item;
			while (root->getParent()) root = root->getParent();

			// проверяем на модальность
			if (!mVectorModalRootWidget.empty())
			{
				if (root != mVectorModalRootWidget.back())
				{
					item = nullptr;
				}
			}

			if (item != nullptr)
			{
				mLayerMouseFocus = root->getLayer();
			}
		}

		// в методе может пропасть наш виджет
		WidgetManager::getInstance().addWidgetToUnlink(item);


		//-------------------------------------------------------------------------------------//
		// новый вид рутового фокуса мыши
		Widget* save_widget = nullptr;

		// спускаемся по новому виджету и устанавливаем рутовый фокус
		Widget* root_focus = item;
		while (root_focus != nullptr)
		{
			if (root_focus->mRootMouseActive)
			{
				save_widget = root_focus;
				break;
			}
			root_focus->mRootMouseActive = true;

			// в методе может пропасть наш виджет
			WidgetManager::getInstance().addWidgetToUnlink(root_focus);
			root_focus->onMouseChangeRootFocus(true);
			WidgetManager::getInstance().removeWidgetFromUnlink(root_focus);

			if (root_focus)
				root_focus = root_focus->getParent();
		}

		// спускаемся по старому виджету и сбрасываем фокус
		root_focus = mWidgetMouseFocus;
		while (root_focus != nullptr)
		{
			if (root_focus == save_widget)
			{
				break;
			}
			root_focus->mRootMouseActive = false;

			// в методе может пропасть наш виджет
			WidgetManager::getInstance().addWidgetToUnlink(root_focus);
			root_focus->onMouseChangeRootFocus(false);
			WidgetManager::getInstance().removeWidgetFromUnlink(root_focus);

			if (root_focus)
				root_focus = root_focus->getParent();
		}
		//-------------------------------------------------------------------------------------//

		// смена фокуса, проверяем на доступность виджета
		if ((mWidgetMouseFocus != nullptr) && (mWidgetMouseFocus->isEnabled()))
		{
			mWidgetMouseFocus->onMouseLostFocus(item);
		}

		WidgetManager::getInstance().removeWidgetFromUnlink(item);


		if ((item != nullptr) && (item->isEnabled()))
		{
			item->onMouseMove(_absx, _absy);
			item->onMouseSetFocus(mWidgetMouseFocus);
		}

		// запоминаем текущее окно
		mWidgetMouseFocus = item;

		if (old_mouse_focus != mWidgetMouseFocus)
			eventChangeMouseFocus(mWidgetMouseFocus);

		return isFocusMouse();
	}


分析点一:LayerManager

层的概念在这里运用:

第一步找出大的层位置:

LayerManager

	Widget* LayerManager::getWidgetFromPoint(int _left, int _top)
	{
		VectorLayer::reverse_iterator iter = mLayerNodes.rbegin();
		while (iter != mLayerNodes.rend())
		{
			ILayerItem * item = (*iter)->getLayerItemByPoint(_left, _top);
			if (item != nullptr) return static_cast<Widget*>(item);
			++iter;
		}
		return nullptr;
	}

分析点二:ILayerItem

找到对应的ILayerItem

 Widget* LayerManager::getWidgetFromPoint(int _left, int _top)

	{
		VectorLayer::reverse_iterator iter = mLayerNodes.rbegin();
		while (iter != mLayerNodes.rend())
		{
			ILayerItem * item = (*iter)->getLayerItemByPoint(_left, _top);
			if (item != nullptr) return static_cast<Widget*>(item);
			++iter;
		}
		return nullptr;
	}

分析点三:LayerNode

Widget的子节点放入LayerNode中进行子节点的查询操作

这一招MyGUI用的已经很纯熟,因为每次的WidgetFocus只可能是不可分割的一部分,原子级的。

所以MyGUI 遍历子节点查询自己的焦点所在。

注意:第一个for的作用和第二个for的作用是不同的。

第一个for的作用就是用作遍历子节点,而第二个是查找子节点的Item

	ILayerItem* LayerNode::getLayerItemByPoint(int _left, int _top)
	{
		// сначала пикаем детей
		for (VectorILayerNode::iterator iter = mChildItems.begin(); iter!=mChildItems.end(); ++iter)
		{
			ILayerItem * item = (*iter)->getLayerItemByPoint(_left, _top);
			if (nullptr != item) return item;
		}

		for (VectorLayerItem::iterator iter=mLayerItems.begin(); iter!=mLayerItems.end(); ++iter)
		{
			ILayerItem * item = (*iter)->getLayerItemByPoint(_left, _top);
			if (nullptr != item) return item;
		}

		return nullptr;
	}

分析点四:

最终实现 IlayerItem 的 getLayerItemByPoint 接口

LayerItem 是一个虚类,因为其没有实现 IlayerItem 接口

是Widget实现的。

而Widget 的方法与LayerNode 中找焦点的办法一样

同样的两个for,同样的回调本身

这应该算是递归吧,;)(本人基础的问题!)

	ILayerItem * Widget::getLayerItemByPoint(int _left, int _top)
	{
		// проверяем попадание
		if (!mSubSkinsVisible
			|| !mEnabled
			|| !mVisible
			|| (!mNeedMouseFocus && !mInheritsPick)
			|| !_checkPoint(_left, _top)
			// если есть маска, проверяем еще и по маске
			|| ((!mMaskPickInfo->empty()) && (!mMaskPickInfo->pick(IntPoint(_left - mCoord.left, _top - mCoord.top), mCoord))))
				return nullptr;
		// спрашиваем у детишек
		for (VectorWidgetPtr::reverse_iterator widget= mWidgetChild.rbegin(); widget != mWidgetChild.rend(); ++widget)
		{
			// общаемся только с послушными детьми
			if ((*widget)->mWidgetStyle == WidgetStyle::Popup) continue;

			ILayerItem * item = (*widget)->getLayerItemByPoint(_left - mCoord.left, _top - mCoord.top);
			if (item != nullptr) return item;
		}
		// спрашиваем у детишек скна
		for (VectorWidgetPtr::reverse_iterator widget= mWidgetChildSkin.rbegin(); widget != mWidgetChildSkin.rend(); ++widget)
		{
			ILayerItem * item = (*widget)->getLayerItemByPoint(_left - mCoord.left, _top - mCoord.top);
			if (item != nullptr) return item;
		}
		// непослушные дети
		return mInheritsPick ? nullptr : this;
	}

经过四层传回move所聚焦的Widget

好漫长啊!为什么?






<think>好的,我现在需要回答用户关于如何在安卓中使用InputManager API的问题。首先,用户提供的引用中有几个关于InputManager的内容,比如引用[1]提到了InputManager的注册信息,引用[2]涉及了NativeInputManager和start()方法,引用[4]展示了如何注入按键事件。这些应该是关键点。 首先,我需要确认InputManager的基本用法。根据安卓开发文档,InputManager主要用于监听输入设备的变化,比如键盘、触摸屏等。用户可能需要知道如何获取InputManager的实例,因为通常系统服务是通过getSystemService获取的,但InputManager可能有所不同。引用[1]显示它被注册为系统服务,但可能需要使用Context.INPUT_SERVICE来获取。 然后,引用[2]提到nativeStart()和NativeInputManager,这可能涉及到底层启动流程,但用户可能更关心应用层的使用。接下来,引用[4]中的代码展示了如何注入按键事件,这涉及到INPUT_EVENT_INJECTION_SYNC_NONE模式,可能需要特定的权限,比如INJECT_EVENTS权限,这在系统应用中常见。 用户的问题是如何使用InputManager API,可能需要分步骤说明:获取实例、监听输入设备、处理输入事件,以及可能的注入事件。但需要注意权限问题,因为普通应用可能无法注入事件,需要系统签名或特权。 另外,需要检查是否有遗漏的步骤,比如注册监听器时的注意事项,或者输入事件的处理方法。同时,用户可能对实际代码示例感兴趣,所以应该提供相关的Java或Kotlin代码片段,并解释每个部分的作用。 还要考虑用户可能的后续问题,比如性能优化、权限申请方法,或者不同安卓版本的差异。因此,回答中应该包含这些方面的提示,并在相关问题中列出,帮助用户进一步探索。 最后,确保回答结构清晰,分点说明,并正确引用提供的资料,比如引用[1]和[4],同时遵循用户给出的格式要求,比如使用中文、LaTeX格式正确等。</think>### 安卓InputManager使用方法解析 #### 1. 获取InputManager实例 InputManager属于系统级服务,需要通过`Context.getSystemService()`获取: ```java InputManager inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE); ``` 注意:部分低级别API需要系统级权限才能调用[^1] #### 2. 监听输入设备变化 注册输入设备监听器实现硬件状态监控: ```java InputManager.InputDeviceListener listener = new InputManager.InputDeviceListener() { @Override public void onInputDeviceAdded(int deviceId) { // 新设备连接处理 } @Override public void onInputDeviceRemoved(int deviceId) { // 设备断开处理 } @Override public void onInputDeviceChanged(int deviceId) { // 设备状态变化处理 } }; inputManager.registerInputDeviceListener(listener, null); ``` #### 3. 注入输入事件(需系统权限) 通过`injectInputEvent`实现事件注入,参考引用[4]示例: ```java // 注入返回键事件 public void injectBackKey() { long now = SystemClock.uptimeMillis(); // 按下事件 KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0); inputManager.injectInputEvent(downEvent, InputManager.INPUT_EVENT_INJECTION_SYNC_NONE); // 抬起事件 KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0); inputManager.injectInputEvent(upEvent, InputManager.INPUT_EVENT_INJECTION_SYNC_NONE); } ``` 需要声明权限:`<uses-permission android:name="android.permission.INJECT_EVENTS" />`[^4] #### 4. 获取输入设备信息 ```java int[] deviceIds = inputManager.getInputDeviceIds(); for (int id : deviceIds) { InputDevice device = inputManager.getInputDevice(id); String name = device.getName(); int sources = device.getSources(); } ``` #### 关键注意事项: 1. 事件注入功能需要系统级签名权限 2. API级别差异需处理版本兼容 3. 输入事件处理应考虑线程安全问题 4. 需要处理`SecurityException`等异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值