我是靠谱客的博主 务实睫毛膏,这篇文章主要介绍Android 系统鼠标1. 自定义鼠标样式2. 控制类:3. 鼠标样式更新: ViewRootImpl4. 自定义鼠标样式案例,现在分享给大家,希望可以做个参考。

Base on AOSP Android 11.

文章目录

  • 1. 自定义鼠标样式
  • 2. 控制类:
    • 1. 代表类: PointerIcon.java
    • 2. 控制类: PointerController
  • 3. 鼠标样式更新: ViewRootImpl
  • 4. 自定义鼠标样式案例
    • 1. 通过重写onResolvePointerIcon
      • 1. TextView
      • 2. Button
    • 2. 在xml中直接设置

1. 自定义鼠标样式

定义了鼠标样式对应事件下的显示样式.

  1. 系统资源路径: frameworks/base/core/res/res/
  2. 样式定义路径: frameworks/base/core/res/res/values/styles.xml

2. 控制类:

1. 代表类: PointerIcon.java

frameworks/base/core/java/android/view/PointerIcon.java

PointerIcon代表了系统的鼠标样式, 在方法 中定义了几乎所有需要用到的样式:

复制代码
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
52
53
54
55
56
57
private static int getSystemIconTypeIndex(int type) { switch (type) { case TYPE_ARROW: return com.android.internal.R.styleable.Pointer_pointerIconArrow; case TYPE_SPOT_HOVER: return com.android.internal.R.styleable.Pointer_pointerIconSpotHover; case TYPE_SPOT_TOUCH: return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch; case TYPE_SPOT_ANCHOR: return com.android.internal.R.styleable.Pointer_pointerIconSpotAnchor; case TYPE_HAND: return com.android.internal.R.styleable.Pointer_pointerIconHand; case TYPE_CONTEXT_MENU: return com.android.internal.R.styleable.Pointer_pointerIconContextMenu; case TYPE_HELP: return com.android.internal.R.styleable.Pointer_pointerIconHelp; case TYPE_WAIT: return com.android.internal.R.styleable.Pointer_pointerIconWait; case TYPE_CELL: return com.android.internal.R.styleable.Pointer_pointerIconCell; case TYPE_CROSSHAIR: return com.android.internal.R.styleable.Pointer_pointerIconCrosshair; case TYPE_TEXT: return com.android.internal.R.styleable.Pointer_pointerIconText; case TYPE_VERTICAL_TEXT: return com.android.internal.R.styleable.Pointer_pointerIconVerticalText; case TYPE_ALIAS: return com.android.internal.R.styleable.Pointer_pointerIconAlias; case TYPE_COPY: return com.android.internal.R.styleable.Pointer_pointerIconCopy; case TYPE_ALL_SCROLL: return com.android.internal.R.styleable.Pointer_pointerIconAllScroll; case TYPE_NO_DROP: return com.android.internal.R.styleable.Pointer_pointerIconNodrop; case TYPE_HORIZONTAL_DOUBLE_ARROW: return com.android.internal.R.styleable.Pointer_pointerIconHorizontalDoubleArrow; case TYPE_VERTICAL_DOUBLE_ARROW: return com.android.internal.R.styleable.Pointer_pointerIconVerticalDoubleArrow; case TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW: return com.android.internal.R.styleable. Pointer_pointerIconTopRightDiagonalDoubleArrow; case TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW: return com.android.internal.R.styleable. Pointer_pointerIconTopLeftDiagonalDoubleArrow; case TYPE_ZOOM_IN: return com.android.internal.R.styleable.Pointer_pointerIconZoomIn; case TYPE_ZOOM_OUT: return com.android.internal.R.styleable.Pointer_pointerIconZoomOut; case TYPE_GRAB: return com.android.internal.R.styleable.Pointer_pointerIconGrab; case TYPE_GRABBING: return com.android.internal.R.styleable.Pointer_pointerIconGrabbing; default: return 0; } }

2. 控制类: PointerController

工程路径: frameworks/base/libs/input/

1. PointerController.cpp:
控制当前鼠标的坐标位置, 更新和动画等(横竖屏切换的坐标转换也在这里).
Input系统中对于鼠标坐标的上报也是从这里获取(getPosition接口)的.

