17
17
package com.xiaocydx.cxrv.internal
18
18
19
19
import android.content.Context
20
+ import android.graphics.Canvas
20
21
import android.view.Choreographer
21
22
import android.view.Choreographer.FrameCallback
22
23
import android.view.KeyEvent
@@ -88,10 +89,13 @@ private class BroadcastProxy(private val view: View) {
88
89
return
89
90
}
90
91
callbacks + = callback
91
- view.requestLayout()
92
92
broadcastFrame?.scheduleTraversal()
93
93
}
94
94
95
+ fun requestLayoutIfNecessary () {
96
+ if (callbacks.size > 0 ) view.requestLayout()
97
+ }
98
+
95
99
fun removeTraversalCallback (callback : FrameCallback ) {
96
100
callbacks - = callback
97
101
}
@@ -116,13 +120,10 @@ private class BroadcastProxy(private val view: View) {
116
120
}
117
121
118
122
private class BroadcastFrame private constructor(context : Context ) : View(context) {
119
- private var canUnregisterOnDetached = true
123
+ private val scheduleTraversalRunner = Runnable { scheduleTraversal() }
120
124
private var proxyList = InlineList <BroadcastProxy >()
121
-
122
- init {
123
- setWillNotDraw(true )
124
- visibility = INVISIBLE
125
- }
125
+ private var canUnregisterOnDetached = true
126
+ private var isScheduled = false
126
127
127
128
fun register (proxy : BroadcastProxy ) {
128
129
proxyList + = proxy
@@ -133,6 +134,9 @@ private class BroadcastFrame private constructor(context: Context) : View(contex
133
134
}
134
135
135
136
fun scheduleTraversal () {
137
+ removeCallbacks(scheduleTraversalRunner)
138
+ proxyList.reverseAccessEach { it.requestLayoutIfNecessary() }
139
+
136
140
ensureFirstMeasure()
137
141
requestLayout()
138
142
if (parent !is FrameLayout ) {
@@ -141,6 +145,13 @@ private class BroadcastFrame private constructor(context: Context) : View(contex
141
145
// requestApplyInsets()是替补方案,尽可能让BroadcastFrame在其它child之前执行。
142
146
requestApplyInsets()
143
147
}
148
+
149
+ // 调用scheduleTraversal()时,当前可能处于measure、layout阶段,
150
+ // 此时调用requestLayout()会被parent的flag拦截,不会有下一帧。
151
+ // 因此调用invalidate(),在onDraw()判断isScheduled是否被重置,
152
+ // 若isScheduled未被重置,则表示被拦截,需要重新调度。
153
+ invalidate()
154
+ isScheduled = true
144
155
}
145
156
146
157
override fun dispatchApplyWindowInsets (insets : WindowInsets ): WindowInsets {
@@ -153,7 +164,16 @@ private class BroadcastFrame private constructor(context: Context) : View(contex
153
164
setMeasuredDimension(0 , 0 )
154
165
}
155
166
167
+ override fun onDraw (canvas : Canvas ) {
168
+ if (isScheduled) {
169
+ isScheduled = false
170
+ // 前置流程可能会添加同步屏障,因此不使用post()
171
+ postOnAnimation(scheduleTraversalRunner)
172
+ }
173
+ }
174
+
156
175
private fun consumeTraversalCallbacks () {
176
+ isScheduled = false
157
177
proxyList.reverseAccessEach { it.consumeTraversalCallbacks() }
158
178
}
159
179
@@ -171,6 +191,7 @@ private class BroadcastFrame private constructor(context: Context) : View(contex
171
191
override fun onDetachedFromWindow () {
172
192
super .onDetachedFromWindow()
173
193
if (! canUnregisterOnDetached) return
194
+ removeCallbacks(scheduleTraversalRunner)
174
195
proxyList.reverseAccessEach { it.unregisterFromBroadcastFrame() }
175
196
proxyList = proxyList.clear()
176
197
}
0 commit comments