AndroidViewsHandler.kt
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:OptIn(ExperimentalLayoutNodeApi::class)
package androidx.compose.ui.platform
import android.annotation.SuppressLint
import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewParent
import androidx.compose.ui.node.ExperimentalLayoutNodeApi
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.node.LayoutNode.LayoutState.NeedsRemeasure
/**
* Used by [AndroidComposeView] to handle the Android [View]s attached to its hierarchy.
* The [AndroidComposeView] has one direct [AndroidViewsHandler], which is responsible
* of intercepting [requestLayout]s, [onMeasure]s, [invalidate]s, etc. sent from or towards
* children.
*/
internal class AndroidViewsHandler(context: Context) : ViewGroup(context) {
val layoutNode = hashMapOf<View, LayoutNode>()
// No call to super to avoid invalidating the AndroidComposeView and rely on
// component nodes logic.
@SuppressLint("MissingSuperCall")
override fun onDescendantInvalidated(child: View, target: View) {
layoutNode[child]?.invalidateLayer()
}
// No call to super to avoid invalidating the AndroidComposeView and rely on
// component nodes logic.
override fun invalidateChildInParent(
location: IntArray?,
dirty: android.graphics.Rect?
): ViewParent? = null
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Layout will be handled by component nodes.
setMeasuredDimension(0, 0)
}
override fun measureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// Layout will be handled by component nodes.
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
// Layout was already handled by component nodes, but replace here because
// the View system has forced relayout on children. This method will only be called
// when forceLayout is called on the Views hierarchy.
layoutNode.keys.forEach { it.layout(it.left, it.top, it.right, it.bottom) }
}
// Touch events forwarding will be handled by component nodes.
override fun dispatchTouchEvent(ev: MotionEvent?) = true
// No call to super to avoid invalidating the AndroidComposeView and rely on
// component nodes logic.
@SuppressLint("MissingSuperCall")
override fun requestLayout() {
// Hack to cleanup the dirty layout flag on ourselves, such that this method continues
// to be called for further children requestLayout().
cleanupLayoutState(this)
// requestLayout() was called by a child, so we have to request remeasurement for
// their corresponding layout node.
for (i in 0 until childCount) {
val child = getChildAt(i)
val node = layoutNode[child]
if (child.isLayoutRequested && node != null &&
node.layoutState != NeedsRemeasure
) {
layoutNode[child]!!.requestRemeasure()
}
}
}
}