@@ -14,8 +14,9 @@ class PseudoSelectorManager(
1414 private val detachActions = HashMap <String , Runnable >()
1515
1616 private val activeCallbacks = LinkedHashMap <View , PseudoSelectorCallback >()
17+ private val deepestCallbacks = LinkedHashMap <View , PseudoSelectorCallback >()
1718
18- private val activeDeepestViews = LinkedHashSet <View >()
19+ private val touchListenerViews = HashSet <View >()
1920
2021 fun attach (
2122 tag : Int ,
@@ -104,21 +105,11 @@ class PseudoSelectorManager(
104105 callback : PseudoSelectorCallback ,
105106 ) {
106107 activeCallbacks[view] = callback
107- val listener =
108- View .OnTouchListener { _, event ->
109- val action = event.actionMasked
110- if (action == MotionEvent .ACTION_DOWN ) {
111- fireActiveCallbacksUpTree(view, true )
112- } else if (action == MotionEvent .ACTION_UP || action == MotionEvent .ACTION_CANCEL ) {
113- fireActiveCallbacksUpTree(view, false )
114- }
115- false
116- }
117- view.setOnTouchListener(listener)
108+ ensureTouchListener(view)
118109 detachActions[key] =
119110 Runnable {
120111 activeCallbacks.remove(view)
121- view.setOnTouchListener( null )
112+ maybeRemoveTouchListener(view )
122113 }
123114 }
124115
@@ -127,27 +118,43 @@ class PseudoSelectorManager(
127118 key : String ,
128119 callback : PseudoSelectorCallback ,
129120 ) {
130- activeDeepestViews.add(view)
131- val listener =
132- View .OnTouchListener { _, event ->
133- val action = event.actionMasked
134- if (action == MotionEvent .ACTION_DOWN ) {
135- if (! hasDeepestDescendantAt(view, event.rawX, event.rawY)) {
136- callback.onSelectorStateChanged(true )
137- }
121+ deepestCallbacks[view] = callback
122+ ensureTouchListener(view)
123+ detachActions[key] =
124+ Runnable {
125+ deepestCallbacks.remove(view)
126+ maybeRemoveTouchListener(view)
127+ }
128+ }
129+
130+ private fun ensureTouchListener (view : View ) {
131+ if (! touchListenerViews.add(view)) {
132+ return
133+ }
134+ view.setOnTouchListener { _, event ->
135+ when (event.actionMasked) {
136+ MotionEvent .ACTION_DOWN -> {
138137 fireActiveCallbacksUpTree(view, true )
139- } else if (action == MotionEvent .ACTION_UP || action == MotionEvent .ACTION_CANCEL ) {
140- callback.onSelectorStateChanged(false )
138+ deepestCallbacks[view]?.let {
139+ if (! hasDeepestDescendantAt(view, event.rawX, event.rawY)) {
140+ it.onSelectorStateChanged(true )
141+ }
142+ }
143+ }
144+ MotionEvent .ACTION_UP , MotionEvent .ACTION_CANCEL -> {
141145 fireActiveCallbacksUpTree(view, false )
146+ deepestCallbacks[view]?.onSelectorStateChanged(false )
142147 }
143- false
144- }
145- view.setOnTouchListener(listener)
146- detachActions[key] =
147- Runnable {
148- activeDeepestViews.remove(view)
149- view.setOnTouchListener(null )
150148 }
149+ false
150+ }
151+ }
152+
153+ private fun maybeRemoveTouchListener (view : View ) {
154+ if (view !in activeCallbacks && view !in deepestCallbacks) {
155+ touchListenerViews.remove(view)
156+ view.setOnTouchListener(null )
157+ }
151158 }
152159
153160 fun detach (
@@ -158,9 +165,9 @@ class PseudoSelectorManager(
158165 }
159166
160167 /* *
161- * Returns true if any view in `activeDeepestViews` is a strict descendant of `ancestor`
162- * and contains the screen point (`rawX`, `rawY`).
163- * Used by _:active-deepest_ to yield priority to deeper registered views .
168+ * Returns true if any view registered for _:active-deepest_ is a strict descendant of
169+ * `ancestor` and contains the screen point (`rawX`, `rawY`), so the ancestor yields priority
170+ * to the deeper view .
164171 */
165172 private fun hasDeepestDescendantAt (
166173 ancestor : View ,
@@ -169,7 +176,7 @@ class PseudoSelectorManager(
169176 ): Boolean {
170177 val loc = IntArray (2 )
171178 // TODO: Optimize so we don't iterate over all the views with :active-deepest every time.
172- for (candidate in activeDeepestViews ) {
179+ for (candidate in deepestCallbacks.keys ) {
173180 if (candidate == = ancestor) continue
174181 if (! isDescendantOf(candidate, ancestor)) continue
175182 candidate.getLocationOnScreen(loc)
0 commit comments