diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java index 1cf285dc2..851a4b3a4 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java @@ -17,12 +17,10 @@ package com.hippo.ehviewer.ui.scene; import android.animation.Animator; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; @@ -35,7 +33,6 @@ import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; @@ -66,6 +63,9 @@ import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.ViewUtils; +import io.codetail.animation.SupportAnimator; +import io.codetail.animation.ViewAnimationUtils; + public final class GalleryCommentsScene extends ToolbarScene implements EasyRecyclerView.OnItemClickListener, EasyRecyclerView.OnItemLongClickListener, View.OnClickListener { @@ -236,8 +236,7 @@ public final class GalleryCommentsScene extends ToolbarScene return false; } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void showEditPanelWithAnimationL() { + private void showEditPanelWithAnimation() { mInAnimation = true; mFab.setTranslationX(0.0f); mFab.setTranslationY(0.0f); @@ -254,11 +253,12 @@ public final class GalleryCommentsScene extends ToolbarScene mEditPanel.setVisibility(View.VISIBLE); int halfW = mEditPanel.getWidth() / 2; int halfH = mEditPanel.getHeight() / 2; - Animator animator = ViewAnimationUtils.createCircularReveal(mEditPanel, halfW, halfH, 0, - (float) Math.hypot(halfW, halfH)).setDuration(300); - animator.addListener(new SimpleAnimatorListener() { + SupportAnimator animator = ViewAnimationUtils.createCircularReveal(mEditPanel, halfW, halfH, 0, + (float) Math.hypot(halfW, halfH)); + animator.setDuration(300L); + animator.addListener(new SupportAnimator.SimpleAnimatorListener() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationEnd() { mInAnimation = false; } }); @@ -269,29 +269,23 @@ public final class GalleryCommentsScene extends ToolbarScene private void showEditPanel(boolean animation) { if (animation) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - showEditPanelWithAnimationL(); - } else { - // TODO Add animation for pre-L - mFab.setVisibility(View.INVISIBLE); - mEditPanel.setVisibility(View.VISIBLE); - } + showEditPanelWithAnimation(); } else { mFab.setVisibility(View.INVISIBLE); mEditPanel.setVisibility(View.VISIBLE); } } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void hideEditPanelWithAnimationL() { + private void hideEditPanelWithAnimation() { mInAnimation = true; int halfW = mEditPanel.getWidth() / 2; int halfH = mEditPanel.getHeight() / 2; - Animator animator = ViewAnimationUtils.createCircularReveal(mEditPanel, halfW, halfH, - (float) Math.hypot(halfW, halfH), 0.0f).setDuration(300L); - animator.addListener(new SimpleAnimatorListener() { + SupportAnimator animator = ViewAnimationUtils.createCircularReveal(mEditPanel, halfW, halfH, + (float) Math.hypot(halfW, halfH), 0.0f); + animator.setDuration(300L); + animator.addListener(new SupportAnimator.SimpleAnimatorListener() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationEnd() { mEditPanel.setVisibility(View.GONE); mFab.setVisibility(View.VISIBLE); int fabStartX = mEditPanel.getLeft() + (mEditPanel.getWidth() / 2) - (mFab.getWidth() / 2); @@ -315,13 +309,7 @@ public final class GalleryCommentsScene extends ToolbarScene private void hideEditPanel(boolean animation) { if (animation) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - hideEditPanelWithAnimationL(); - } else { - // TODO Add animation for pre-L - mFab.setVisibility(View.VISIBLE); - mEditPanel.setVisibility(View.INVISIBLE); - } + hideEditPanelWithAnimation(); } else { mFab.setVisibility(View.VISIBLE); mEditPanel.setVisibility(View.INVISIBLE); diff --git a/app/src/main/java/io/codetail/animation/RevealAnimator.java b/app/src/main/java/io/codetail/animation/RevealAnimator.java new file mode 100644 index 000000000..4624337e0 --- /dev/null +++ b/app/src/main/java/io/codetail/animation/RevealAnimator.java @@ -0,0 +1,147 @@ +package io.codetail.animation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.graphics.Rect; +import android.util.Property; +import android.view.View; + +import java.lang.ref.WeakReference; + +/** + * @hide + */ +public interface RevealAnimator{ + + RevealRadius CLIP_RADIUS = new RevealRadius(); + + /** + * Listen when animation start/end/cancel + * and setup view for it + */ + void onRevealAnimationStart(); + void onRevealAnimationEnd(); + void onRevealAnimationCancel(); + + /** + * Used with animator to animate view clipping + * + * @param value clip radius + */ + void setRevealRadius(float value); + + /** + * Used with animator to animate view clipping + * + * @return current radius + */ + float getRevealRadius(); + + /** + * Invalidate certain rectangle + * + * @param bounds bounds to redraw + * @see View#invalidate(Rect) + */ + void invalidate(Rect bounds); + + /** + * {@link ViewAnimationUtils#createCircularReveal(View, int, int, float, float)} is + * called it creates new {@link io.codetail.animation.RevealAnimator.RevealInfo} + * and attaches to parent, here is necessary data about animation + * + * @param info reveal information + * + * @see RevealAnimator.RevealInfo + */ + void attachRevealInfo(RevealInfo info); + + /** + * Returns new {@link SupportAnimator} that plays + * reversed animation of current one + * + * This method might be temporary, you should call + * {@link SupportAnimator#reverse()} instead + * + * @hide + * @return reverse {@link SupportAnimator} + * + * @see SupportAnimator#reverse() + */ + SupportAnimator startReverseAnimation(); + + class RevealInfo{ + public final int centerX; + public final int centerY; + public final float startRadius; + public final float endRadius; + public final WeakReference target; + + public RevealInfo(int centerX, int centerY, float startRadius, float endRadius, + WeakReference target) { + this.centerX = centerX; + this.centerY = centerY; + this.startRadius = startRadius; + this.endRadius = endRadius; + this.target = target; + } + + public View getTarget(){ + return target.get(); + } + + public boolean hasTarget(){ + return getTarget() != null; + } + } + + class RevealFinishedIceCreamSandwich extends AnimatorListenerAdapter { + WeakReference mReference; + int mFeaturedLayerType; + int mLayerType; + + RevealFinishedIceCreamSandwich(RevealAnimator target, int layerType) { + mReference = new WeakReference<>(target); + mLayerType = ((View) target).getLayerType(); + mFeaturedLayerType = layerType; + } + + @Override + public void onAnimationStart(Animator animation) { + RevealAnimator target = mReference.get(); + ((View) target).setLayerType(mFeaturedLayerType, null); + target.onRevealAnimationStart(); + } + + @Override + public void onAnimationCancel(Animator animation) { + RevealAnimator target = mReference.get(); + ((View) target).setLayerType(mLayerType, null); + target.onRevealAnimationCancel(); + } + + @Override + public void onAnimationEnd(Animator animation) { + RevealAnimator target = mReference.get(); + ((View) target).setLayerType(mLayerType, null); + target.onRevealAnimationEnd(); + } + } + + class RevealRadius extends Property { + + public RevealRadius() { + super(Float.class, "revealRadius"); + } + + @Override + public void set(RevealAnimator object, Float value) { + object.setRevealRadius(value); + } + + @Override + public Float get(RevealAnimator object) { + return object.getRevealRadius(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/codetail/animation/SupportAnimator.java b/app/src/main/java/io/codetail/animation/SupportAnimator.java new file mode 100644 index 000000000..b9b8e30dd --- /dev/null +++ b/app/src/main/java/io/codetail/animation/SupportAnimator.java @@ -0,0 +1,186 @@ +package io.codetail.animation; + +import android.animation.Animator; + +import java.lang.ref.WeakReference; + +public abstract class SupportAnimator extends Animator{ + + WeakReference mTarget; + + public SupportAnimator(RevealAnimator target) { + mTarget = new WeakReference<>(target); + } + + /** + * @return true if using native android animation framework, otherwise is + * nineoldandroids + */ + public abstract boolean isNativeAnimator(); + + /** + * @return depends from {@link android.os.Build.VERSION} if sdk version + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and greater will return + * {@link android.animation.Animator} + */ + public abstract Object get(); + + /** + * Starts this animation. If the animation has a nonzero startDelay, the animation will start + * running after that delay elapses. A non-delayed animation will have its initial + * value(s) set immediately, followed by calls to + * {@link android.animation.Animator.AnimatorListener#onAnimationStart(android.animation.Animator)} + * for any listeners of this animator. + * + *

The animation started by calling this method will be run on the thread that called + * this method. This thread should have a Looper on it (a runtime exception will be thrown if + * this is not the case). Also, if the animation will animate + * properties of objects in the view hierarchy, then the calling thread should be the UI + * thread for that view hierarchy.

+ * + */ + @Override + public abstract void start(); + + + /** + * Adds a listener to the set of listeners that are sent events through the life of an + * animation, such as start, repeat, and end. + * + * @param listener the listener to be added to the current set of listeners for this animation. + */ + public abstract void addListener(AnimatorListener listener); + + + /** + * Returns whether this Animator is currently running (having been started and gone past any + * initial startDelay period and not yet ended). + * + * @return Whether the Animator is running. + */ + @Override + public abstract boolean isRunning(); + + + /** + * Cancels the animation. Unlike {@link #end()}, cancel() causes the animation to + * stop in its tracks, sending an + * {@link AnimatorListener#onAnimationCancel()} to + * its listeners, followed by an + * {@link AnimatorListener#onAnimationEnd()} message. + * + *

This method must be called on the thread that is running the animation.

+ */ + @Override + public abstract void cancel(); + + /** + * Ends the animation. This causes the animation to assign the end value of the property being + * animated, then calling the + * {@link AnimatorListener#onAnimationEnd()} method on + * its listeners. + * + *

This method must be called on the thread that is running the animation.

+ */ + @Override + public void end() { + } + + /** + * This method tells the object to use appropriate information to extract + * starting values for the animation. For example, a AnimatorSet object will pass + * this call to its child objects to tell them to set up the values. A + * ObjectAnimator object will use the information it has about its target object + * and PropertyValuesHolder objects to get the start values for its properties. + * A ValueAnimator object will ignore the request since it does not have enough + * information (such as a target object) to gather these values. + */ + @Override + public void setupStartValues() { + } + + /** + * This method tells the object to use appropriate information to extract + * ending values for the animation. For example, a AnimatorSet object will pass + * this call to its child objects to tell them to set up the values. A + * ObjectAnimator object will use the information it has about its target object + * and PropertyValuesHolder objects to get the start values for its properties. + * A ValueAnimator object will ignore the request since it does not have enough + * information (such as a target object) to gather these values. + */ + @Override + public void setupEndValues() { + } + + /** + * Experimental feature + */ + public SupportAnimator reverse() { + if(isRunning()){ + return null; + } + + RevealAnimator target = mTarget.get(); + if(target != null){ + return target.startReverseAnimation(); + } + + return null; + } + + /** + *

An animation listener receives notifications from an animation. + * Notifications indicate animation related events, such as the end or the + * repetition of the animation.

+ */ + public interface AnimatorListener { + /** + *

Notifies the start of the animation.

+ */ + void onAnimationStart(); + + /** + *

Notifies the end of the animation. This callback is not invoked + * for animations with repeat count set to INFINITE.

+ */ + void onAnimationEnd(); + + /** + *

Notifies the cancellation of the animation. This callback is not invoked + * for animations with repeat count set to INFINITE.

+ */ + void onAnimationCancel(); + + /** + *

Notifies the repetition of the animation.

+ */ + void onAnimationRepeat(); + } + + /** + *

Provides default implementation for AnimatorListener.

+ */ + public static abstract class SimpleAnimatorListener implements AnimatorListener { + + @Override + public void onAnimationStart() { + + } + + @Override + public void onAnimationEnd() { + + } + + @Override + public void onAnimationCancel() { + + } + + @Override + public void onAnimationRepeat() { + + } + } + +} diff --git a/app/src/main/java/io/codetail/animation/SupportAnimatorImpl.java b/app/src/main/java/io/codetail/animation/SupportAnimatorImpl.java new file mode 100644 index 000000000..de0214ca3 --- /dev/null +++ b/app/src/main/java/io/codetail/animation/SupportAnimatorImpl.java @@ -0,0 +1,154 @@ +package io.codetail.animation; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.annotation.TargetApi; +import android.os.Build; + +import java.lang.ref.WeakReference; + +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +final class SupportAnimatorImpl extends SupportAnimator{ + + WeakReference mAnimator; + + SupportAnimatorImpl(Animator animator, RevealAnimator target) { + super(target); + mAnimator = new WeakReference<>(animator); + } + + @Override + public boolean isNativeAnimator() { + return true; + } + + @Override + public Object get() { + return mAnimator.get(); + } + + + @Override + public void start() { + Animator a = mAnimator.get(); + if(a != null) { + a.start(); + } + } + + @Override + public Animator setDuration(long duration) { + Animator a = mAnimator.get(); + if(a != null) { + a.setDuration(duration); + } + return this; + } + + @Override + public void setInterpolator(TimeInterpolator value) { + Animator a = mAnimator.get(); + if(a != null) { + a.setInterpolator(value); + } + } + + @Override + public void addListener(final AnimatorListener listener) { + Animator a = mAnimator.get(); + if(a == null) { + return; + } + + if(listener == null){ + a.addListener(null); + return; + } + + a.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + listener.onAnimationStart(); + } + + @Override + public void onAnimationEnd(Animator animation) { + listener.onAnimationEnd(); + } + + @Override + public void onAnimationCancel(Animator animation) { + listener.onAnimationCancel(); + } + + @Override + public void onAnimationRepeat(Animator animation) { + listener.onAnimationRepeat(); + } + }); + } + + @Override + public boolean isRunning() { + Animator a = mAnimator.get(); + return a != null && a.isRunning(); + } + + @Override + public void cancel() { + Animator a = mAnimator.get(); + if(a != null){ + a.cancel(); + } + } + + @Override + public void end() { + Animator a = mAnimator.get(); + if(a != null){ + a.end(); + } + } + + @Override + public long getStartDelay() { + Animator a = mAnimator.get(); + if(a != null){ + return a.getStartDelay(); + } + return 0; + } + + @Override + public void setStartDelay(long startDelay) { + Animator a = mAnimator.get(); + if(a != null){ + a.setStartDelay(startDelay); + } + } + + @Override + public long getDuration() { + Animator a = mAnimator.get(); + if(a != null){ + return a.getDuration(); + } + return 0; + } + + @Override + public void setupStartValues() { + Animator a = mAnimator.get(); + if(a != null){ + a.setupStartValues(); + } + } + + @Override + public void setupEndValues() { + Animator a = mAnimator.get(); + if(a != null){ + a.setupEndValues(); + } + } +} diff --git a/app/src/main/java/io/codetail/animation/ViewAnimationUtils.java b/app/src/main/java/io/codetail/animation/ViewAnimationUtils.java new file mode 100644 index 000000000..159c61fda --- /dev/null +++ b/app/src/main/java/io/codetail/animation/ViewAnimationUtils.java @@ -0,0 +1,157 @@ +package io.codetail.animation; + +import android.animation.ObjectAnimator; +import android.annotation.TargetApi; +import android.os.Build; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; + +import java.lang.ref.WeakReference; + +import io.codetail.animation.RevealAnimator.RevealInfo; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static io.codetail.animation.RevealAnimator.CLIP_RADIUS; + +public class ViewAnimationUtils { + + private final static boolean LOLLIPOP_PLUS = SDK_INT >= LOLLIPOP; + + public static final int SCALE_UP_DURATION = 500; + + /** + * Returns an Animator which can animate a clipping circle. + *

+ * Any shadow cast by the View will respect the circular clip from this animator. + *

+ * Only a single non-rectangular clip can be applied on a View at any time. + * Views clipped by a circular reveal animation take priority over + * {@link android.view.View#setClipToOutline(boolean) View Outline clipping}. + *

+ * Note that the animation returned here is a one-shot animation. It cannot + * be re-used, and once started it cannot be paused or resumed. + * + * @param view The View will be clipped to the animating circle. + * @param centerX The x coordinate of the center of the animating circle. + * @param centerY The y coordinate of the center of the animating circle. + * @param startRadius The starting radius of the animating circle. + * @param endRadius The ending radius of the animating circle. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static SupportAnimator createCircularReveal(View view, + int centerX, int centerY, + float startRadius, float endRadius) { + + return createCircularReveal(view, centerX, centerY, startRadius, endRadius, View.LAYER_TYPE_SOFTWARE); + } + + /** + * Returns an Animator which can animate a clipping circle. + *

+ * Any shadow cast by the View will respect the circular clip from this animator. + *

+ * Only a single non-rectangular clip can be applied on a View at any time. + * Views clipped by a circular reveal animation take priority over + * {@link android.view.View#setClipToOutline(boolean) View Outline clipping}. + *

+ * Note that the animation returned here is a one-shot animation. It cannot + * be re-used, and once started it cannot be paused or resumed. + * + * @param view The View will be clipped to the animating circle. + * @param centerX The x coordinate of the center of the animating circle. + * @param centerY The y coordinate of the center of the animating circle. + * @param startRadius The starting radius of the animating circle. + * @param endRadius The ending radius of the animating circle. + * + * @param layerType View layer type {@link View#LAYER_TYPE_HARDWARE} or {@link View#LAYER_TYPE_SOFTWARE} + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static SupportAnimator createCircularReveal(View view, + int centerX, int centerY, + float startRadius, float endRadius, int layerType) { + + if(!(view instanceof RevealAnimator)){ + throw new IllegalArgumentException("View must be inside RevealFrameLayout or RevealLinearLayout."); + } + + RevealAnimator revealLayout = (RevealAnimator) view; + revealLayout.attachRevealInfo(new RevealInfo(centerX, centerY, startRadius, endRadius, + new WeakReference<>(view))); + + if(LOLLIPOP_PLUS){ + return new SupportAnimatorImpl(android.view.ViewAnimationUtils + .createCircularReveal(view, centerX, centerY, startRadius, endRadius), revealLayout); + } + + ObjectAnimator reveal = ObjectAnimator.ofFloat(revealLayout, CLIP_RADIUS, startRadius, endRadius); + reveal.addListener(new RevealAnimator.RevealFinishedIceCreamSandwich(revealLayout, layerType)); + return new SupportAnimatorImpl(reveal, revealLayout); + } + + /** + * Lifting view + * + * @param view The animation target + * @param baseRotation initial Rotation X in 3D space + * @param fromY initial Y position of view + * @param duration aniamtion duration + * @param startDelay start delay before animation begin + */ + @Deprecated + public static void liftingFromBottom(View view, float baseRotation, float fromY, int duration, int startDelay){ + view.setRotationX(baseRotation); + view.setTranslationY(fromY); + + view.animate() + .setInterpolator(new AccelerateDecelerateInterpolator()) + .setDuration(duration) + .setStartDelay(startDelay) + .rotationX(0) + .translationY(0) + .start(); + + } + + /** + * Lifting view + * + * @param view The animation target + * @param baseRotation initial Rotation X in 3D space + * @param duration aniamtion duration + * @param startDelay start delay before animation begin + */ + @Deprecated + public static void liftingFromBottom(View view, float baseRotation, int duration, int startDelay){ + view.setRotationX(baseRotation); + view.setTranslationY(view.getHeight() / 3); + + view.animate().setInterpolator(new AccelerateDecelerateInterpolator()) + .setDuration(duration) + .setStartDelay(startDelay) + .rotationX(0) + .translationY(0) + .start(); + + } + + /** + * Lifting view + * + * @param view The animation target + * @param baseRotation initial Rotation X in 3D space + * @param duration aniamtion duration + */ + @Deprecated + public static void liftingFromBottom(View view, float baseRotation, int duration){ + view.setRotationX(baseRotation); + view.setTranslationY(view.getHeight() / 3); + + view.animate().setInterpolator(new AccelerateDecelerateInterpolator()) + .setDuration(duration) + .rotationX(0) + .translationY(0) + .start(); + + } +} diff --git a/app/src/main/java/io/codetail/widget/RevealLinearLayout.java b/app/src/main/java/io/codetail/widget/RevealLinearLayout.java new file mode 100644 index 000000000..ab785460e --- /dev/null +++ b/app/src/main/java/io/codetail/widget/RevealLinearLayout.java @@ -0,0 +1,115 @@ +package io.codetail.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +import io.codetail.animation.RevealAnimator; +import io.codetail.animation.SupportAnimator; +import io.codetail.animation.ViewAnimationUtils; + +public class RevealLinearLayout extends LinearLayout implements RevealAnimator{ + + private final Path mRevealPath; + private final Rect mTargetBounds = new Rect(); + private RevealInfo mRevealInfo; + private boolean mRunning; + private float mRadius; + + public RevealLinearLayout(Context context) { + this(context, null); + } + + public RevealLinearLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RevealLinearLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + mRevealPath = new Path(); + } + + @Override + public void onRevealAnimationStart() { + mRunning = true; + } + + @Override + public void onRevealAnimationEnd() { + mRunning = false; + invalidate(mTargetBounds); + } + + @Override + public void onRevealAnimationCancel() { + onRevealAnimationEnd(); + } + + /** + * Circle radius size + * + * @hide + */ + @Override + public void setRevealRadius(float radius){ + mRadius = radius; + mRevealInfo.getTarget().getHitRect(mTargetBounds); + invalidate(mTargetBounds); + } + + /** + * Circle radius size + * + * @hide + */ + @Override + public float getRevealRadius(){ + return mRadius; + } + + /** + * @hide + */ + @Override + public void attachRevealInfo(RevealInfo info) { + mRevealInfo = info; + } + + /** + * @hide + */ + @Override + public SupportAnimator startReverseAnimation() { + if(mRevealInfo != null && mRevealInfo.hasTarget() && !mRunning) { + return ViewAnimationUtils.createCircularReveal(mRevealInfo.getTarget(), + mRevealInfo.centerX, mRevealInfo.centerY, + mRevealInfo.endRadius, mRevealInfo.startRadius); + } + return null; + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if(mRunning && child == mRevealInfo.getTarget()){ + final int state = canvas.save(); + + mRevealPath.reset(); + mRevealPath.addCircle(mRevealInfo.centerX, mRevealInfo.centerY, mRadius, Path.Direction.CW); + + canvas.clipPath(mRevealPath); + + boolean isInvalided = super.drawChild(canvas, child, drawingTime); + + canvas.restoreToCount(state); + + return isInvalided; + } + + return super.drawChild(canvas, child, drawingTime); + } + +} diff --git a/app/src/main/res/layout/scene_gallery_comments.xml b/app/src/main/res/layout/scene_gallery_comments.xml index 51abb3b1e..6c7f5bcb1 100644 --- a/app/src/main/res/layout/scene_gallery_comments.xml +++ b/app/src/main/res/layout/scene_gallery_comments.xml @@ -38,7 +38,7 @@ android:text="@string/no_one_comments_gallery" style="@style/TextAppearance.AppCompat.Medium"/> - - +