Android 11 触摸小圆点显示流程

在开发者选项中,打开 “显示点按操作反馈” 开关,当我们在触摸屏幕时,会显示一个小圆点,来分析下小圆点的显示流程。
操作这个开关时,其实就是操作Settings数据库中的 SHOW_TOUCHES

//packages\apps\Settings\src\com\android\settings\development\ShowTapsPreferenceController.java
@Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean isEnabled = (Boolean) newValue;
        Settings.System.putInt(mContext.getContentResolver(),
                Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
        return true;
    }

而在IMS中,会对 SHOW_TOUCHES 这个key进行监听

//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private void registerShowTouchesSettingObserver() {
        mContext.getContentResolver().registerContentObserver(
                Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
                new ContentObserver(mHandler) {
                    @Override
                    public void onChange(boolean selfChange) {
                        updateShowTouchesFromSettings();
                    }
                }, UserHandle.USER_ALL);
    }

当SHOW_TOUCHES 这个key的值有改变时,调用updateShowTouchesFromSettings方法,在updateShowTouchesFromSettings方法中,是调用nativeSetShowTouches这个native方法,直接来看下这个方法

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeSetShowTouches(JNIEnv* /* env */,
        jclass /* clazz */, jlong ptr, jboolean enabled) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    im->setShowTouches(enabled);//调用NativeInputManager的setShowTouches方法
}

void NativeInputManager::setShowTouches(bool enabled) {
    { // acquire lock
        AutoMutex _l(mLock);

        if (mLocked.showTouches == enabled) {
            return;
        }

        ALOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled");
        mLocked.showTouches = enabled;//1
    } // release lock

    mInputManager->getReader()->requestRefreshConfiguration(
            InputReaderConfiguration::CHANGE_SHOW_TOUCHES);//2
}

注释1处mLocked.showTouches就为传进来的enabled(打开为true,关闭为false),注释2处调用InputReader的requestRefreshConfiguration继续处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::requestRefreshConfiguration(uint32_t changes) {
    AutoMutex _l(mLock);

    if (changes) {
        bool needWake = !mConfigurationChangesToRefresh;
        mConfigurationChangesToRefresh |= changes;//changes为InputReaderConfiguration::CHANGE_SHOW_TOUCHES

        if (needWake) {
            mEventHub->wake();//唤醒InputReader线程
        }
    }
}

InputReader线程被唤醒,loopOnce继续执行

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
    //省略
    { // acquire lock
        AutoMutex _l(mLock);
        
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);//1
        } else if (mNextTimeout != LLONG_MAX) {
           //省略
        }
    } // release lock

	//省略

注释1处,调用refreshConfigurationLocked继续处理

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::refreshConfigurationLocked(uint32_t changes) {
    mPolicy->getReaderConfiguration(&mConfig);//1
    mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);

    if (changes) {
       //省略
        if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
            mEventHub->requestReopenDevices();
        } else {
            for (auto& devicePair : mDevices) {
                std::shared_ptr<InputDevice>& device = devicePair.second;
                device->configure(now, &mConfig, changes);//2
            }
        }
    }
}

注释1处主要是将前面的mLocked.showTouches又赋值给mConfig的showTouches,注释2处调用InputDevice的configure继续处理。
1,getReaderConfiguration

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
	//省略

	{ // acquire lock
        AutoMutex _l(mLock);
		//省略

        outConfig->showTouches = mLocked.showTouches;//赋值
		
		//省略
    } //  release lock
}

2,InputDevice:configure

//frameworks\native\services\inputflinger\reader\InputDevice.cpp
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                            uint32_t changes) {
	//省略
	for_each_mapper([this, when, config, changes](InputMapper& mapper) {
            mapper.configure(when, config, changes);
            mSources |= mapper.getSources();
        });
	//省略

}

