概述
Base on AOSP Android 11.
文章目录
- 1. 自定义鼠标样式
- 2. 控制类:
- 1. 代表类: PointerIcon.java
- 2. 控制类: PointerController
- 3. 鼠标样式更新: ViewRootImpl
- 4. 自定义鼠标样式案例
- 1. 通过重写onResolvePointerIcon
- 1. TextView
- 2. Button
- 2. 在xml中直接设置
1. 自定义鼠标样式
定义了鼠标样式对应事件下的显示样式.
- 系统资源路径: frameworks/base/core/res/res/
- 样式定义路径: frameworks/base/core/res/res/values/styles.xml
2. 控制类:
1. 代表类: PointerIcon.java
frameworks/base/core/java/android/view/PointerIcon.java
PointerIcon代表了系统的鼠标样式, 在方法 中定义了几乎所有需要用到的样式:
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. 鼠标的控制接口
//设置窗口大小,鼠标会在这个范围内可用
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. 鼠标图层的初始化:
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
@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
@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
@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/
<!-- 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 系统鼠标1. 自定义鼠标样式2. 控制类:3. 鼠标样式更新: ViewRootImpl4. 自定义鼠标样式案例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复