diff --git a/CHANGELOG.md b/CHANGELOG.md index b8f19b47..be426a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ #### Changed * `html` - `SimpleTagHandler` visits children tags if supplied tag is block one ([#235]) * `inline-parser` - `BangInlineProcessor` properly returns `null` if no image node is found (possible to define other inline parsers that use `!` as special character) +* `image` - `AsyncDrawable` won't trigger loading if it has result (aim: `RecyclerView` due to multiple attach/detach events of a View) +* `image` - `AsyncDrawable` will resume result if it is `Animatable` and was playing before detach event (aim: `RecyclerView`) ([#241]) #### Fixed * `image-glide` cache `RequestManager` in `GlideImagesPlugin#create(Context)` factory method ([#259]) @@ -20,6 +22,7 @@ * `image` - `AsyncDrawable#hasKnownDimentions` (deprecated in `4.2.1`) [#235]: https://github.com/noties/Markwon/issues/235 +[#241]: https://github.com/noties/Markwon/issues/241 [#259]: https://github.com/noties/Markwon/issues/259 diff --git a/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawable.java b/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawable.java index 686ecd25..330403b8 100644 --- a/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawable.java +++ b/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawable.java @@ -18,6 +18,9 @@ public class AsyncDrawable extends Drawable { private final ImageSize imageSize; private final ImageSizeResolver imageSizeResolver; + // @since $nap; + private final Drawable placeholder; + private Drawable result; private Callback callback; @@ -27,6 +30,10 @@ public class AsyncDrawable extends Drawable { // @since 2.0.1 for use-cases when image is loaded faster than span is drawn and knows canvas width private boolean waitingForDimensions; + // @since $nap; in case if result is Animatable and this drawable was detached, we + // keep the state to resume when we are going to be attached again (when used in RecyclerView) + private boolean wasPlayingBefore = false; + /** * @since 1.0.1 */ @@ -41,7 +48,7 @@ public class AsyncDrawable extends Drawable { this.imageSizeResolver = imageSizeResolver; this.imageSize = imageSize; - final Drawable placeholder = loader.placeholder(this); + final Drawable placeholder = this.placeholder = loader.placeholder(this); if (placeholder != null) { setPlaceholderResult(placeholder); } @@ -99,7 +106,6 @@ public class AsyncDrawable extends Drawable { return result; } - @SuppressWarnings("WeakerAccess") public boolean hasResult() { return result != null; } @@ -108,8 +114,6 @@ public class AsyncDrawable extends Drawable { return getCallback() != null; } - // yeah - @SuppressWarnings("WeakerAccess") public void setCallback2(@Nullable Callback cb) { // @since 4.2.1 @@ -132,7 +136,21 @@ public class AsyncDrawable extends Drawable { result.setCallback(callback); } - loader.load(this); + // @since $nap; we trigger loading only if we have no result (and result is not placeholder) + final boolean shouldLoad = result == null || result == placeholder; + + if (result != null) { + result.setCallback(callback); + + // @since $nap; + if (result instanceof Animatable && wasPlayingBefore) { + ((Animatable) result).start(); + } + } + + if (shouldLoad) { + loader.load(this); + } } else { if (result != null) { @@ -140,9 +158,14 @@ public class AsyncDrawable extends Drawable { // let's additionally stop if it Animatable if (result instanceof Animatable) { - ((Animatable) result).stop(); + final Animatable animatable = (Animatable) result; + final boolean isPlaying = wasPlayingBefore = animatable.isRunning(); + if (isPlaying) { + animatable.stop(); + } } } + loader.cancel(this); } } @@ -206,6 +229,9 @@ public class AsyncDrawable extends Drawable { public void setResult(@NonNull Drawable result) { + // @since $nap; revert this flag when we have new source + wasPlayingBefore = false; + // if we have previous one, detach it if (this.result != null) { this.result.setCallback(null); @@ -250,6 +276,7 @@ public class AsyncDrawable extends Drawable { waitingForDimensions = false; final Rect bounds = resolveBounds(); + result.setBounds(bounds); // @since 4.2.1, we set callback after bounds are resolved // to reduce number of invalidations diff --git a/sample/build.gradle b/sample/build.gradle index b87cab5e..81d14d9e 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -60,6 +60,7 @@ dependencies { implementation it['debug'] implementation it['adapt'] implementation it['android-svg'] + implementation it['android-gif'] } deps['annotationProcessor'].with { diff --git a/sample/src/main/java/io/noties/markwon/sample/recycler/RecyclerActivity.java b/sample/src/main/java/io/noties/markwon/sample/recycler/RecyclerActivity.java index c1424e6d..3d73de6d 100644 --- a/sample/src/main/java/io/noties/markwon/sample/recycler/RecyclerActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/recycler/RecyclerActivity.java @@ -3,12 +3,14 @@ package io.noties.markwon.sample.recycler; import android.app.Activity; import android.content.Context; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.text.style.UpdateAppearance; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -87,8 +89,11 @@ public class RecyclerActivity extends Activity { plugin .addSchemeHandler(FileSchemeHandler.createWithAssets(context)) .addSchemeHandler(OkHttpNetworkSchemeHandler.create()) - .addMediaDecoder(SvgMediaDecoder.create()) - .placeholderProvider(drawable -> new ColorDrawable(0xFFff0000)); + .placeholderProvider(drawable -> { + final Drawable placeholder = new ColorDrawable(0xFFff0000); + placeholder.setBounds(0, 0, 100, 100); + return placeholder; + }); })) // .usePlugin(PicassoImagesPlugin.create(context)) // .usePlugin(GlideImagesPlugin.create(context)) @@ -122,6 +127,8 @@ public class RecyclerActivity extends Activity { // `RemoveUnderlineSpan` will be added AFTER original, thus it will remove underline applied by original builder.appendFactory(Link.class, (configuration, props) -> new RemoveUnderlineSpan()); } + + }) .build(); }