Skip to content

Commit

Permalink
Add support for text in dynamic properties (#1995)
Browse files Browse the repository at this point in the history
The original TextDelegate API pre-dates dynamic properties. Compose only has access to the newer dynamic properties API so this PR extends dynamic property support to include text.

Fixes #1903
  • Loading branch information
gpeal authored Jan 16, 2022
1 parent c82ed2f commit ca94498
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.airbnb.lottie.value.ScaleXY
fun rememberLottieDynamicProperties(
vararg properties: LottieDynamicProperty<*>,
): LottieDynamicProperties {
return remember(properties) {
return remember(properties.contentHashCode()) {
LottieDynamicProperties(properties.toList())
}
}
Expand Down Expand Up @@ -67,7 +67,7 @@ fun <T> rememberLottieDynamicProperty(
vararg keyPath: String,
callback: (frameInfo: LottieFrameInfo<T>) -> T,
): LottieDynamicProperty<T> {
val keyPathObj = remember(keyPath) { KeyPath(*keyPath) }
val keyPathObj = remember(keyPath.contentHashCode()) { KeyPath(*keyPath) }
val callbackState by rememberUpdatedState(callback)
return remember(keyPathObj, property) {
LottieDynamicProperty(
Expand Down Expand Up @@ -101,6 +101,7 @@ class LottieDynamicProperties internal constructor(
private val intArrayProperties: List<LottieDynamicProperty<Array<*>>>,
private val typefaceProperties: List<LottieDynamicProperty<Typeface>>,
private val bitmapProperties: List<LottieDynamicProperty<Bitmap>>,
private val charSequenceProperties: List<LottieDynamicProperty<CharSequence>>,
) {
@Suppress("UNCHECKED_CAST")
constructor(properties: List<LottieDynamicProperty<*>>) : this(
Expand All @@ -112,6 +113,7 @@ class LottieDynamicProperties internal constructor(
properties.filter { it.property is Array<*> } as List<LottieDynamicProperty<Array<*>>>,
properties.filter { it.property is Typeface } as List<LottieDynamicProperty<Typeface>>,
properties.filter { it.property is Bitmap } as List<LottieDynamicProperty<Bitmap>>,
properties.filter { it.property is CharSequence } as List<LottieDynamicProperty<CharSequence>>,
)

internal fun addTo(drawable: LottieDrawable) {
Expand Down Expand Up @@ -139,7 +141,9 @@ class LottieDynamicProperties internal constructor(
bitmapProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
}

charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
}
}

internal fun removeFrom(drawable: LottieDrawable) {
Expand Down Expand Up @@ -167,6 +171,9 @@ class LottieDynamicProperties internal constructor(
bitmapProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<Bitmap>?)
}
charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<CharSequence>?)
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ public interface LottieProperty {
* Keypath after the layer name.
*/
Float DROP_SHADOW_RADIUS = 18f;

/**
* Set the color filter for an entire drawable content. Can be applied to fills, strokes, images, and solids.
*/
ColorFilter COLOR_FILTER = new ColorFilter();
/**
* Array of ARGB colors that map to position stops in the original gradient.
Expand All @@ -214,4 +216,8 @@ public interface LottieProperty {
* Set on image layers.
*/
Bitmap IMAGE = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
/**
* Replace the text for a text layer.
*/
CharSequence TEXT = "dynamic_text";
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.airbnb.lottie.animation.keyframe;

import androidx.annotation.Nullable;

import com.airbnb.lottie.model.DocumentData;
import com.airbnb.lottie.value.Keyframe;
import com.airbnb.lottie.value.LottieFrameInfo;
import com.airbnb.lottie.value.LottieValueCallback;

import java.util.List;

Expand All @@ -11,10 +15,33 @@ public TextKeyframeAnimation(List<Keyframe<DocumentData>> keyframes) {
}

@Override DocumentData getValue(Keyframe<DocumentData> keyframe, float keyframeProgress) {
if (keyframeProgress != 1.0f || keyframe.endValue == null) {
if (valueCallback != null) {
return valueCallback.getValueInternal(keyframe.startFrame, keyframe.endFrame == null ? Float.MAX_VALUE : keyframe.endFrame,
keyframe.startValue, keyframe.endValue == null ? keyframe.startValue : keyframe.endValue, keyframeProgress,
getInterpolatedCurrentKeyframeProgress(), getProgress());
} else if (keyframeProgress != 1.0f || keyframe.endValue == null) {
return keyframe.startValue;
} else {
return keyframe.endValue;
}
}

public void setStringValueCallback(LottieValueCallback<String> valueCallback) {
final LottieFrameInfo<String> stringFrameInfo = new LottieFrameInfo<>();
final DocumentData documentData = new DocumentData();
super.setValueCallback(new LottieValueCallback<DocumentData>() {
@Override
public DocumentData getValue(LottieFrameInfo<DocumentData> frameInfo) {
stringFrameInfo.set(frameInfo.getStartFrame(), frameInfo.getEndFrame(), frameInfo.getStartValue().text,
frameInfo.getEndValue().text, frameInfo.getLinearKeyframeProgress(), frameInfo.getInterpolatedKeyframeProgress(),
frameInfo.getOverallProgress());
String text = valueCallback.getValue(stringFrameInfo);
DocumentData baseDocumentData = frameInfo.getInterpolatedKeyframeProgress() == 1f ? frameInfo.getEndValue() : frameInfo.getStartValue();
documentData.set(text, baseDocumentData.fontName, baseDocumentData.size, baseDocumentData.justification, baseDocumentData.tracking,
baseDocumentData.lineHeight, baseDocumentData.baselineShift, baseDocumentData.color, baseDocumentData.strokeColor,
baseDocumentData.strokeWidth, baseDocumentData.strokeOverFill);
return documentData;
}
});
}
}
31 changes: 20 additions & 11 deletions lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,31 @@ public enum Justification {
CENTER
}

public final String text;
@SuppressWarnings("WeakerAccess") public final String fontName;
public final float size;
@SuppressWarnings("WeakerAccess") public final Justification justification;
public final int tracking;
@SuppressWarnings("WeakerAccess") public final float lineHeight;
public final float baselineShift;
@ColorInt public final int color;
@ColorInt public final int strokeColor;
public final float strokeWidth;
public final boolean strokeOverFill;
public String text;
@SuppressWarnings("WeakerAccess") public String fontName;
public float size;
@SuppressWarnings("WeakerAccess") public Justification justification;
public int tracking;
@SuppressWarnings("WeakerAccess") public float lineHeight;
public float baselineShift;
@ColorInt public int color;
@ColorInt public int strokeColor;
public float strokeWidth;
public boolean strokeOverFill;


public DocumentData(String text, String fontName, float size, Justification justification, int tracking,
float lineHeight, float baselineShift, @ColorInt int color, @ColorInt int strokeColor,
float strokeWidth, boolean strokeOverFill) {
set(text, fontName, size, justification, tracking, lineHeight, baselineShift, color, strokeColor, strokeWidth, strokeOverFill);
}

public DocumentData() {
}

public void set(String text, String fontName, float size, Justification justification, int tracking,
float lineHeight, float baselineShift, @ColorInt int color, @ColorInt int strokeColor,
float strokeWidth, boolean strokeOverFill) {
this.text = text;
this.fontName = fontName;
this.size = size;
Expand Down
22 changes: 22 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/model/KeyPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@ public String keysToString() {
return keys.toString();
}

@Override public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

KeyPath keyPath = (KeyPath) o;

if (!keys.equals(keyPath.keys)) {
return false;
}
return resolvedElement != null ? resolvedElement.equals(keyPath.resolvedElement) : keyPath.resolvedElement == null;
}

@Override public int hashCode() {
int result = keys.hashCode();
result = 31 * result + (resolvedElement != null ? resolvedElement.hashCode() : 0);
return result;
}

@Override public String toString() {
return "KeyPath{" + "keys=" + keys + ",resolved=" + (resolvedElement != null) + '}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> ca
typefaceCallbackAnimation.addUpdateListener(this);
addAnimation(typefaceCallbackAnimation);
}
} else if (property == LottieProperty.TEXT) {
textAnimation.setStringValueCallback((LottieValueCallback<String>) callback);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package com.airbnb.lottie.snapshots.tests

import androidx.compose.runtime.getValue
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.TextDelegate
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.rememberLottieComposition
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.airbnb.lottie.compose.rememberLottieDynamicProperty
import com.airbnb.lottie.snapshots.LocalSnapshotReady
import com.airbnb.lottie.snapshots.SnapshotTestCase
import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
import com.airbnb.lottie.snapshots.loadCompositionFromAssetsSync
import com.airbnb.lottie.snapshots.snapshotComposable
import com.airbnb.lottie.snapshots.withAnimationView

class TextTestCase : SnapshotTestCase {
Expand Down Expand Up @@ -126,5 +137,47 @@ class TextTestCase : SnapshotTestCase {
animationView.setTextDelegate(textDelegate)
textDelegate.setText("NAME", "\uD83D\uDC91")
}

snapshotComposable("Compose Dynamic Text", "Emoji") {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/DynamicText.json"))
LocalSnapshotReady.current.value = composition != null
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(LottieProperty.TEXT, "NAME") {
"🔥💪💯"
},
)
LottieAnimation(composition, 0f, dynamicProperties = dynamicProperties)
}

snapshotComposable("Compose Dynamic Text", "Taiwanese") {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/DynamicText.json"))
LocalSnapshotReady.current.value = composition != null
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(LottieProperty.TEXT, "我的密碼", "NAME"),
)
LottieAnimation(composition, 0f, dynamicProperties = dynamicProperties)
}

snapshotComposable("Compose Dynamic Text", "FrameInfo.startValue") {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/DynamicText.json"))
LocalSnapshotReady.current.value = composition != null
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(LottieProperty.TEXT, "NAME") { frameInfo ->
"${frameInfo.startValue}!!!"
},
)
LottieAnimation(composition, 0f, dynamicProperties = dynamicProperties)
}

snapshotComposable("Compose Dynamic Text", "FrameInfo.endValue") {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/DynamicText.json"))
LocalSnapshotReady.current.value = composition != null
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(LottieProperty.TEXT, "NAME") { frameInfo ->
"${frameInfo.endValue}!!!"
},
)
LottieAnimation(composition, 0f, dynamicProperties = dynamicProperties)
}
}
}

0 comments on commit ca94498

Please sign in to comment.