本文基于Android8.1系统编写
一、背景
今天想实现自定义的back和home键,比着葫芦画瓢,把back和home键的部局抄过来了。但怎么也没找到back和home键的java功能实现部分。
后来度娘了一下才找到实现的原理。
参考链接:http://blog.chinaunix.net/uid-701715-id-3054850.html
自己也记录一下
二、按钮的组件类KeyButtonView
在部局中可以看到,back和home键用了一个自定义view,开始我还以为是为了实现按钮的动画效果才自定义了一个这个view,现在发现自己天真了!
KeyButtonView的核心在于,他定义了一个可设置的部局属性keyCode,用于设置按钮对应的键,如下back键的部局代码:
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/back"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:layout_weight="0"
systemui:keyCode="4"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_back"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/back"
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:layout_weight="0"
systemui:keyCode="4"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_back"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
keyCode=”4″对应的为KeyEvent.KEYCODE_BACK键
在KeyButtonView的构造函数中,读取并保存了这个属性:
Java
public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
//...省略前面的代码
mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
//...省略后面的代码
}
1
2
3
4
5
6
7
publicKeyButtonView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs);
//...省略前面的代码
mCode=a.getInteger(R.styleable.KeyButtonView_keyCode,0);
//...省略后面的代码
}
如此,在部局文件中设置的keyCode读到了类中
三、模拟发送按键
原理是,拦截onTouch事件,当按钮按下时发送keyCode的ACTION_DOWN操作,当按钮弹开时发送keyCode的ACTION_UP操作,完成一次按键的操作。
Java
public boolean onTouchEvent(MotionEvent ev) {
//...省略部分代码
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
setPressed(true);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
playSoundEffect(SoundEffectConstants.CLICK);
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
case MotionEvent.ACTION_MOVE:
//...省略
case MotionEvent.ACTION_CANCEL:
//...省略
break;
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
// Always send a release ourselves because it doesn't seem to be sent elsewhere
// and it feels weird to sometimes get a release haptic and other times not.
if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if (mCode != 0) {
if (doIt) {
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
} else {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
} else {
// no key code, just a regular ImageView
if (doIt && mOnClickListener != null) {
mOnClickListener.onClick(this);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
}
removeCallbacks(mCheckLongPress);
break;
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
publicbooleanonTouchEvent(MotionEventev){
//...省略部分代码
switch(action){
caseMotionEvent.ACTION_DOWN:
mDownTime=SystemClock.uptimeMillis();
mLongClicked=false;
setPressed(true);
if(mCode!=0){
sendEvent(KeyEvent.ACTION_DOWN,0,mDownTime);
}else{
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
playSoundEffect(SoundEffectConstants.CLICK);
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress,ViewConfiguration.getLongPressTimeout());
break;
caseMotionEvent.ACTION_MOVE:
//...省略
caseMotionEvent.ACTION_CANCEL:
//...省略
break;
caseMotionEvent.ACTION_UP:
finalbooleandoIt=isPressed()&&!mLongClicked;
setPressed(false);
// Always send a release ourselves because it doesn't seem to be sent elsewhere
// and it feels weird to sometimes get a release haptic and other times not.
if((SystemClock.uptimeMillis()-mDownTime)>150&&!mLongClicked){
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if(mCode!=0){
if(doIt){
sendEvent(KeyEvent.ACTION_UP,0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}else{
sendEvent(KeyEvent.ACTION_UP,KeyEvent.FLAG_CANCELED);
}
}else{
// no key code, just a regular ImageView
if(doIt&&mOnClickListener!=null){
mOnClickListener.onClick(this);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
}
removeCallbacks(mCheckLongPress);
break;
}
returntrue;
}
sendEvent函数,先构造KeyEvent对象,然后通过InputManager发送到系统,代码如下:
Java
void sendEvent(int action, int flags, long when) {
//...省略部分代码
final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
1
2
3
4
5
6
7
8
9
10
voidsendEvent(intaction,intflags,longwhen){
//...省略部分代码
finalintrepeatCount=(flags&KeyEvent.FLAG_LONG_PRESS)!=0?1:0;
finalKeyEventev=newKeyEvent(mDownTime,when,action,mCode,repeatCount,
0,KeyCharacterMap.VIRTUAL_KEYBOARD,0,
flags|KeyEvent.FLAG_FROM_SYSTEM|KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
打赏
微信扫一扫,打赏作者吧~