对于InputReaderConfiguration::CHANGE_SHOW_TOUCHES,又调用对应mapper的configure处理。多指触摸的话mapper为MultiTouchInputMapper,MultiTouchInputMapper没有实现configure方法,在其父类TouchInputMapper中实现

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                 uint32_t changes) {
    //省略
    if (!changes ||
        (changes &
         (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
          InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
          InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
          InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
        // Configure device sources, surface dimensions, orientation and
        // scaling factors.
        configureSurface(when, &resetNeeded);
    }
	//省略

调用configureSurface继续处理

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
	//省略
	// Create pointer controller if needed.
    if (mDeviceMode == DEVICE_MODE_POINTER ||
        (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
        if (mPointerController == nullptr) {
            mPointerController = getContext()->getPointerController(getDeviceId());
        }
    } else {
        mPointerController.clear();
    }
	//省略
}

getPointerController方法最终调用InputReader的getPointerControllerLocked方法

//frameworks\native\services\inputflinger\reader\InputReader.cpp
sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) {
    sp<PointerControllerInterface> controller = mPointerController.promote();
    if (controller == nullptr) {
        controller = mPolicy->obtainPointerController(deviceId);//1
        mPointerController = controller;
        updatePointerDisplayLocked();//2
    }
    return controller;
}

注释1处创建PointerController对象,注释2处调用updatePointerDisplayLocked继续处理
1,obtainPointerController

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
    ATRACE_CALL();
    AutoMutex _l(mLock);

    sp<PointerController> controller = mLocked.pointerController.promote();
    if (controller == nullptr) {
        ensureSpriteControllerLocked();//创建SpriteController对象

        controller = new PointerController(this, mLooper, mLocked.spriteController);//创建PointerController对象
        mLocked.pointerController = controller;
        updateInactivityTimeoutLocked();
    }

    return controller;
}

2,updatePointerDisplayLocked

//frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::updatePointerDisplayLocked() {
    sp<PointerControllerInterface> controller = mPointerController.promote();//取出前面创建的PointerController对象
    if (controller == nullptr) {
        return;
    }

   //省略

    controller->setDisplayViewport(*viewport);
}

来看一下PointerController的setDisplayViewport方法

frameworks\base\libs\input\PointerController.cpp
void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
	//省略
		loadResourcesLocked();//加载小圆点图片资源
	//省略

	updatePointerLocked();//保存资源信息到SpriteController中,其中包含了小圆点图片的bitmap

}

loadResourcesLocked加载图片信息,主要是通过JNI调用,调用到PointerIcon中的getSystemIcon方法中

//frameworks\base\core\java\android\view\PointerIcon.java
public static PointerIcon getSystemIcon(@NonNull Context context, int type) {
       //省略
        int typeIndex = getSystemIconTypeIndex(type);
        if (typeIndex == 0) {
            typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT);
        }

        int defStyle = sUseLargeIcons ?
                com.android.internal.R.style.LargePointer : com.android.internal.R.style.Pointer;
        TypedArray a = context.obtainStyledAttributes(null,
                com.android.internal.R.styleable.Pointer,
                0, defStyle);
        int resourceId = a.getResourceId(typeIndex, -1);
        a.recycle();
        
        icon = new PointerIcon(type);
        if ((resourceId & 0xff000000) == 0x01000000) {
            icon.mSystemIconResourceId = resourceId;//设置ID
        } else {
            icon.loadResource(context, context.getResources(), resourceId);
        }
        systemIcons.append(type, icon);
        return icon;
    }

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;
	//省略
}

可以看出,加载的资源ID为pointer_spot_touch_icon

//frameworks\base\core\res\res\drawable\pointer_spot_touch_icon.xml
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
    android:bitmap="@drawable/pointer_spot_touch"
    android:hotSpotX="16dp"
    android:hotSpotY="16dp" />

小圆点的图片资源已经被加载,并将其保存在相应的变量中了,接下来就需要将其显示出来了。当有触摸数据产生时,InputReader在读取到数据,然后会调用到cookAndDispatch来处理原始数据

//frameworks\native\services\inputflinger\reader\mapper\TouchInputMapper.cpp
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
	//省略
	if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches &&
            mPointerController != nullptr) {//如果需要显示小圆点
            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);

            mPointerController->setButtonState(mCurrentRawState.buttonState);
            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
                                         mCurrentCookedState.cookedPointerData.idToIndex,
                                         mCurrentCookedState.cookedPointerData.touchingIdBits,
                                         mViewport.displayId);
        }
	//省略
}

主要是通过调用setSpots去处理,在setSpots函数中,主要是先创建一个Spot对象,然后调用其updateSprite方法去请求更新小圆点

