手把手教你Android全局觸摸事件監(jiān)聽
Android系統(tǒng)全局觸摸事件監(jiān)聽
Android觸摸全局監(jiān)聽指的是調(diào)用監(jiān)聽后在任何界面都能獲取到觸摸事件。
要實(shí)現(xiàn)這個(gè)功能必須要修改源碼添加新的接口,因?yàn)橄到y(tǒng)默認(rèn)是不暴露這個(gè)方法的。
源碼
監(jiān)聽系統(tǒng)全局觸摸事件的類和相關(guān)代碼:
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
@Override
public void registerPointerEventListener(PointerEventListener listener, int displayId) {
Slog.i(TAG, "registerPointerEventListener PointerEventListener = " + listener);
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
displayContent.registerPointerEventListener(listener);
}
}
}
@Override
public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
displayContent.unregisterPointerEventListener(listener);
}
}
}
第一個(gè)參數(shù):是中PointerEventListener接口,
里面有MotionEvent對(duì)象含有點(diǎn)擊事件,比如DOWN、UP、MOVING等其他信息。
package android.view;
public interface WindowManagerPolicyConstants {
interface PointerEventListener {
void onPointerEvent(MotionEvent motionEvent);
}
}
第二個(gè)參數(shù),屏幕id,正常用0 ,表示主屏幕id。有些設(shè)備有投屏或者第二屏才需要關(guān)注這個(gè)。
下面介紹如何注冊(cè)這個(gè)服務(wù)
1、綁定這個(gè)系統(tǒng)服務(wù),這個(gè)方法行不通
因?yàn)檫@個(gè)服務(wù)的aidl接口IWindowManager,并沒有暴露這個(gè)方法
registerPointerEventListener方法定義在另一個(gè)內(nèi)部接口 WindowManagerFuncs 中
public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public interface WindowManagerFuncs {
/** Register a system listener for touch events */
void registerPointerEventListener(PointerEventListener listener, int displayId);
/** Unregister a system listener for touch events */
void unregisterPointerEventListener(PointerEventListener listener, int displayId);
}
}
2、獲取WindowManagerFuncs對(duì)象,該對(duì)象獲取的方式在源碼中有多種
參考:
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
public PhoneWindowManager extends AbsPhoneWindowManager implements WindowManagerPolicy, IHwPhoneWindowManagerInner{
public WindowManagerFuncs getWindowManagerFuncs(){
return mWindowManagerFuncs;
}
}
WindowManagerFuncs在源碼中是可以直接new的,使用如下:
PhoneWindowManager phoneWindowManager = new PhoneWindowManager(); WindowManagerFuncs windowManagerFuncs = phoneWindowManager.getWindowManagerFuncs(); windowManagerFuncsEx.registerPointerEventListener(listener, Display.DEFAULT_DISPLAY);
3、在華為Emui源碼添加aidl回調(diào)
WindowManagerEx有通道直接發(fā)送數(shù)據(jù)到WindowManagerService并可以進(jìn)行數(shù)據(jù)監(jiān)聽
(1)添加aidl接口
vendor\huawei\Emui\frameworks\hwCommInterface\base\core\java\com\huawei\android\app\IHwPointEventCallback.aidl
package com.huawei.android.app;
import android.view.MotionEvent;
oneway interface IHwPointEventCallback {
void onPointerEvent(in MotionEvent motionEvent);
}
(2)WindowManagerEx的修改
vendor\huawei\Emui\frameworks\hwext\hwext\framework\src\com\huawei\android\app\WindowManagerEx.java
private final int TRANSACTION_SET_POINTER_EVENT_LISTENER = android.os.IBinder.FIRST_CALL_TRANSACTION + 2100;
//給WindowManagerService傳遞監(jiān)聽對(duì)象
public static void setPointerEventListener(IHwPointEventCallback listener) {
Log.i(LOG_TAG, "setPointerEventListener listener = " + listener);
IBinder windowManagerBinder = WindowManagerGlobal.getWindowManagerService().asBinder();
if (windowManagerBinder != null) {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken("android.view.IWindowManager");
//傳遞aidl監(jiān)聽對(duì)象
data.writeStrongBinder(listener != null ? listener.asBinder() : null);
//發(fā)送
windowManagerBinder.transact(TRANSACTION_SET_POINTER_EVENT_LISTENER, data, reply, 0);
} catch (RemoteException e){
Log.e(LOG_TAG, "setPointerEventListener exception is " + e.getMessage());
} finally {
data.recycle();
reply.recycle();
}
} else {
Log.w(LOG_TAG, "setPointerEventListener windowManagerBinder is null");
}
}
(3)在WindowManagerService中接收數(shù)據(jù)并做實(shí)際監(jiān)聽
基于盡量不修改源碼的理念,Emui中有WindowManagerService的子類HwWindowManagerService,在子類中修改代碼即可。
vendor\huawei\Emui\frameworks\base\services\java\huawei\com\android\server\wm\HwWindowManagerService.java
HwWindowManagerService extends WindowManagerService
private final int TRANSACTION_SET_POINTER_EVENT_LISTENER = android.os.IBinder.FIRST_CALL_TRANSACTION + 2100;
private IHwPointEventCallback mIHwPointEventCallback = null;
//接收WindowManagerEx傳遞過來的數(shù)據(jù)
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case TRANSACTION_SET_POINTER_EVENT_LISTENER:
data.enforceInterface("android.view.IWindowManager");
IHwPointEventCallback observer = IHwPointEventCallback.Stub.asInterface(data.readStrongBinder());
setPointerEventListener(observer);
reply.writeNoException();
return true;
}
}
//在Service中創(chuàng)建唯一的監(jiān)聽對(duì)象
private PointerEventListener mPointerEventListener = new PointerEventListener() {
@Override
public void onPointerEvent(MotionEvent motionEvent) {
if(mIHwPointEventCallback != null) {
try {
mIHwPointEventCallback.onPointerEvent(motionEvent);
} catch (RemoteException e) {
Slog.e(TAG, "mIHwPointEventCallback error = " + e.getMessage());
}
}
}
};
//添加設(shè)置觸摸監(jiān)聽方法
private void setPointerEventListener(IHwPointEventCallback listener) {
Slog.i(TAG, "setPointerEventListener PointerEventListener = " + listener);
int uid = Binder.getCallingUid();
if(uid != Process.SYSTEM_UID){
Slog.e(TAG, "setPointerEventListener uid must be "+ Process.SYSTEM_UID +",but now uid = " + uid);
return;
}
mIHwPointEventCallback = listener;
if(listener != null) {
//實(shí)際調(diào)到父類的注冊(cè)觸摸事件的方法
registerPointerEventListener(mPointerEventListener, Display.DEFAULT_DISPLAY);
}
else {
//實(shí)際調(diào)到父類的反注冊(cè)觸摸事件的方法
unregisterPointerEventListener(mPointerEventListener, Display.DEFAULT_DISPLAY);
}
}
方法3可以實(shí)現(xiàn)在普通app中監(jiān)聽到系統(tǒng)的全局觸摸事件,
因?yàn)閍pp可以依賴Emui的emui_addons.jar,
調(diào)用到里面的部分類,比如WindowManagerEx,就可以監(jiān)聽全局觸摸事件。
其他系統(tǒng)環(huán)境可以根據(jù)實(shí)際情況參考上面的實(shí)現(xiàn)。
共勉:看得更多才知道還有更多還沒看過。
到此這篇關(guān)于手把手教你Android全局觸摸事件監(jiān)聽的文章就介紹到這了,更多相關(guān)Android觸摸監(jiān)聽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解
這篇文章主要為大家介紹了Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Android?14新功能HighLights快速實(shí)現(xiàn)文本高亮
這篇文章主要為大家介紹了Android?14新功能HighLights快速實(shí)現(xiàn)文本高亮示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
android特賣列表倒計(jì)時(shí)卡頓問題的解決方法
這篇文章主要為大家詳細(xì)介紹了android特賣列表倒計(jì)時(shí)卡頓問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Android dip,px,pt,sp 的區(qū)別詳解
本篇文章是對(duì)Android中dip,px,pt,sp的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁效果實(shí)例代碼
本篇文章主要介紹了android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁效果實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
RxJava加Retrofit文件分段上傳實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了RxJava加Retrofit文件分段上傳實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
android 獲取文件的擴(kuò)展名和去掉文件擴(kuò)展名的小例子
android 獲取文件的擴(kuò)展名和去掉文件擴(kuò)展名的小例子,需要的朋友可以參考一下2013-06-06
Android項(xiàng)目實(shí)戰(zhàn)(二十八):使用Zxing實(shí)現(xiàn)二維碼及優(yōu)化實(shí)例
這篇文章主要介紹了Android項(xiàng)目實(shí)戰(zhàn)(二十八):使用Zxing實(shí)現(xiàn)二維碼及優(yōu)化實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11
Android實(shí)現(xiàn)讀取掃碼槍內(nèi)容(條形碼)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)讀取掃碼槍內(nèi)容、條形碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

