supported layout_provider

941efb5b9a1f6f24b25421890cee9a3712d59acf
This commit is contained in:
tayrinn
2024-07-26 17:43:49 +03:00
parent 114e53d82c
commit 8901e2558e
9 changed files with 166 additions and 2 deletions
+1
View File
@@ -1124,6 +1124,7 @@
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivImageBinder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivImageBinder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivIndicatorBinder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivIndicatorBinder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivInputBinder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivInputBinder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivLayoutProviderVariablesHolder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivLayoutProviderVariablesHolder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSelectBinder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSelectBinder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSeparatorBinder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSeparatorBinder.kt",
"client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSightExtensions.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/divs/DivSightExtensions.kt",
@@ -14,6 +14,7 @@ internal const val SIZE_PROVIDER_EXTENSION_ID = "size_provider"
internal const val SIZE_PROVIDER_PARAM_HEIGHT = "height_variable_name"
internal const val SIZE_PROVIDER_PARAM_WIDTH = "width_variable_name"
@Deprecated("Use div-base.layout-provider.")
class DivSizeProviderExtensionHandler(
private val errorLogger: DivSizeProviderErrorLogger = DivSizeProviderErrorLogger.STUB
): DivExtensionHandler {
@@ -9,6 +9,7 @@ import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.annotation.VisibleForTesting
import androidx.core.view.ViewCompat
import androidx.core.view.doOnAttach
@@ -44,6 +45,7 @@ import com.yandex.div.core.view2.animations.DivComparator
import com.yandex.div.core.view2.animations.DivTransitionHandler
import com.yandex.div.core.view2.animations.allowsTransitionsOnDataChange
import com.yandex.div.core.view2.animations.doOnEnd
import com.yandex.div.core.view2.divs.DivLayoutProviderVariablesHolder
import com.yandex.div.core.view2.divs.bindLayoutParams
import com.yandex.div.core.view2.divs.bindingContext
import com.yandex.div.core.view2.divs.drawChildrenShadows
@@ -171,6 +173,10 @@ class Div2View private constructor(
}
internal val inputFocusTracker = viewComponent.inputFocusTracker
internal val layoutSizes = mutableMapOf<String, Int>()
internal val variablesHolders = mutableMapOf<DivData, DivLayoutProviderVariablesHolder>()
internal var clearVariablesListener: ViewTreeObserver.OnPreDrawListener? = null
var dataTag: DivDataTag = DivDataTag.INVALID
internal set(value) {
prevDataTag = field
@@ -2,13 +2,17 @@ package com.yandex.div.core.view2.divs
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver
import androidx.core.view.ViewCompat
import androidx.transition.Transition
import androidx.transition.TransitionManager
import androidx.transition.Visibility
import com.yandex.div.R
import com.yandex.div.core.actions.logError
import com.yandex.div.core.dagger.DivScope
import com.yandex.div.core.tooltip.DivTooltipController
import com.yandex.div.core.util.equalsToConstant
@@ -64,6 +68,7 @@ internal class DivBaseBinder @Inject constructor(
view.bindId(divView, div, oldDiv)
view.bindLayoutParams(div, oldDiv, resolver, subscriber)
view.bindLayoutProvider(divView, div, oldDiv, resolver)
view.bindAccessibility(divView, div, oldDiv, resolver, subscriber)
view.bindAlpha(div, oldDiv, resolver, subscriber)
@@ -238,6 +243,80 @@ internal class DivBaseBinder @Inject constructor(
subscriber.addSubscription(newDiv.alignmentVertical?.observe(resolver, callback))
}
private fun View.bindLayoutProvider(
divView: Div2View,
newDiv: DivBase,
oldDiv: DivBase?,
resolver: ExpressionResolver
) {
val data = divView.divData ?: return
val layoutProvider = newDiv.layoutProvider ?: return
if (layoutProvider.widthVariableName.equals(oldDiv?.layoutProvider?.widthVariableName)
&& layoutProvider.heightVariableName.equals(oldDiv?.layoutProvider?.heightVariableName)) {
return
}
if (oldDiv?.layoutProvider != null) {
clearLayoutProviderVariables()
}
val widthVariable = layoutProvider.widthVariableName
val heightVariable = layoutProvider.heightVariableName
if (widthVariable.isNullOrEmpty() && heightVariable.isNullOrEmpty()) {
divView.logError(Throwable("Neither width_variable_name nor height_variable_name found."))
return
}
val variablesHolder = divView.variablesHolders[data] ?: DivLayoutProviderVariablesHolder()
.apply { observeDivData(data, resolver) }
.also { divView.variablesHolders[data] = it }
val listener = View.OnLayoutChangeListener { _, left, top, right, bottom,
oldLeft, oldTop, oldRight, oldBottom ->
val metrics = resources.displayMetrics
updateVariable(divView, metrics, widthVariable, variablesHolder, left, right, oldLeft, oldRight)
updateVariable(divView, metrics, heightVariable, variablesHolder, top, bottom, oldTop, oldBottom)
}
addOnLayoutChangeListener(listener)
setTag(R.id.div_layout_provider_listener_id, listener)
if (divView.clearVariablesListener != null) return
val clearVariablesListener = ViewTreeObserver.OnPreDrawListener {
variablesHolder.clear()
divView.layoutSizes.forEach { divView.setVariable(it.key, it.value.toString()) }
divView.layoutSizes.clear()
true
}
divView.clearVariablesListener = clearVariablesListener
divView.viewTreeObserver.addOnPreDrawListener(clearVariablesListener)
}
private fun updateVariable(
divView: Div2View,
metrics: DisplayMetrics,
variableName: String?,
variablesHolder: DivLayoutProviderVariablesHolder,
start: Int,
end: Int,
oldStart: Int,
oldEnd: Int
) {
if (variableName.isNullOrEmpty()) return
val size = end - start
if (size == oldEnd - oldStart) return
if (variablesHolder.contains(variableName)) {
divView.logError(Throwable(
"Size subscriber affects original view size. Relayout was prevented."))
return
}
divView.layoutSizes[variableName] = size.pxToDp(metrics)
}
private fun View.clearLayoutProviderVariables() {
removeOnLayoutChangeListener(getTag(R.id.div_layout_provider_listener_id) as? View.OnLayoutChangeListener)
}
//endregion
//region Paddings
@@ -0,0 +1,73 @@
package com.yandex.div.core.view2.divs
import com.yandex.div.core.Disposable
import com.yandex.div.internal.core.DivVisitor
import com.yandex.div.internal.core.ExpressionSubscriber
import com.yandex.div.internal.core.buildItems
import com.yandex.div.internal.core.nonNullItems
import com.yandex.div.json.expressions.Expression
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.Div
import com.yandex.div2.DivData
import com.yandex.div2.DivFixedSize
import com.yandex.div2.DivSize
class DivLayoutProviderVariablesHolder : DivVisitor<Unit>(), ExpressionSubscriber {
private val changedVariables = mutableListOf<String>()
override val subscriptions = mutableListOf<Disposable>()
fun contains(variable: String) = changedVariables.contains(variable)
fun clear() = changedVariables.clear()
fun observeDivData(data: DivData, resolver: ExpressionResolver) = data.states.forEach { visit(it.div, resolver) }
override fun defaultVisit(data: Div, resolver: ExpressionResolver) = data.observeSize(resolver)
override fun visit(data: Div.Container, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.buildItems(resolver).forEach { visit(it.div, it.expressionResolver) }
}
override fun visit(data: Div.Grid, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.nonNullItems.forEach { visit(it, resolver) }
}
override fun visit(data: Div.Gallery, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.buildItems(resolver).forEach { visit(it.div, it.expressionResolver) }
}
override fun visit(data: Div.Pager, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.buildItems(resolver).forEach { visit(it.div, it.expressionResolver) }
}
override fun visit(data: Div.Tabs, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.items.forEach { visit(it.div, resolver) }
}
override fun visit(data: Div.State, resolver: ExpressionResolver) {
defaultVisit(data, resolver)
data.value.states.forEach { state -> state.div?.let { visit(it, resolver) } }
}
private fun Div.observeSize(resolver: ExpressionResolver) {
with(value()) {
width.observe(resolver)
height.observe(resolver)
}
}
private fun DivSize.observe(resolver: ExpressionResolver) {
val size = value() as? DivFixedSize ?: return
val sizeExpr = size.value as? Expression.MutableExpression<*, Long> ?: return
addSubscription(sizeExpr.observe(resolver) {
changedVariables.addAll(sizeExpr.getVariablesName())
})
}
}
@@ -31,4 +31,6 @@
<item name="div_releasable_list" type="id"/>
<item name="div_pager_item_clip_id" type="id"/>
<item name="div_layout_provider_listener_id" type="id"/>
</resources>
+1
View File
@@ -226,6 +226,7 @@
"$ref": "div-layout-provider.json",
"$description": "translations.json#/div_layout_provider",
"platforms": [
"android",
"ios"
]
},
+2 -2
View File
@@ -2,11 +2,11 @@
"type": "object",
"properties": {
"width_variable_name": {
"type": "string",
"$ref": "div-variable-name.json",
"$description": "translations.json#/div_layout_provider_width_variable_name"
},
"height_variable_name": {
"type": "string",
"$ref": "div-variable-name.json",
"$description": "translations.json#/div_layout_provider_height_variable_name"
}
}
@@ -1951,6 +1951,7 @@
{
"title": "LayoutProvider",
"platforms": [
"android",
"ios"
],
"tags": [