Add RevealLinearLayout for pre-L
This commit is contained in:
1 parent
df41c62269
commit
ba72dbff7c
7 files changed
+778
-31
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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<View> target;
|
||||
|
||||
public RevealInfo(int centerX, int centerY, float startRadius, float endRadius,
|
||||
WeakReference<View> 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<RevealAnimator> 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<RevealAnimator, Float> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package io.codetail.animation;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class SupportAnimator extends Animator{
|
||||
|
||||
WeakReference<RevealAnimator> 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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
*/
|
||||
@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()}, <code>cancel()</code> causes the animation to
|
||||
* stop in its tracks, sending an
|
||||
* {@link AnimatorListener#onAnimationCancel()} to
|
||||
* its listeners, followed by an
|
||||
* {@link AnimatorListener#onAnimationEnd()} message.
|
||||
*
|
||||
* <p>This method must be called on the thread that is running the animation.</p>
|
||||
*/
|
||||
@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.
|
||||
*
|
||||
* <p>This method must be called on the thread that is running the animation.</p>
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>An animation listener receives notifications from an animation.
|
||||
* Notifications indicate animation related events, such as the end or the
|
||||
* repetition of the animation.</p>
|
||||
*/
|
||||
public interface AnimatorListener {
|
||||
/**
|
||||
* <p>Notifies the start of the animation.</p>
|
||||
*/
|
||||
void onAnimationStart();
|
||||
|
||||
/**
|
||||
* <p>Notifies the end of the animation. This callback is not invoked
|
||||
* for animations with repeat count set to INFINITE.</p>
|
||||
*/
|
||||
void onAnimationEnd();
|
||||
|
||||
/**
|
||||
* <p>Notifies the cancellation of the animation. This callback is not invoked
|
||||
* for animations with repeat count set to INFINITE.</p>
|
||||
*/
|
||||
void onAnimationCancel();
|
||||
|
||||
/**
|
||||
* <p>Notifies the repetition of the animation.</p>
|
||||
*/
|
||||
void onAnimationRepeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Provides default implementation for AnimatorListener.</p>
|
||||
*/
|
||||
public static abstract class SimpleAnimatorListener implements AnimatorListener {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Animator> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Any shadow cast by the View will respect the circular clip from this animator.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Any shadow cast by the View will respect the circular clip from this animator.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* 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();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
android:text="@string/no_one_comments_gallery"
|
||||
style="@style/TextAppearance.AppCompat.Medium"/>
|
||||
|
||||
<LinearLayout
|
||||
<io.codetail.widget.RevealLinearLayout
|
||||
android:id="@+id/edit_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -79,7 +79,7 @@
|
||||
app:srcCompat="@drawable/v_send_dark_x24"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</io.codetail.widget.RevealLinearLayout>
|
||||
|
||||
<com.hippo.widget.FabLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
Reference in new issue
Block a user