安卓无障碍自动点击

该文章介绍了如何在Android应用中利用无障碍服务实现自动化点击功能,包括配置无障碍服务XML,创建AccessibilityService,以及利用Databinding简化UI交互。同时,还展示了如何创建悬浮窗并监听其上的按钮点击事件,以模拟用户操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:如果你的项目名称不是"test"
这行package com.example.test就会报错改正即可
build.gradle(Project:test)

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven { url 'https://jitpack.io' }
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        repositories {
            google()
            mavenCentral()
            // 下面这两个允许引入第三方库
            jcenter()
            maven { url 'https://jitpack.io' }
        }
    }

}
rootProject.name = "test"
include ':app'

build.gradle(Module:app)

plugins {
    ···
}

android {
	···
    // 开启data binding
    dataBinding {
        enabled = true
    }
	···
}


dependencies {
    ···
}

主界面 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:id="@+id/linear"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#D65B5B"
        android:backgroundTint="#D65B5B"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">
          <Button
                android:id="@+id/btn01"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="3dp"
                android:minHeight="48dp"
                android:text="Toast" />
            <Button
                android:id="@+id/btn02"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="3dp"
                android:minHeight="48dp"
                android:text="开启无障碍" />
     

        </LinearLayout>

    </LinearLayout>
</layout>

主界面MainActivity.tk

package com.example.test

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.example.test.databinding.ActivityMainBinding
import kotlin.system.exitProcess


class MainActivity : AppCompatActivity() {
    private var id = R.layout.activity_main
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // databinding对象自己上网搜索一下即可,不用findViewByMe了
        binding = DataBindingUtil.setContentView(this, id)
        // 将自动点击该按钮
        binding.btn01.setOnClickListener{
        	print("点击了Toast")
        	Log.i("tag","点击了Toast")
        }
        binding.btn02.setOnClickListener {
            // 挑转到无障碍服务
        if (!ASHelper.isAccessibilitySettingsOn(this, TheAccessibilityService::class.java.name)) {
            ASHelper.jumpToSettingPage(this)
        }
        }
    }

}

上面需要用到的ASHelper.kt类

package com.example.test

import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.provider.Settings

object ASHelper {
    //跳转到设置页面无障碍服务开启自定义辅助功能服务
    fun jumpToSettingPage(context: Context) {
        val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        context.startActivity(intent)
    }

    //判断自定义辅助功能服务是否开启
    fun isAccessibilitySettingsOn(context: Context?, className: String): Boolean {
        if (context == null) {
            return false
        }
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        if (activityManager != null) {
            val runningServices = activityManager.getRunningServices(100) // 获取正在运行的服务列表
            if (runningServices.size < 0) {
                return false
            }
            for (i in runningServices.indices) {
                val service = runningServices[i].service
                if (service.className == className) {
                    return true
                }
            }
        }
        return false
    }
}

接下来准备一个acuton.xml文件放入layout文件夹,用于在桌面上悬浮
acuton.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="text"
            type="String" />
    </data>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <Button
            android:id="@+id/start"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="模拟点击" />
    </LinearLayout>
</layout>

配置无障碍服务的xml

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagReportViewIds"
    android:canRetrieveWindowContent="true"
    android:canPerformGestures="true"
    android:notificationTimeout="100"
    android:description="描述"
    <!-- 这个是你要操作的APP的包名比如QQ的包名就是com.tencent.mobileqq -->
    <!-- 包名都可以在网上搜索到,这里用我自己的项目包名去点击本程序 -->
    android:packageNames="com.example.test" />

配置自己的无障碍程序

package com.example.test

import android.accessibilityservice.AccessibilityService
import android.annotation.SuppressLint
import android.graphics.PixelFormat
import android.graphics.Rect
import android.os.Build
import android.os.Environment
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.Button
import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.RequiresApi
import com.alibaba.fastjson.JSON
import java.io.File


class TheAccessibilityService : AccessibilityService() {
    private lateinit var mLayout: FrameLayout