PointerController中对应于时间更新的接口是:PointerControllerInterface
frameworks/native/services/inputflinger/include/PointerControllerInterface.h
这个接口由InputFlinger使用(更新, 获取等)

PointerController是PointerControllerInterface的具体实现类.

_1.1. 鼠标的控制接口

复制代码
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//设置窗口大小,鼠标会在这个范围内可用 void PointerController::setDisplayViewport(const DisplayViewport& viewport) { AutoMutex _l(mLock); if (viewport == mLocked.viewport) { return; } const DisplayViewport oldViewport = mLocked.viewport; mLocked.viewport = viewport; int32_t oldDisplayWidth, oldDisplayHeight; getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); int32_t newDisplayWidth, newDisplayHeight; getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); // Reset cursor position to center if size or display changed. if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth || oldDisplayHeight != newDisplayHeight) { float minX, minY, maxX, maxY; if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { mLocked.pointerX = (minX + maxX) * 0.5f; mLocked.pointerY = (minY + maxY) * 0.5f; // Reload icon resources for density may be changed. loadResourcesLocked(); } else { mLocked.pointerX = 0; mLocked.pointerY = 0; } fadeOutAndReleaseAllSpotsLocked(); } else if (oldViewport.orientation != viewport.orientation) { //横竖屏切换的时候,会转换鼠标在屏幕上的位置计算参照 // Apply offsets to convert from the pixel top-left corner position to the pixel center. // This creates an invariant frame of reference that we can easily rotate when // taking into account that the pointer may be located at fractional pixel offsets. float x = mLocked.pointerX + 0.5f; float y = mLocked.pointerY + 0.5f; float temp; // Undo the previous rotation. switch (oldViewport.orientation) { case DISPLAY_ORIENTATION_90: temp = x; x = oldViewport.deviceHeight - y; y = temp; break; case DISPLAY_ORIENTATION_180: x = oldViewport.deviceWidth - x; y = oldViewport.deviceHeight - y; break; case DISPLAY_ORIENTATION_270: temp = x; x = y; y = oldViewport.deviceWidth - temp; break; } // Perform the new rotation. switch (viewport.orientation) { case DISPLAY_ORIENTATION_90: temp = x; x = y; y = viewport.deviceHeight - temp; break; case DISPLAY_ORIENTATION_180: x = viewport.deviceWidth - x; y = viewport.deviceHeight - y; break; case DISPLAY_ORIENTATION_270: temp = x; x = viewport.deviceWidth - y; y = temp; break; } // Apply offsets to convert from the pixel center to the pixel top-left corner position // and save the results. mLocked.pointerX = x - 0.5f; mLocked.pointerY = y - 0.5f; } updatePointerLocked(); } //鼠标移动更新, 这些都是由对应device的Mapper来处理的. void PointerController::move(float deltaX, float deltaY) { #if DEBUG_POINTER_UPDATES ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); #endif if (deltaX == 0.0f && deltaY == 0.0f) { return; } AutoMutex _l(mLock); setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); } //获取当前鼠标的位置 //注意: 这个会被InputReader当作原始做坐标数据上报,界面上的鼠标事件的坐标都是来自于这里, 在move之后调用 void PointerController::getPosition(float* outX, float* outY) const { AutoMutex _l(mLock); *outX = mLocked.pointerX; *outY = mLocked.pointerY; }

2. SpriteController.cpp:
管理鼠标的图层, 显示图标样式和显示的样式变换等.
!!鼠标的图层是单独的, 一般的设备都是从硬件对鼠标的图层进行支持(也是谷歌的官方要求),在所有内容的最上层.