//frameworks\base\libs\input\PointerController.cpp
void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
        int32_t displayId) {
    sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
    sprite->setAlpha(alpha);
    sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
    sprite->setPosition(x, y);
    sprite->setDisplayId(displayId);
    this->x = x;
    this->y = y;

    if (icon != lastIcon) {
        lastIcon = icon;
        if (icon) {
            sprite->setIcon(*icon);
            sprite->setVisible(true);//显示
        } else {
            sprite->setVisible(false);
        }
    }
}

接下来的流程,主要是通过发送发送MSG_UPDATE_SPRITES消息,然后调用doUpdateSprites来更新绘制小圆点

//frameworks\base\libs\input\SpriteController.cpp
void SpriteController::doUpdateSprites() {
	//省略
	if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
                && update.state.wantSurfaceVisible()) {
            sp<Surface> surface = update.state.surfaceControl->getSurface();
            ANativeWindow_Buffer outBuffer;
            status_t status = surface->lock(&outBuffer, NULL);
            if (status) {
                ALOGE("Error %d locking sprite surface before drawing.", status);
            } else {
                graphics::Paint paint;
                paint.setBlendMode(ABLEND_MODE_SRC);

                graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace());
                canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);

                const int iconWidth = update.state.icon.bitmap.getInfo().width;
                const int iconHeight = update.state.icon.bitmap.getInfo().height;

                if (outBuffer.width > iconWidth) {
                    paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
                    canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint);
                }
                if (outBuffer.height > iconHeight) {
                    paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
                    canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint);
                }

                status = surface->unlockAndPost();

		//省略
}

总结
触摸圆点的显示逻辑都是在InputReader线程中完成。以下为显示的流程图
在这里插入图片描述

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android ProgressBar 中添加小圆点,可以使用自定义 Drawable 的方式来实现。以下是一个简单的实现步骤: 1. 在 drawable 文件夹下创建一个名为 custom_progress_bar.xml 的文件,用于定义进度条的样式。与上面的示例不同,这里需要添加一个小圆点的样式: ```xml <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 进度条背景 --> <item android:id="@android:id/background"> <shape> <corners android:radius="5dp" /> <gradient android:startColor="#cfcfcf" android:endColor="#cfcfcf" android:angle="270"/> </shape> </item> <!-- 进度条 --> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dp" /> <gradient android:startColor="#FF0000" android:centerColor="#FF9900" android:endColor="#FFFF00" android:angle="270" /> </shape> </clip> </item> <!-- 小圆点 --> <item android:id="@android:id/progress"> <shape android:shape="oval"> <size android:width="10dp" android:height="10dp" /> <solid android:color="#FFFF00" /> </shape> </item> </layer-list> ``` 这里添加了一个形状为圆形的小圆点,颜色与进度条的当前颜色相同,大小为 10dp。 2. 在代码中获取 ProgressBar 控件,并设置进度: ```java ProgressBar progressBar = findViewById(R.id.progressBar); // 设置进度条进度 progressBar.setProgress(50); ``` 3. 如果需要将小圆点放置在进度条的当前进度位置,可以使用 setProgressDrawable 方法将自定义 Drawable 设置为进度条的样式,并在 OnProgressChangedListener 中获取当前进度位置,然后动态设置小圆点的位置。具体实现如下: ```java // 获取自定义 Drawable Drawable customDrawable = getResources().getDrawable(R.drawable.custom_progress_bar); // 设置自定义 Drawable progressBar.setProgressDrawable(customDrawable); // 添加进度改变监听器 progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // 获取进度条宽度 int width = seekBar.getWidth(); // 获取自定义 Drawable Drawable customDrawable = seekBar.getProgressDrawable(); if (customDrawable != null && customDrawable instanceof LayerDrawable) { // 获取进度条 LayerDrawable LayerDrawable layerDrawable = (LayerDrawable) customDrawable; // 获取小圆点 Drawable Drawable thumbDrawable = layerDrawable.findDrawableByLayerId(android.R.id.progress); if (thumbDrawable != null) { // 计算小圆点位置 int thumbX = (int) (width * (float) progress / seekBar.getMax()); int thumbY = thumbDrawable.getIntrinsicHeight() / 2; // 设置小圆点位置 thumbDrawable.setBounds(thumbX, 0, thumbX + thumbDrawable.getIntrinsicWidth(), thumbDrawable.getIntrinsicHeight()); } } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); ``` 这样就可以在 Android ProgressBar 中添加小圆点,并将其放置在当前进度位置了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值