ps: 不想看代码的滑到最下面有apk包百度网盘下载地址 1. 先看效果图 不然都是耍流氓 2.项目目录 3.一些配置 build.gradle plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' } android { compileSdkVersion 31 buildToolsVersion "30.0.3" defaultConfig { applicationId "com.znan.autoclick" minSdkVersion 24 targetSdkVersion 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' //协程 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3" }accessibility.xml <?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackAllMask" android:canPerformGestures="true" android:canRetrieveWindowContent="true" android:description="@string/accessibility_desc" />AndroidManifest.xml 注册权限和服务 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="" package="com.znan.autoclick"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AutoClick"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".AutoClickService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility" /> </service> </application> </manifest>4.代码 AutoClickService.kt 无障碍服务 class AutoClickService : AccessibilityService() { private val TAG = javaClass.canonicalName var mainScope: CoroutineScope? = null private val broadcastReceiver = BroadcastHandler(this) //点击间隔 private var mInterval = -1L //点击坐标xy private var mPointX = -1f private var mPointY = -1f //悬浮窗视图 private lateinit var mFloatingView: FloatingClickView companion object { //打开悬浮窗 val ACTION_SHOW = "action_show" //自动点击事件 开启/关闭 val ACTION_PLAY = "action_play" val ACTION_STOP = "action_stop" //关闭悬浮窗 val ACTION_CLOSE = "action_close" } private inner class BroadcastHandler(val context: Context) : BroadcastReceiver() { fun register() { context.registerReceiver( this, IntentFilter().apply { addAction(BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK) //息屏关闭自动点击事件 addAction(Intent.ACTION_SCREEN_OFF) } ) } fun unregister() { context.unregisterReceiver(this) } override fun onReceive(p0: Context?, intent: Intent?) { intent?.apply { when(action) { Intent.ACTION_SCREEN_OFF -> { mFloatingView.remove() mainScope?.cancel() } BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK -> { when (getStringExtra(BroadcastConstants.KEY_ACTION)) { ACTION_SHOW -> { mFloatingView.remove() mainScope?.cancel() mInterval = getLongExtra(BroadcastConstants.KEY_INTERVAL, 5000) mFloatingView.show() } ACTION_PLAY -> { mPointX = getFloatExtra(BroadcastConstants.KEY_POINT_X, 0f) mPointY = getFloatExtra(BroadcastConstants.KEY_POINT_Y, 0f) mainScope = MainScope() autoClickView(mPointX, mPointY) } ACTION_STOP -> { mainScope?.cancel() } ACTION_CLOSE -> { mFloatingView.remove() mainScope?.cancel() } else -> { Log.e(TAG, "action error") } } } } } } } override fun onCreate() { super.onCreate() startForegroundNotification() mFloatingView = FloatingClickView(this) broadcastReceiver.register() } private fun startForegroundNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationBuilder = NotificationCompat.Builder(this, NotificationConstants.CHANNEL_ID) val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setCategory(Notification.CATEGORY_SERVICE) .build() startForeground(-1, notification) } else { startForeground(-1, Notification()) } } @RequiresApi(Build.VERSION_CODES.N) private fun autoClickView(x: Float, y: Float) { mainScope?.launch { while (true) { delay(mInterval) Log.d(TAG, "auto click x:$x y:$y") val path = Path() path.moveTo(x, y) val gestureDescription = GestureDescription.Builder() .addStroke(GestureDescription.StrokeDescription(path, 100L, 100L)) .build() dispatchGesture( gestureDescription, object : AccessibilityService.GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription?) { super.onCompleted(gestureDescription) Log.d(TAG, "自动点击完成") } override fun onCancelled(gestureDescription: GestureDescription?) { super.onCancelled(gestureDescription) Log.d(TAG, "自动点击取消") } }, null ) } } } override fun onInterrupt() { } override fun onAccessibilityEvent(event: AccessibilityEvent?) { } override fun onDestroy() { super.onDestroy() broadcastReceiver.unregister() mainScope?.cancel() } }悬浮窗 SingletonHolder.kt open class SingletonHolder<out T, in A>(creator: (A) -> T) { private var creator: ((A) -> T)? = creator @Volatile private var instance: T? = null fun getInstance(arg: A): T { val i = instance if (i != null) { return i } return synchronized(this) { val i2 = instance if (i2 != null) { i2 } else { val created = creator!!(arg) instance = created creator = null created } } } }FloatingManager.kt import android.content.Context import android.view.View import android.view.WindowManager class FloatingManager private constructor(context: Context) { //获得WindowManager对象 private var mWindowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager companion object : SingletonHolder<FloatingManager, Context>(::FloatingManager) /** * 添加悬浮窗 * @param view * @param params * @return */ fun addView(view: View, params: WindowManager.LayoutParams): Boolean { try { mWindowManager.addView(view, params) return true } catch (e: Exception) { e.printStackTrace() } return false } /** * 移除悬浮窗 * * @param view * @return */ fun removeView(view: View): Boolean { try { mWindowManager.removeView(view) return true } catch (e: Exception) { e.printStackTrace() } return false } /** * 更新悬浮窗参数 * * @param view * @param params * @return */ fun updateView(view: View, params: WindowManager.LayoutParams): Boolean { try { mWindowManager.updateViewLayout(view, params) return true } catch (e: Exception) { e.printStackTrace() } return false } }FloatingClickView.kt class FloatingClickView(private val mContext: Context) : FrameLayout(mContext) { private lateinit var mWindowManager: FloatingManager private var mParams: WindowManager.LayoutParams? = null private lateinit var mView: View //按下坐标 private var mTouchStartX = -1f private var mTouchStartY = -1f val STATE_CLICKING = "state_clicking" val STATE_NORMAL = "state_normal" private var mCurrentState = STATE_NORMAL private var ivIcon: AppCompatImageView? = null init { initView() } private fun initView() { mView = LayoutInflater.from(context).inflate(R.layout.view_floating_click, null) ivIcon = mView.findViewById(R.id.iv_icon) mWindowManager = FloatingManager.getInstance(mContext) initListener() } @SuppressLint("ClickableViewAccessibility") private fun initListener() { mView.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { mTouchStartX = event.rawX mTouchStartY = event.rawY } MotionEvent.ACTION_MOVE -> { mParams?.let { it.x += (event.rawX - mTouchStartX).toInt() it.y += (event.rawY - mTouchStartY).toInt() mWindowManager.updateView(mView, it) } mTouchStartX = event.rawX mTouchStartY = event.rawY } } false } mView.setOnClickListener { val location = IntArray(2) it.getLocationOnScreen(location) val intent = Intent().apply { action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK when (mCurrentState) { STATE_NORMAL -> { mCurrentState = STATE_CLICKING putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_PLAY) putExtra(BroadcastConstants.KEY_POINT_X, (location[0] - 1).toFloat()) putExtra(BroadcastConstants.KEY_POINT_Y, (location[1] - 1).toFloat()) ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_green_24) } STATE_CLICKING -> { mCurrentState = STATE_NORMAL putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_STOP) ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_gray_24) } } } context.sendBroadcast(intent) } } fun show() { mParams = WindowManager.LayoutParams() mParams?.apply { gravity = Gravity.CENTER //总是出现在应用程序窗口之上 type = if (Build.VERSION.SDK_INT >= 26) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { WindowManager.LayoutParams.TYPE_SYSTEM_ALERT } //设置图片格式,效果为背景透明 format = PixelFormat.RGBA_8888 flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH width = LayoutParams.WRAP_CONTENT height = LayoutParams.WRAP_CONTENT if (mView.isAttachedToWindow) { mWindowManager.removeView(mView) } mWindowManager.addView(mView, this) } } fun remove() { mCurrentState = STATE_NORMAL ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_gray_24) mWindowManager.removeView(mView) } }页面事件 MainActivity.kt class MainActivity : AppCompatActivity() { private val TAG = javaClass::class.java.canonicalName override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initNotification() initListener() startAutoClickService() } private fun initListener() { btn_accessibility.setOnClickListener { checkAccessibility() } btn_floating_window.setOnClickListener { checkFloatingWindow() } btn_show_window.setOnClickListener { hideKeyboard() if (TextUtils.isEmpty(et_interval.text.toString())) { Snackbar.make(et_interval, "请输入间隔", Snackbar.LENGTH_SHORT).show() return@setOnClickListener } showFloatingWindow(et_interval.text.toString().toLong()) } btn_close_window.setOnClickListener { closeFloatWindow() } btn_test.setOnClickListener { Log.d(TAG, "btn_test on click") } } /** * 跳转设置开启无障碍 */ private fun checkAccessibility() { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) startActivity(intent) } /** * 跳转设置顶层悬浮窗 */ private fun checkFloatingWindow() { if (Build.VERSION.SDK_INT >= 23) { if (Settings.canDrawOverlays(this)) { Toast.makeText(this, "已开启", Toast.LENGTH_SHORT).show() } else { val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) startActivity(intent) } } } private fun startAutoClickService() { val intent = Intent(this, AutoClickService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent) } else { startService(intent) } } private fun showFloatingWindow(interval: Long) { sendBroadcast(Intent().apply { action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_SHOW) putExtra(BroadcastConstants.KEY_INTERVAL, interval) }) } private fun closeFloatWindow() { sendBroadcast(Intent().apply { action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_CLOSE) }) } private fun initNotification() { //注册渠道id if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = NotificationConstants.CHANNEl_NAME val descriptionText = NotificationConstants.CHANNEL_DES val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel(NotificationConstants.CHANNEL_ID, name, importance).apply { description = descriptionText } channel.enableLights(true) channel.lightColor = Color.GREEN // Register the channel with the system val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } override fun onDestroy() { val intent = Intent(this, AutoClickService::class.java) stopService(intent) super.onDestroy() } //收起输入法 fun hideKeyboard() { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager if (imm.isActive && currentFocus != null) { imm.hideSoftInputFromWindow( currentFocus!!.windowToken, InputMethodManager.HIDE_NOT_ALWAYS ) } } } object NotificationConstants { val CHANNEL_ID = "auto_channel_id" val CHANNEl_NAME = "Auto Click" val CHANNEL_DES = "Auto Click Service" } object BroadcastConstants { val BROADCAST_ACTION_AUTO_CLICK = "BROADCAST_ACTION_AUTO_CLICK" val KEY_ACTION = "KEY_ACTION" val KEY_INTERVAL = "KEY_INTERVAL" val KEY_POINT_X = "KEY_POINT_X" val KEY_POINT_Y = "KEY_POINT_Y" }5.布局 <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="" xmlns:app="" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tv_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="50dp" android:layout_marginTop="20dp" android:text="先打开无障碍权限 和 悬浮窗顶层权限\n点击位置在图标左上角xy偏移一个px\n程序将会在延迟一个间隔后开始执行" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_accessibility" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="50dp" android:layout_marginTop="20dp" android:text="无障碍选项" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_message" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_floating_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="20dp" android:text="悬浮窗选项" app:layout_constraintStart_toEndOf="@id/btn_accessibility" app:layout_constraintTop_toTopOf="@id/btn_accessibility" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/tv_unit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="50dp" android:text="ms" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="@id/et_interval" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/et_interval" /> <androidx.appcompat.widget.AppCompatEditText android:id="@+id/et_interval" android:layout_width="0dp" android:layout_height="48dp" android:layout_marginStart="50dp" android:layout_marginTop="50dp" android:layout_marginEnd="10dp" android:hint="点击的间隔(建议大于100)(毫秒)" android:inputType="number" android:textColor="#FF0000" android:textSize="14sp" app:layout_constraintEnd_toStartOf="@id/tv_unit" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_accessibility" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_show_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:text="打开悬浮视图" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/et_interval" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_close_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="关闭悬浮视图" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_show_window" /> <com.google.android.material.button.MaterialButton android:id="@+id/btn_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="100dp" android:text="测试点击按钮" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="" android:layout_width="wrap_content" android:layout_height="wrap_content"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/iv_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_auto_click_icon_gray_24" /> </FrameLayout>6.app-auto-click.zip (责任编辑:) |