mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
supported layout_provider
941efb5b9a1f6f24b25421890cee9a3712d59acf
This commit is contained in:
@@ -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",
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
+73
@@ -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>
|
||||
|
||||
@@ -226,6 +226,7 @@
|
||||
"$ref": "div-layout-provider.json",
|
||||
"$description": "translations.json#/div_layout_provider",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -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": [
|
||||
|
||||
Reference in New Issue
Block a user