package com.cusnews.widgets; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Path; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.FrameLayout; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.animation.ObjectAnimator; /** * Created by yugy on 14/11/21. * <p/> * Support material-design feeling. * <p/> * <a href="https://github.com/kyze8439690/RevealLayout">RevealLayout</a> */ public class RevealLayout extends FrameLayout { private Path mClipPath; private float mClipRadius = 0; private int mClipCenterX, mClipCenterY = 0; private Animator mAnimator; private static final int DEFAULT_DURATION = 600; private boolean mIsContentShown = true; public RevealLayout( Context context ) { this( context, null ); } public RevealLayout( Context context, AttributeSet attrs ) { this( context, attrs, 0 ); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RevealLayout( Context context, AttributeSet attrs, int defStyleAttr ) { super( context, attrs, defStyleAttr ); mClipPath = new Path(); if( Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) { setLayerType( View.LAYER_TYPE_SOFTWARE, null ); } } @Override protected void onSizeChanged( int w, int h, int oldw, int oldh ) { mClipCenterX = w / 2; mClipCenterY = h / 2; if( !mIsContentShown ) { mClipRadius = 0; } else { mClipRadius = (float) ( Math.sqrt( w * w + h * h ) / 2 ); } super.onSizeChanged( w, h, oldw, oldh ); } public float getClipRadius() { return mClipRadius; } public void setClipRadius( float clipRadius ) { mClipRadius = clipRadius; invalidate(); } public boolean isContentShown() { return mIsContentShown; } public void setContentShown( boolean isContentShown ) { mIsContentShown = isContentShown; if( mIsContentShown ) { mClipRadius = 0; } else { mClipRadius = getMaxRadius( mClipCenterX, mClipCenterY ); } invalidate(); } public void show() { show( DEFAULT_DURATION ); } public void hide() { hide( DEFAULT_DURATION ); } public void next() { next( DEFAULT_DURATION ); } public void show( int duration ) { show( getWidth() / 2, getHeight() / 2, duration ); } public void hide( int duration ) { hide( getWidth() / 2, getHeight() / 2, duration ); } public void next( int duration ) { next( getWidth() / 2, getHeight() / 2, duration ); } public void show( int x, int y ) { show( x, y, DEFAULT_DURATION ); } public void hide( int x, int y ) { hide( x, y, DEFAULT_DURATION ); } public void next( int x, int y ) { next( x, y, DEFAULT_DURATION ); } public void show( int x, int y, int duration ) { if( x < 0 || x > getWidth() || y < 0 || y > getHeight() ) { throw new RuntimeException( "Center point out of range or call method when View is not initialed yet." ); } mClipCenterX = x; mClipCenterY = y; float maxRadius = getMaxRadius( x, y ); if( mAnimator != null && mAnimator.isRunning() ) { mAnimator.cancel(); } mAnimator = ObjectAnimator.ofFloat( this, "clipRadius", 0f, maxRadius ); mAnimator.setInterpolator( new BakedBezierInterpolator() ); mAnimator.setDuration( duration ); mAnimator.addListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd( Animator animation ) { super.onAnimationEnd( animation ); mIsContentShown = true; } } ); mAnimator.start(); } public void hide( int x, int y, int duration ) { if( x < 0 || x > getWidth() || y < 0 || y > getHeight() ) { throw new RuntimeException( "Center point out of range or call method when View is not initialed yet." ); } if( x != mClipCenterX || y != mClipCenterY ) { mClipCenterX = x; mClipCenterY = y; mClipRadius = getMaxRadius( x, y ); } if( mAnimator != null && mAnimator.isRunning() ) { mAnimator.cancel(); } mAnimator = ObjectAnimator.ofFloat( this, "clipRadius", 0f ); mAnimator.setInterpolator( new BakedBezierInterpolator() ); mAnimator.setDuration( duration ); mAnimator.addListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd( Animator animation ) { super.onAnimationEnd( animation ); mIsContentShown = false; } } ); mAnimator.start(); } public void next( int x, int y, int duration ) { final int childCount = getChildCount(); if( childCount > 1 ) { for( int i = 0; i < childCount; i++ ) { View child = getChildAt( i ); if( i == 0 ) { bringChildToFront( child ); } } show( x, y, duration ); } } private float getMaxRadius( int x, int y ) { int h = Math.max( x, getWidth() - x ); int v = Math.max( y, getHeight() - y ); return (float) Math.sqrt( h * h + v * v ); } @Override protected boolean drawChild( Canvas canvas, View child, long drawingTime ) { if( indexOfChild( child ) == getChildCount() - 1 ) { boolean result; mClipPath.reset(); mClipPath.addCircle( mClipCenterX, mClipCenterY, mClipRadius, Path.Direction.CW ); Log.d( "RevealLayout", "ClipRadius: " + mClipRadius ); canvas.save(); canvas.clipPath( mClipPath ); result = super.drawChild( canvas, child, drawingTime ); canvas.restore(); return result; } else { return super.drawChild( canvas, child, drawingTime ); } } }