Added support for setting the start/end frame/progress manually (#433)
Fixes #415
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationFragment.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationFragment.kt
index 2231edb..f1ea666 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationFragment.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/AnimationFragment.kt
@@ -107,7 +107,7 @@
))
view.animationView.addAnimatorUpdateListener {
- animation -> seekBar.progress = (animation.animatedFraction * 100).toInt()
+ animation -> seekBar.progress = ((animation.animatedValue as Float) * 100f).toInt()
}
view.seekBar.setOnSeekBarChangeListener(OnSeekBarChangeListenerAdapter(
@@ -118,6 +118,11 @@
}
))
+ view.trimView.setCallback({ startProgress, endProgress ->
+ animationView.setMinAndMaxProgress(startProgress, endProgress)
+ animationView.progress = startProgress
+ })
+
view.scaleSeekBar.setOnSeekBarChangeListener(OnSeekBarChangeListenerAdapter(
onProgressChanged = { _, progress, _ ->
animationView.scale = progress / SCALE_SLIDER_FACTOR
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieFontViewGroup.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieFontViewGroup.kt
index 3bda165..79d4a76 100644
--- a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieFontViewGroup.kt
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/LottieFontViewGroup.kt
@@ -14,11 +14,6 @@
import com.airbnb.lottie.LottieComposition
import java.util.*
-private inline fun consume(f: () -> Unit): Boolean {
- f()
- return true
-}
-
class LottieFontViewGroup @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
diff --git a/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TrimView.kt b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TrimView.kt
new file mode 100644
index 0000000..a6dc2f8
--- /dev/null
+++ b/LottieSample/src/main/kotlin/com/airbnb/lottie/samples/TrimView.kt
@@ -0,0 +1,75 @@
+package com.airbnb.lottie.samples
+
+import android.content.Context
+import android.support.v4.widget.ViewDragHelper
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.ImageView
+
+class TrimView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : FrameLayout(context, attrs, defStyleAttr) {
+
+ private val leftAnchor by lazy {
+ val iv = ImageView(context)
+ iv.setImageResource(R.drawable.ic_trim)
+ iv
+ }
+ private val rightAnchor by lazy {
+ val iv = ImageView(context)
+ iv.setImageResource(R.drawable.ic_trim)
+ iv
+ }
+ private lateinit var callback: (Float, Float) -> Unit
+
+ private val dragHelper = ViewDragHelper.create(this, object: ViewDragHelper.Callback() {
+ override fun tryCaptureView(child: View, pointerId: Int) = true
+
+ override fun getViewHorizontalDragRange(child: View) = width
+
+ override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
+ if (child == leftAnchor) {
+ return maxOf(minOf(left, rightAnchor.left - leftAnchor.width), 0)
+ } else {
+ return minOf(maxOf(leftAnchor.right, left), width - rightAnchor.width)
+ }
+ }
+
+ override fun onViewPositionChanged(view: View, left: Int, top: Int, dx: Int, dy: Int) {
+ val startProgress = (leftAnchor.left + leftAnchor.width / 2f) / width.toFloat()
+ val endProgress = (rightAnchor.right - leftAnchor.width / 2f) / width.toFloat()
+ callback(startProgress, endProgress)
+ }
+ })
+
+
+ init {
+ val leftLp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
+ leftLp.gravity = Gravity.START
+ leftAnchor.layoutParams = leftLp
+ addView(leftAnchor)
+ val rightLp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
+ rightLp.gravity = Gravity.END
+ rightAnchor.layoutParams = rightLp
+ addView(rightAnchor)
+ }
+
+ override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
+ if (dragHelper.shouldInterceptTouchEvent(ev)) {
+ return true
+ }
+ return super.onInterceptTouchEvent(ev)
+ }
+
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ dragHelper.processTouchEvent(event)
+ return true
+ }
+
+ fun setCallback(callback: (Float, Float) -> Unit) {
+ this.callback = callback
+ }
+}
\ No newline at end of file
diff --git a/LottieSample/src/main/res/drawable/ic_trim.xml b/LottieSample/src/main/res/drawable/ic_trim.xml
new file mode 100644
index 0000000..203640d
--- /dev/null
+++ b/LottieSample/src/main/res/drawable/ic_trim.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zM16.59,5.41L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
+</vector>
diff --git a/LottieSample/src/main/res/layout/fragment_animation.xml b/LottieSample/src/main/res/layout/fragment_animation.xml
index 24cc8e5..d3c5049 100644
--- a/LottieSample/src/main/res/layout/fragment_animation.xml
+++ b/LottieSample/src/main/res/layout/fragment_animation.xml
@@ -144,8 +144,7 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="6dp">
+ android:layout_height="wrap_content">
<ImageButton
android:id="@+id/invertColors"
@@ -225,16 +224,30 @@
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
android:layout_marginRight="16dp"
android:layout_weight="1"
android:gravity="right"
android:text="Progress"/>
- <android.support.v7.widget.AppCompatSeekBar
- android:id="@+id/seekBar"
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_weight="3"/>
+ android:layout_weight="3">
+ <com.airbnb.lottie.samples.TrimView
+ android:id="@+id/trimView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+ <android.support.v7.widget.AppCompatSeekBar
+ android:id="@+id/seekBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"/>
+ </FrameLayout>
+
<Space
android:layout_width="0dp"
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 1d9d592..c1eafa3 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -41,14 +41,13 @@
* You can manually set the progress of the animation with {@link #setProgress(float)} or
* {@link R.attr#lottie_progress}
*/
-public class LottieAnimationView extends AppCompatImageView {
+@SuppressWarnings({"unused", "WeakerAccess"}) public class LottieAnimationView extends AppCompatImageView {
private static final String TAG = LottieAnimationView.class.getSimpleName();
/**
* Caching strategy for compositions that will be reused frequently.
* Weak or Strong indicates the GC reference strength of the composition in the cache.
*/
- @SuppressWarnings("WeakerAccess")
public enum CacheStrategy {
None,
Weak,
@@ -161,7 +160,7 @@
* @param contentName name of the specific content that the color filter is to be applied
* @param colorFilter the color filter, null to clear the color filter
*/
- @SuppressWarnings("unused") public void addColorFilterToContent(
+ public void addColorFilterToContent(
String layerName, String contentName, @Nullable ColorFilter colorFilter) {
lottieDrawable.addColorFilterToContent(layerName, contentName, colorFilter);
}
@@ -171,7 +170,7 @@
* @param layerName name of the layer that the color filter is to be applied
* @param colorFilter the color filter, null to clear the color filter
*/
- @SuppressWarnings("unused") public void addColorFilterToLayer(
+ public void addColorFilterToLayer(
String layerName, @Nullable ColorFilter colorFilter) {
lottieDrawable.addColorFilterToLayer(layerName, colorFilter);
}
@@ -187,7 +186,7 @@
/**
* Clear all color filters on all layers and all content in the layers
*/
- @SuppressWarnings("unused") public void clearColorFilters() {
+ public void clearColorFilters() {
lottieDrawable.clearColorFilters();
}
@@ -264,7 +263,6 @@
* first shape. If you need to cut out one shape from another shape, use an even-odd fill type
* instead of using merge paths.
*/
- @SuppressWarnings({"WeakerAccess", "Unused"})
public void enableMergePathsForKitKatAndAbove(boolean enable) {
lottieDrawable.enableMergePathsForKitKatAndAbove(enable);
}
@@ -272,7 +270,6 @@
/**
* @see #useHardwareAcceleration(boolean)
*/
- @SuppressWarnings({"WeakerAccess", "unused"})
@Deprecated
public void useExperimentalHardwareAcceleration() {
useHardwareAcceleration(true);
@@ -282,7 +279,6 @@
/**
* @see #useHardwareAcceleration(boolean)
*/
- @SuppressWarnings({"WeakerAccess", "unused"})
@Deprecated
public void useExperimentalHardwareAcceleration(boolean use) {
useHardwareAcceleration(use);
@@ -291,7 +287,7 @@
/**
* @see #useHardwareAcceleration(boolean)
*/
- @SuppressWarnings("unused") public void useHardwareAcceleration() {
+ public void useHardwareAcceleration() {
useHardwareAcceleration(true);
}
@@ -307,7 +303,6 @@
* potentially break hardware rendering with bugs in their SKIA engine. Lottie cannot do
* anything about that.
*/
- @SuppressWarnings({"WeakerAccess", "unused"})
public void useHardwareAcceleration(boolean use) {
useHardwareLayer = use;
enableOrDisableHardwareLayer();
@@ -319,7 +314,7 @@
* <p>
* Will not cache the composition once loaded.
*/
- @SuppressWarnings("WeakerAccess") public void setAnimation(String animationName) {
+ public void setAnimation(String animationName) {
setAnimation(animationName, defaultCacheStrategy);
}
@@ -331,7 +326,6 @@
* strong reference to the composition once it is loaded
* and deserialized. {@link CacheStrategy#Weak} will hold a weak reference to said composition.
*/
- @SuppressWarnings("WeakerAccess")
public void setAnimation(final String animationName, final CacheStrategy cacheStrategy) {
this.animationName = animationName;
if (WEAK_REF_CACHE.containsKey(animationName)) {
@@ -414,14 +408,14 @@
/**
* Returns whether or not any layers in this composition has masks.
*/
- @SuppressWarnings("unused") public boolean hasMasks() {
+ public boolean hasMasks() {
return lottieDrawable.hasMasks();
}
/**
* Returns whether or not any layers in this composition has a matte layer.
*/
- @SuppressWarnings("unused") public boolean hasMatte() {
+ public boolean hasMatte() {
return lottieDrawable.hasMatte();
}
@@ -429,7 +423,6 @@
lottieDrawable.addAnimatorUpdateListener(updateListener);
}
- @SuppressWarnings("unused")
public void removeUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) {
lottieDrawable.removeAnimatorUpdateListener(updateListener);
}
@@ -438,7 +431,6 @@
lottieDrawable.addAnimatorListener(listener);
}
- @SuppressWarnings("unused")
public void removeAnimatorListener(Animator.AnimatorListener listener) {
lottieDrawable.removeAnimatorListener(listener);
}
@@ -461,17 +453,50 @@
enableOrDisableHardwareLayer();
}
- @SuppressWarnings("unused") public void reverseAnimation() {
+ public void playAnimation(final int startFrame, final int endFrame) {
+ lottieDrawable.playAnimation(startFrame, endFrame);
+ }
+
+ public void playAnimation(@FloatRange(from = 0f, to = 1f) float startProgress,
+ @FloatRange(from = 0f, to = 1f) float endProgress) {
+ lottieDrawable.playAnimation(startProgress, endProgress);
+ }
+
+ public void reverseAnimation() {
lottieDrawable.reverseAnimation();
enableOrDisableHardwareLayer();
}
- @SuppressWarnings("unused") public void resumeReverseAnimation() {
+ public void setMinFrame(int startFrame) {
+ lottieDrawable.setMinFrame(startFrame);
+ }
+
+ public void setMinProgress(float startProgress) {
+ lottieDrawable.setMinProgress(startProgress);
+ }
+
+ public void setMaxFrame(int endFrame) {
+ lottieDrawable.setMaxFrame(endFrame);
+ }
+
+ public void setMaxProgress(float endProgress) {
+ lottieDrawable.setMaxProgress(endProgress);
+ }
+
+ public void setMinAndMaxFrame(int minFrame, int maxFrame) {
+ lottieDrawable.setMinAndMaxFrame(minFrame, maxFrame);
+ }
+
+ public void setMinAndMaxProgress(float minProgress, float maxProgress) {
+ lottieDrawable.setMinAndMaxProgress(minProgress, maxProgress);
+ }
+
+ public void resumeReverseAnimation() {
lottieDrawable.resumeReverseAnimation();
enableOrDisableHardwareLayer();
}
- @SuppressWarnings("unused") public void setSpeed(float speed) {
+ public void setSpeed(float speed) {
lottieDrawable.setSpeed(speed);
}
@@ -483,7 +508,7 @@
* If your images are located in src/main/assets/airbnb_loader/ then call
* `setImageAssetsFolder("airbnb_loader/");`.
*/
- @SuppressWarnings("WeakerAccess") public void setImageAssetsFolder(String imageAssetsFolder) {
+ public void setImageAssetsFolder(String imageAssetsFolder) {
lottieDrawable.setImagesAssetsFolder(imageAssetsFolder);
}
@@ -499,7 +524,6 @@
* @return the previous Bitmap or null.
*/
@Nullable
- @SuppressWarnings({"unused", "WeakerAccess"})
public Bitmap updateBitmap(String id, @Nullable Bitmap bitmap) {
return lottieDrawable.updateBitmap(id, bitmap);
}
@@ -509,14 +533,14 @@
* animations from the network or have the images saved to an SD Card. In that case, Lottie
* will defer the loading of the bitmap to this delegate.
*/
- @SuppressWarnings("unused") public void setImageAssetDelegate(ImageAssetDelegate assetDelegate) {
+ public void setImageAssetDelegate(ImageAssetDelegate assetDelegate) {
lottieDrawable.setImageAssetDelegate(assetDelegate);
}
/**
* Use this to manually set fonts.
*/
- @SuppressWarnings({"unused", "WeakerAccess"}) public void setFontAssetDelegate(
+ public void setFontAssetDelegate(
@SuppressWarnings("NullableProblems") FontAssetDelegate assetDelegate) {
lottieDrawable.setFontAssetDelegate(assetDelegate);
}
@@ -569,7 +593,7 @@
return lottieDrawable.getProgress();
}
- @SuppressWarnings("unused") public long getDuration() {
+ public long getDuration() {
return composition != null ? composition.getDuration() : 0;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 087c1ee..7879cd2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -33,7 +33,7 @@
* handles bitmap recycling and asynchronous loading
* of compositions.
*/
-public class LottieDrawable extends Drawable implements Drawable.Callback {
+@SuppressWarnings({"WeakerAccess", "unused"}) public class LottieDrawable extends Drawable implements Drawable.Callback {
private static final String TAG = LottieDrawable.class.getSimpleName();
private interface LazyCompositionTask {
@@ -42,7 +42,7 @@
private final Matrix matrix = new Matrix();
private LottieComposition composition;
- private final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ private final LottieValueAnimator animator = new LottieValueAnimator();
private float speed = 1f;
private float progress = 0f;
private float scale = 1f;
@@ -61,7 +61,7 @@
private int alpha = 255;
private boolean performanceTrackingEnabled;
- @SuppressWarnings("WeakerAccess") public LottieDrawable() {
+ public LottieDrawable() {
animator.setRepeatCount(0);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -79,14 +79,14 @@
/**
* Returns whether or not any layers in this composition has masks.
*/
- @SuppressWarnings({"unused", "WeakerAccess"}) public boolean hasMasks() {
+ public boolean hasMasks() {
return compositionLayer != null && compositionLayer.hasMasks();
}
/**
* Returns whether or not any layers in this composition has a matte layer.
*/
- @SuppressWarnings({"unused", "WeakerAccess"}) public boolean hasMatte() {
+ public boolean hasMatte() {
return compositionLayer != null && compositionLayer.hasMatte();
}
@@ -101,7 +101,7 @@
* first shape. If you need to cut out one shape from another shape, use an even-odd fill type
* instead of using merge paths.
*/
- @SuppressWarnings("WeakerAccess") public void enableMergePathsForKitKatAndAbove(boolean enable) {
+ public void enableMergePathsForKitKatAndAbove(boolean enable) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Log.w(TAG, "Merge paths are not supported pre-Kit Kat.");
return;
@@ -125,11 +125,11 @@
* are done. Calling {@link #recycleBitmaps()} doesn't have to be final and {@link LottieDrawable}
* will recreate the bitmaps if needed but they will leak if you don't recycle them.
*/
- @SuppressWarnings("WeakerAccess") public void setImagesAssetsFolder(@Nullable String imageAssetsFolder) {
+ public void setImagesAssetsFolder(@Nullable String imageAssetsFolder) {
this.imageAssetsFolder = imageAssetsFolder;
}
- @SuppressWarnings("WeakerAccess") @Nullable public String getImageAssetsFolder() {
+ @Nullable public String getImageAssetsFolder() {
return imageAssetsFolder;
}
@@ -140,7 +140,7 @@
* will recreate the bitmaps if needed but they will leak if you don't recycle them.
*
*/
- @SuppressWarnings("WeakerAccess") public void recycleBitmaps() {
+ public void recycleBitmaps() {
if (imageAssetManager != null) {
imageAssetManager.recycleBitmaps();
}
@@ -149,7 +149,7 @@
/**
* @return True if the composition is different from the previously set composition, false otherwise.
*/
- @SuppressWarnings("WeakerAccess") public boolean setComposition(LottieComposition composition) {
+ public boolean setComposition(LottieComposition composition) {
if (this.composition == composition) {
return false;
}
@@ -173,7 +173,7 @@
return true;
}
- @SuppressWarnings("WeakerAccess") public void setPerformanceTrackingEnabled(boolean enabled) {
+ public void setPerformanceTrackingEnabled(boolean enabled) {
performanceTrackingEnabled = enabled;
if (composition != null) {
composition.setPerformanceTrackingEnabled(enabled);
@@ -235,7 +235,7 @@
* @param contentName name of the specific content that the color filter is to be applied
* @param colorFilter the color filter, null to clear the color filter
*/
- @SuppressWarnings("WeakerAccess") public void addColorFilterToContent(String layerName, String contentName,
+ public void addColorFilterToContent(String layerName, String contentName,
@Nullable ColorFilter colorFilter) {
addColorFilterInternal(layerName, contentName, colorFilter);
}
@@ -245,7 +245,7 @@
* @param layerName name of the layer that the color filter is to be applied
* @param colorFilter the color filter, null to clear the color filter
*/
- @SuppressWarnings("WeakerAccess") public void addColorFilterToLayer(String layerName, @Nullable ColorFilter colorFilter) {
+ public void addColorFilterToLayer(String layerName, @Nullable ColorFilter colorFilter) {
addColorFilterInternal(layerName, null, colorFilter);
}
@@ -260,7 +260,7 @@
/**
* Clear all color filters on all layers and all content in the layers
*/
- @SuppressWarnings("WeakerAccess") public void clearColorFilters() {
+ public void clearColorFilters() {
colorFilterData.clear();
addColorFilterInternal(null, null, null);
}
@@ -339,23 +339,23 @@
systemAnimationsAreDisabled = true;
}
- @SuppressWarnings("WeakerAccess") public void loop(boolean loop) {
+ public void loop(boolean loop) {
animator.setRepeatCount(loop ? ValueAnimator.INFINITE : 0);
}
- @SuppressWarnings("WeakerAccess") public boolean isLooping() {
+ public boolean isLooping() {
return animator.getRepeatCount() == ValueAnimator.INFINITE;
}
- @SuppressWarnings("WeakerAccess") public boolean isAnimating() {
+ public boolean isAnimating() {
return animator.isRunning();
}
- @SuppressWarnings("WeakerAccess") public void playAnimation() {
+ public void playAnimation() {
playAnimation((progress > 0.0 && progress < 1.0));
}
- @SuppressWarnings("WeakerAccess") public void resumeAnimation() {
+ public void resumeAnimation() {
playAnimation(true);
}
@@ -375,11 +375,33 @@
}
}
- @SuppressWarnings({"unused", "WeakerAccess"}) public void resumeReverseAnimation() {
+ public void playAnimation(final int startFrame, final int endFrame) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override public void run(LottieComposition composition) {
+ playAnimation(startFrame / composition.getDurationFrames(),
+ endFrame / composition.getDurationFrames());
+ }
+ });
+ return;
+ }
+ playAnimation(startFrame / composition.getDurationFrames(),
+ endFrame / composition.getDurationFrames());
+ }
+
+ public void playAnimation(@FloatRange(from = 0f, to = 1f) float startProgress,
+ @FloatRange(from = 0f, to = 1f) float endProgress) {
+ animator.updateValues(startProgress, endProgress);
+ animator.setCurrentPlayTime(0);
+ setProgress(startProgress);
+ playAnimation(false);
+ }
+
+ public void resumeReverseAnimation() {
reverseAnimation(true);
}
- @SuppressWarnings("WeakerAccess") public void reverseAnimation() {
+ public void reverseAnimation() {
reverseAnimation((progress > 0.0 && progress < 1.0));
}
@@ -398,13 +420,51 @@
animator.reverse();
}
- @SuppressWarnings("WeakerAccess") public void setSpeed(float speed) {
- this.speed = speed;
- if (speed < 0) {
- animator.setFloatValues(1f, 0f);
- } else {
- animator.setFloatValues(0f, 1f);
+ public void setMinFrame(final int startFrame) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override public void run(LottieComposition composition) {
+ setMinProgress(startFrame / composition.getDurationFrames());
+ }
+ });
+ return;
}
+ setMinProgress(startFrame / composition.getDurationFrames());
+ }
+
+ public void setMinProgress(float startProgress) {
+ animator.setStartProgress(startProgress);
+ }
+
+ public void setMaxFrame(final int endFrame) {
+ if (composition == null) {
+ lazyCompositionTasks.add(new LazyCompositionTask() {
+ @Override public void run(LottieComposition composition) {
+ setMaxProgress(endFrame / composition.getDurationFrames());
+ }
+ });
+ return;
+ }
+ setMaxProgress(endFrame / composition.getDurationFrames());
+ }
+
+ public void setMaxProgress(float endProgress) {
+ animator.setEndProgress(endProgress);
+ }
+
+ public void setMinAndMaxFrame(int minFrame, int maxFrame) {
+ setMinFrame(minFrame);
+ setMaxFrame(maxFrame);
+ }
+
+ public void setMinAndMaxProgress(float minProgress, float maxProgress) {
+ setMinProgress(minProgress);
+ setMaxProgress(maxProgress);
+ }
+
+ public void setSpeed(float speed) {
+ this.speed = speed;
+ animator.setIsReversed(speed < 0);
if (composition != null) {
animator.setDuration((long) (composition.getDuration() / Math.abs(speed)));
@@ -431,7 +491,7 @@
* with a scaleType such as centerInside will yield better performance with little perceivable
* quality loss.
*/
- @SuppressWarnings("WeakerAccess") public void setScale(float scale) {
+ public void setScale(float scale) {
this.scale = scale;
updateBounds();
}
@@ -441,7 +501,7 @@
* animations from the network or have the images saved to an SD Card. In that case, Lottie
* will defer the loading of the bitmap to this delegate.
*/
- @SuppressWarnings({"unused", "WeakerAccess"}) public void setImageAssetDelegate(
+ public void setImageAssetDelegate(
@SuppressWarnings("NullableProblems") ImageAssetDelegate assetDelegate) {
this.imageAssetDelegate = assetDelegate;
if (imageAssetManager != null) {
@@ -452,7 +512,7 @@
/**
* Use this to manually set fonts.
*/
- @SuppressWarnings({"unused", "WeakerAccess"}) public void setFontAssetDelegate(
+ public void setFontAssetDelegate(
@SuppressWarnings("NullableProblems") FontAssetDelegate assetDelegate) {
this.fontAssetDelegate = assetDelegate;
if (fontAssetManager != null) {
@@ -460,7 +520,6 @@
}
}
- @SuppressWarnings("WeakerAccess")
public void setTextDelegate(@SuppressWarnings("NullableProblems") TextDelegate textDelegate) {
this.textDelegate = textDelegate;
}
@@ -473,11 +532,11 @@
return textDelegate == null && composition.getCharacters().size() > 0;
}
- @SuppressWarnings("WeakerAccess") public float getScale() {
+ public float getScale() {
return scale;
}
- @SuppressWarnings("WeakerAccess") public LottieComposition getComposition() {
+ public LottieComposition getComposition() {
return composition;
}
@@ -490,24 +549,24 @@
(int) (composition.getBounds().height() * scale));
}
- @SuppressWarnings("WeakerAccess") public void cancelAnimation() {
+ public void cancelAnimation() {
lazyCompositionTasks.clear();
animator.cancel();
}
- @SuppressWarnings("WeakerAccess") public void addAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) {
+ public void addAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) {
animator.addUpdateListener(updateListener);
}
- @SuppressWarnings("WeakerAccess") public void removeAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) {
+ public void removeAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) {
animator.removeUpdateListener(updateListener);
}
- @SuppressWarnings("WeakerAccess") public void addAnimatorListener(Animator.AnimatorListener listener) {
+ public void addAnimatorListener(Animator.AnimatorListener listener) {
animator.addListener(listener);
}
- @SuppressWarnings("WeakerAccess") public void removeAnimatorListener(Animator.AnimatorListener listener) {
+ public void removeAnimatorListener(Animator.AnimatorListener listener) {
animator.removeListener(listener);
}
@@ -526,7 +585,6 @@
* @return the previous Bitmap or null.
*/
@Nullable
- @SuppressWarnings({"unused", "WeakerAccess"})
public Bitmap updateBitmap(String id, @Nullable Bitmap bitmap) {
ImageAssetManager bm = getImageAssetManager();
if (bm == null) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieValueAnimator.java b/lottie/src/main/java/com/airbnb/lottie/LottieValueAnimator.java
new file mode 100644
index 0000000..0190866
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieValueAnimator.java
@@ -0,0 +1,78 @@
+package com.airbnb.lottie;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+/**
+ * This is a slightly modified {@link ValueAnimator} that allows us to update start and end values
+ * easily optimizing for the fact that we know that it's a value animator with 2 floats.
+ */
+class LottieValueAnimator extends ValueAnimator {
+ private boolean isReversed = false;
+ private float startProgress = 0f;
+ private float endProgress = 1f;
+ private long duration;
+
+ LottieValueAnimator() {
+ setFloatValues(0f, 1f);
+
+ /*
+ This allows us to reset the values if they were temporarily reset by
+ updateValues(float, float, long, boolean)
+ */
+ addListener(new AnimatorListenerAdapter() {
+ @Override public void onAnimationEnd(Animator animation) {
+ updateValues();
+ }
+
+ @Override public void onAnimationCancel(Animator animation) {
+ updateValues();
+ }
+ });
+ }
+
+ @Override public ValueAnimator setDuration(long duration) {
+ this.duration = duration;
+ updateValues();
+ return this;
+ }
+
+ @Override public long getDuration() {
+ return duration;
+ }
+
+ void setIsReversed(boolean isReversed) {
+ this.isReversed = isReversed;
+ updateValues();
+ }
+
+ void setStartProgress(float startProgress) {
+ this.startProgress = startProgress;
+ updateValues();
+ }
+
+ void setEndProgress(float endProgress) {
+ this.endProgress = endProgress;
+ updateValues();
+ }
+
+ /**
+ * This lets you set the start and end progress for a single play of the animator. After the next
+ * time the animation ends or is cancelled, the values will be reset to those set by
+ * {@link #setStartProgress(float)} or {@link #setEndProgress(float)}.
+ */
+ void updateValues(float startProgress, float endProgress) {
+ float minValue = Math.min(startProgress, endProgress);
+ float maxValue = Math.max(startProgress, endProgress);
+ setFloatValues(
+ isReversed ? maxValue : minValue,
+ isReversed ? minValue : maxValue
+ );
+ super.setDuration((long) (duration * (maxValue - minValue)));
+ }
+
+ private void updateValues() {
+ updateValues(startProgress, endProgress);
+ }
+}