Android自定义View点击事件失效及动画不运行问题求助
问题描述
我有一个用于控制设备连接状态的Service类,当设备接入电源时,会通过WindowManager显示一个自定义View。但目前遇到两个问题:
- 自定义View的点击事件完全失效
- 设置的Lottie动画无法正常运行
Service类代码
class Service : Service() { private var customLockScreenView: CustomLockScreenView? = null private var windowManager: WindowManager? = null private val powerConnectionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_POWER_CONNECTED -> powerWasConnected() Intent.ACTION_POWER_DISCONNECTED -> powerWasDisconnected() } } } @SuppressLint("MissingInflatedId", "InflateParams") override fun onCreate() { super.onCreate() val filter = IntentFilter().apply { addAction(Intent.ACTION_POWER_CONNECTED) addAction(Intent.ACTION_POWER_DISCONNECTED) } registerReceiver(powerConnectionReceiver, filter) } override fun onDestroy() { super.onDestroy() unregisterReceiver(powerConnectionReceiver) powerWasDisconnected() } override fun onBind(intent: Intent): IBinder? { return null } private fun powerWasConnected() { windowManager = getSystemService(WINDOW_SERVICE) as WindowManager? if (customLockScreenView == null) { customLockScreenView = CustomLockScreenView(this) customLockScreenView!!.setClick(object : OnSingleClickListener { override fun onSingleClick() { Log.d("Clicks Service", "Custom view clicked!") } }) customLockScreenView!!.findViewById<LottieAnimationView>(R.id.animationView).setAnimation(R.raw.anim20) Log.d("TAG", "powerWasConnected: Power is connected") val layoutParams = WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, android.graphics.PixelFormat.TRANSLUCENT, ) layoutParams.gravity = Gravity.CENTER windowManager?.addView(customLockScreenView, layoutParams) Handler().postDelayed({ customLockScreenView?.let { windowManager?.removeView(it) customLockScreenView = null } }, 5000) } } private fun powerWasDisconnected() { Log.d("TAG", "powerWasDisconnected: Power is disconnected") clearFilter() } private fun clearFilter() { customLockScreenView?.let { windowManager?.removeView(it) customLockScreenView = null } } }
自定义View类代码
class CustomLockScreenView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RelativeLayout(context, attrs, defStyleAttr) { var clickListener: OnSingleClickListener? = null fun setClick(listener: OnSingleClickListener) { clickListener = listener } init { LayoutInflater.from(context).inflate(R.layout.custom_lock_screen, this, true) findViewById<LottieAnimationView>(R.id.animationView).setAnimation(R.raw.anim20) findViewById<RelativeLayout>(R.id.main)?.setOnClickListener { clickListener?.onSingleClick() } } }
自定义View布局XML
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/view2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color_toolbar" android:clickable="true"> <com.airbnb.lottie.LottieAnimationView android:id="@+id/animationView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:padding="@dimen/_55sdp" app:lottie_rawRes="@raw/anim20" app:lottie_autoPlay="true" app:lottie_loop="true" app:lottie_speed="1" /> <LinearLayout android:id="@+id/layoutBattery" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="@dimen/_35sdp" android:clickable="false" android:focusable="false" android:orientation="vertical"> <TextView android:id="@+id/tvPercentage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_margin="@dimen/_10sdp" android:fontFamily="@font/regular" android:text="10%" android:textColor="@color/white" android:textSize="@dimen/_30ssp" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:src="@drawable/ic_battery" /> </LinearLayout> <ImageView android:id="@+id/iv_close" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/_25sdp" android:src="@drawable/ic_click" android:visibility="invisible" /> </RelativeLayout>
点击监听抽象类
abstract class SingleClickListener : View.OnClickListener { private var mLastClickTime: Long = 0 abstract fun onSingleClick(view: View?) override fun onClick(view: View) { val currentTime = SystemClock.uptimeMillis() if (currentTime - mLastClickTime > MIN_CLICK_INTERVAL) { mLastClickTime = currentTime onSingleClick(view) } } companion object { private const val MIN_CLICK_INTERVAL: Long = 1000 } }
点击监听接口
interface OnSingleClickListener { fun onSingleClick() }
问题修复方案
1. 修复点击事件失效问题
核心原因:
- WindowManager布局参数添加了
FLAG_NOT_FOCUSABLE标记,直接导致View无法获取焦点,无法接收点击事件。 - 自定义View中绑定点击监听的目标ID错误:布局根View的ID是
view2,但代码中尝试绑定不存在的R.id.main,监听未生效。
修复步骤:
(1)调整WindowManager参数flags
移除FLAG_NOT_FOCUSABLE,保留FLAG_NOT_TOUCH_MODAL允许下层View接收非当前View区域的点击:
val layoutParams = WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, android.graphics.PixelFormat.TRANSLUCENT, )
(2)修正点击监听绑定目标
将绑定的ID改为布局中实际存在的view2:
init { LayoutInflater.from(context).inflate(R.layout.custom_lock_screen, this, true) findViewById<LottieAnimationView>(R.id.animationView).setAnimation(R.raw.anim20) findViewById<RelativeLayout>(R.id.view2).setOnClickListener { clickListener?.onSingleClick() } }
2. 修复Lottie动画不运行问题
核心原因:
- 重复设置动画:布局XML、自定义View初始化、Service中三次设置同一动画,导致初始化逻辑冲突。
- 未显式触发动画播放,即使XML设置了
lottie_autoPlay="true",WindowManager添加View后可能需要手动启动。
修复步骤:
(1)移除重复的动画设置
删除Service中多余的动画设置代码:
// 删掉此行 // customLockScreenView!!.findViewById<LottieAnimationView>(R.id.animationView).setAnimation(R.raw.anim20)
(2)显式启动动画
在自定义View初始化时添加playAnimation()调用:
init { LayoutInflater.from(context).inflate(R.layout.custom_lock_screen, this, true) val lottieView = findViewById<LottieAnimationView>(R.id.animationView) lottieView.setAnimation(R.raw.anim20) lottieView.playAnimation() findViewById<RelativeLayout>(R.id.view2).setOnClickListener { clickListener?.onSingleClick() } }
内容的提问来源于stack exchange,提问作者Shivam
相关产品推荐
相关产品推荐