_2.1. 鼠标图层的初始化:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) { ensureSurfaceComposerClient(); sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface( String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden | ISurfaceComposerClient::eCursorWindow); //Flag eCursorWindow是必须的, //标记是鼠标图层,硬件就会将该图层特别处理. if (surfaceControl == NULL || !surfaceControl->isValid()) { ALOGE("Error creating sprite surface."); return NULL; } return surfaceControl; }

3. 鼠标样式更新: ViewRootImpl

鼠标样式具体更新的操作是在View系统的根节点ViewRootImpl中:

android.view.ViewRootImpl.ViewPostImeInputStage#onProcess

复制代码
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { //Pointer事件包括鼠标点击和触摸点击 return processPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q); } else { return processGenericMotionEvent(q); } } } //processPointerEvent->maybeUpdatePointerIcon private void maybeUpdatePointerIcon(MotionEvent event) { if (event.getPointerCount() == 1 && event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { // Other apps or the window manager may change the icon type outside of // this app, therefore the icon type has to be reset on enter/exit event. mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; } if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { //针对鼠标事件, 更新POinterIcon if (!updatePointerIcon(event) && event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { mPointerIconType = PointerIcon.TYPE_NOT_SPECIFIED; } } } } private boolean updatePointerIcon(MotionEvent event) { final int pointerIndex = 0; final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); if (mView == null) { // E.g. click outside a popup to dismiss it Slog.d(mTag, "updatePointerIcon called after view was removed"); return false; } if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) { // E.g. when moving window divider with mouse Slog.d(mTag, "updatePointerIcon called with position out of bounds"); return false; } /// // 这里开始调用View Tree的onResolvePointerIcon方法, 确定在每个view下鼠标应该显示的样式图片. // 通过返回PointerIcon对象给系统用, 可以自定义样式图片, 也可以获取系统的样式. // 默认是黑色白边的箭头样式; /// final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); final int pointerType = (pointerIcon != null) ? pointerIcon.getType() : PointerIcon.TYPE_DEFAULT; if (mPointerIconType != pointerType) { mPointerIconType = pointerType; mCustomPointerIcon = null; if (mPointerIconType != PointerIcon.TYPE_CUSTOM) { InputManager.getInstance().setPointerIconType(pointerType); return true; } } if (mPointerIconType == PointerIcon.TYPE_CUSTOM && !pointerIcon.equals(mCustomPointerIcon)) { mCustomPointerIcon = pointerIcon; InputManager.getInstance().setCustomPointerIcon(mCustomPointerIcon); } return true; } // 默认是箭头样式的 // android.view.View#onResolvePointerIcon public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) { return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW); } return mPointerIcon; }

4. 自定义鼠标样式案例

1. 通过重写onResolvePointerIcon

自定义箭头样式, 只需要在View中重写 onResolvePointerIcon, 闭关返回需要自定义的PointerIcon对象即可.
我们看看系统中的几个View的样式设置:

1. TextView

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { if (mSpannable != null && mLinksClickable) { final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); final int offset = getOffsetForPosition(x, y); final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset, ClickableSpan.class); if (clickables.length > 0) { //对于Spannable的类型, 链接位置会编程"小手"样式 return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND); } } if (isTextSelectable() || isTextEditable()) { // 对于文本的,会变成"文本"样式(就是竖线样子) return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_TEXT); } return super.onResolvePointerIcon(event, pointerIndex); }

2. Button

复制代码
1
2
3
4
5
6
7
8
9
@Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { if (getPointerIcon() == null && isClickable() && isEnabled()) { // 按钮会变成"小手"的样子 return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); }

2. 在xml中直接设置

如: android:pointerIcon="hand"

代码案例来自源码 development/samples/ApiDemos/

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- development/samples/ApiDemos/res/layout/pointer_types.xml --> <Button android:id="@+id/pointer_type_view_hand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pointer_type_hand" android:pointerIcon="hand" /> <!--显示hand --> <Button android:id="@+id/pointer_type_view_context_menu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pointer_type_context_menu" android:pointerIcon="context_menu" /> <!--显示context_menu --> <Button android:id="@+id/pointer_type_view_custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pointer_type_custom_resource" android:pointerIcon="@drawable/custom_pointer_icon" /> <!--显示自定义样式 -->

最后

以上就是务实睫毛膏最近收集整理的关于Android 系统鼠标1. 自定义鼠标样式2. 控制类:3. 鼠标样式更新: ViewRootImpl4. 自定义鼠标样式案例的全部内容,更多相关Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(77)

评论列表共有 0 条评论

立即
投稿
返回
顶部