    @RequiresApi(Build.VERSION_CODES.R)
    @SuppressLint("ServiceCast")
    override fun onServiceConnected() {
        // 在屏幕顶部添加一个 View
        val wm = getSystemService(WINDOW_SERVICE) as? WindowManager
        mLayout = FrameLayout(this)
        // 一个视图对象
        val lp = WindowManager.LayoutParams().apply {
            type =
                WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY // 因为此权限才能展示处理, 只能配合 AccessibilityService 使用
            format = PixelFormat.TRANSLUCENT
            flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            width = WindowManager.LayoutParams.WRAP_CONTENT
            height = WindowManager.LayoutParams.WRAP_CONTENT
            gravity = Gravity.TOP
        }
        // 加入布局到View
        val inflater = LayoutInflater.from(this@TheAccessibilityService)
        inflater.inflate(R.layout.acuton_bar, mLayout)
        wm?.addView(mLayout, lp)
        // 获取按钮
        var start = mLayout.findViewById<Button>(R.id.start)
        // 点击事件
        start.setOnClickListener {
            try {
            	// 根据控件的文本获取节点(返回一个List)	
                var node = rootInActiveWindow?.findAccessibilityNodeInfosByText("Toast")
                // 判断是否有节点
                if (node != null && node.isNotEmpty()) {
                	// 新建矩阵
                    val rect = Rect()
                    // 根据节点位置写入矩阵内
                    node[0].getBoundsInScreen(rect)
                    // 模拟点击
                    AutoClick().clickCMD(rect, this@TheAccessibilityService)
                }
            } catch (e: java.lang.Exception) {
                Log.i("mine", "error: $e")
            }
        }
    }
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
    override fun onInterrupt() {}
}

上面用到的AutoClick.tk类

package com.example.test;

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.graphics.Point
import android.graphics.Rect
import android.os.Build
import android.util.Log
import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.RequiresApi
import java.io.OutputStream


class AutoClick {

    /**
     * 使用安卓内部cmd模拟点击,需要root权限
     */
    open fun clickCMD(rect: Rect, service: AccessibilityService) {
        var os: OutputStream? = null
        val x = (rect.left + rect.right) / 2
        val y = (rect.top + rect.bottom) / 2
        val cmd = "input tap $x $y"
        try {
            if (os == null) {
                os = Runtime.getRuntime().exec("su").outputStream
            }
            os!!.write(cmd.toByteArray())
            os!!.flush() //清空缓存
            os!!.close() //停止流
        } catch (e: Exception) {
        	// 如果没有root的设备就会异常,尝试使用无障碍手势模拟点击
            gestureClick(rect,service)
        }
    }


    /**
     * 无障碍模拟手势点击
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    open fun gestureClick(rect: Rect, service: AccessibilityService): Boolean {
        val x: Int = (rect.left + rect.right) / 2
        val y: Int = (rect.top + rect.bottom) / 2
        val point = Point(x, y)
        val builder: GestureDescription.Builder = GestureDescription.Builder()
        val path = Path()
        path.moveTo(point.x.toFloat(), point.y.toFloat())
        builder.addStroke(GestureDescription.StrokeDescription(path, 0L, 100L))
        val gesture: GestureDescription = builder.build()
        return service.dispatchGesture(
            gesture,
            object : AccessibilityService.GestureResultCallback() {
                override fun onCompleted(gestureDescription: GestureDescription?) {
                    super.onCompleted(gestureDescription)
                    Log.i("mine", "dispatchGesture onCompleted: 完成...");
                }

                override fun onCancelled(gestureDescription: GestureDescription?) {
                    super.onCancelled(gestureDescription)
                    Log.i("mine", "dispatchGesture onCancelled: 取消...");
                }
            },
            null
        )
    }

}

另外的,别忘记了在AndroidManifest.xml里面配置
canPerformGestures="true"允许执行手势

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!--    // Android6.0~Android10.0添加-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!--    // Android11.0添加-->
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

    <application
        android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews"
        canPerformGestures="true"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".TheAccessibilityService"
            android:enabled="true"
            android:exported="true"
            android:label="服务名称"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
            android:process=":BackgroundService">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_config" />
        </service>
    </application>

</manifest>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修罗_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值