所以我尝试基于这个存储库在android studio中创建一个带有辅助功能服务的自动点击器。
我有以下这样的辅助服务配置。
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:description="@string/accessibility_service_description"
/>
这些是 kotlin 文件
FloatingClickService.kt
class FloatingClickService : Service() {
private lateinit var manager: WindowManager
private lateinit var view: View
private lateinit var clickView: View
private lateinit var params: WindowManager.LayoutParams
private lateinit var clickParams: WindowManager.LayoutParams
private var startDragDistance: Int = 0
private var timer: Timer? = null
private lateinit var btnStart: ImageButton
private lateinit var btnSetting: ImageButton
private lateinit var btnMove: ImageButton
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
startDragDistance = dp2px(10f)
manager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
view = createView(R.layout.widget)
clickView = createView(R.layout.edittext_widget)
//setting the layout parameters
params = createLayoutParams()
clickParams = createLayoutParams(true)
//getting windows services and adding the floating view to it
manager.addView(view, params)
manager.addView(clickView, clickParams)
btnStart = view.findViewById(R.id.btn_start)
btnSetting = view.findViewById(R.id.btn_setting)
btnMove = view.findViewById(R.id.btn_move)
updateView()
btnStart.setOnClickListener {
viewOnStart()
}
btnSetting.setOnClickListener {
// setting action
}
setOnTouchListener()
}
private var isOn = false
private fun createLayoutParams(custom: Boolean = false): WindowManager.LayoutParams {
val overlayParam =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
return WindowManager.LayoutParams(
if (custom) dp2px(48f) else WindowManager.LayoutParams.WRAP_CONTENT,
if (custom) dp2px(48f) else WindowManager.LayoutParams.WRAP_CONTENT,
overlayParam,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT)
}
private fun createView(layoutId: Int): View {
val view = LayoutInflater.from(this).inflate(layoutId, null)
//setting the layout parameters
params = createLayoutParams(false)
clickParams = createLayoutParams(true)
//getting windows services and adding the floating view to it
manager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
return view
}
private fun updateView() {
if (isOn) {
btnSetting.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.settings))
btnSetting.isEnabled = false
btnStart.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.pause))
} else {
btnSetting.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.setting_active))
btnSetting.isEnabled = true
btnStart.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.play))
}
}
private fun viewOnStart() {
if (isOn) {
timer?.cancel()
setOnTouchListener()
} else {
timer = fixedRateTimer(
initialDelay = 0,
period = 3000L
) {
val location = IntArray(2)
clickView.getLocationOnScreen(location)
autoClickService?.click(location[0] + clickView.right, location[1] + clickView.bottom)
}
removeTouchListener()
}
isOn = !isOn
updateView()
}
private fun removeView() {
manager.removeView(view)
manager.removeView(clickView)
}
@SuppressLint("ClickableViewAccessibility")
private fun setOnTouchListener() {
clickView.setOnTouchListener(
TouchAndDragListener(clickParams, startDragDistance) {
manager.updateViewLayout(clickView, clickParams)
}
)
btnMove.setOnTouchListener(
TouchAndDragListener(params, startDragDistance) {
manager.updateViewLayout(view, params)
}
)
}
@SuppressLint("ClickableViewAccessibility")
private fun removeTouchListener() {
clickView.setOnTouchListener(null)
btnMove.setOnTouchListener(null)
}
override fun onDestroy() {
timer?.cancel()
removeView()
super.onDestroy()
}
}
AutoClickService.kt
var autoClickService: AutoClickService? = null
class AutoClickService : AccessibilityService() {
override fun onInterrupt() {
// NO-OP
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
}
override fun onServiceConnected() {
autoClickService = this
startActivity(
Intent(this, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
fun click(x: Int, y: Int) {
val path = Path()
path.moveTo(x.toFloat(), y.toFloat())
val builder = GestureDescription.Builder()
val gestureDescription = builder
.addStroke(GestureDescription.StrokeDescription(path, 10, 10))
.build()
dispatchGesture(gestureDescription, null, null)
}
}
像这样启动服务
val serviceIntent = Intent(
this@MainActivity,
FloatingClickService::class.java
)
startService(serviceIntent)
实际结果是,点击的位置不是在视图的中心,而是在视图的右下角。如何让点击发生在clickView的中心?
我尝试获取视图的中心 x 和 y,但没有点击。我的期望是点击的图块应该与点视图相同。 (我希望点击发生在点视图的中心)
我和你有同样的问题,我也使用了Nestorm001的源代码。 所以问题是因为当我们使用 PNG(像您使用的圆形),并且在该自定义布局中添加 onTouchListener 时,该侦听器将吸收辅助功能单击操作(如果您单击视图的中心)。我们仍然可以单击 PNG 的 4 个角,因为它是透明的,这会将单击操作传递到下面的视图。
我疯了两天,终于发现问题所在了。因此,如果您想修复它,在调用 Accessibility to click 之前,您必须更新该自定义布局的布局参数,如下所示:
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; // important - disable touch event
wm.updateViewLayout(your_custom_view, layoutParams);
然后,当您想要停止自动单击时,请将标志更改为 FLAG_NOT_FOCUSABLE,以便您可以再次拖动并移动该自定义布局。
快乐编码。顺便说一句,对不起我的英语。