package com.marshalchen.common.uimodule.androidviewhover; import android.content.Context; import android.graphics.Bitmap; import android.os.Build; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.RelativeLayout; import com.marshalchen.common.uimodule.androidanimations.Techniques; import com.marshalchen.common.uimodule.androidanimations.YoYo; import com.marshalchen.common.uimodule.androidviewhover.proxy.AnimationProxy; import com.marshalchen.common.uimodule.androidviewhover.tools.Blur; import com.marshalchen.common.uimodule.androidviewhover.tools.Util; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorSet; import com.nineoldandroids.animation.ObjectAnimator; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class BlurLayout extends RelativeLayout { private View mHoverView; private boolean enableBlurBackground = true; private int mBlurRadius = 10; private ImageView mBlurImage; private static long DURATION = 500; private ArrayList<Animator> mPlayingAnimators = new ArrayList<Animator>(); private ArrayList<Animator> mAppearingAnimators = new ArrayList<Animator>(); private ArrayList<Animator> mDisappearingAnimators = new ArrayList<Animator>(); private ArrayList<AppearListener> mAppearListeners = new ArrayList<AppearListener>(); private ArrayList<DisappearListener> mDisappearListeners = new ArrayList<DisappearListener>(); private boolean enableBackgroundZoom = false; private float mZoomRatio = 1.14f; private boolean enableTouchEvent = true; private Animator mHoverAppearAnimator; private YoYo.AnimationComposer mHoverAppearAnimationComposer; private Animator mHoverDisappearAnimator; private YoYo.AnimationComposer mHoverDisappearAnimationComposer; private HashMap<View, ArrayList<AnimationProxy>> mChildAppearAnimators = new HashMap<View, ArrayList<AnimationProxy>>(); private HashMap<View, ArrayList<AnimationProxy>> mChildDisappearAnimators = new HashMap<View, ArrayList<AnimationProxy>>(); private long mBlurDuration = DURATION; public enum HOVER_STATUS { APPEARING, APPEARED, DISAPPEARING, DISAPPEARED }; private HOVER_STATUS mHoverStatus = HOVER_STATUS.DISAPPEARED; public BlurLayout(Context context) { super(context); } public BlurLayout(Context context, AttributeSet attrs) { super(context, attrs); } public BlurLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onTouchEvent(MotionEvent event) { return enableTouchEvent && gestureDetector.onTouchEvent(event); } private GestureDetector gestureDetector = new GestureDetector(getContext(), new BlurLayoutDetector()); class BlurLayoutDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { if(hover()) return true; else return super.onSingleTapConfirmed(e); } }; public void showHover(){ hover(); } /** * Let hover show. * @return */ private boolean hover(){ if(mHoverView == null) return false; if(getHoverStatus() != HOVER_STATUS.DISAPPEARED || !mPlayingAnimators.isEmpty()) return true; removeView(mBlurImage); if(enableBlurBackground) addBlurImage(); if(mHoverView.getParent() != null){ ((ViewGroup)(mHoverView.getParent())).removeView(mHoverView); } addView(mHoverView, getFullParentSizeLayoutParams()); mHoverView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { startChildrenAppearAnimations(); startBlurImageAppearAnimator(); startHoverAppearAnimator(); if(Build.VERSION.SDK_INT >= 16) mHoverView.getViewTreeObserver().removeOnGlobalLayoutListener(this); else mHoverView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); return true; } /** * Let hover view dismiss. * Notice: only when hover view status is appeared, then, this may work. */ public void dismissHover(){ if(getHoverStatus() != HOVER_STATUS.APPEARED || !mPlayingAnimators.isEmpty()) return; startBlurImageDisappearAnimator(); startHoverDisappearAnimator(); startChildrenDisappearAnimations(); } public void toggleHover(){ if(getHoverStatus() == HOVER_STATUS.DISAPPEARED) showHover(); else if(getHoverStatus() == HOVER_STATUS.APPEARED) dismissHover(); } /** * get currently hover status. * @return */ public HOVER_STATUS getHoverStatus(){ return mHoverStatus; } private void addBlurImage(){ Bitmap b = Util.getViewBitmap(this); if(b == null) return; Bitmap bm = Blur.apply(getContext(), b, mBlurRadius); ImageView im = new ImageView(getContext()); im.setImageBitmap(bm); mBlurImage = im; this.addView(im); } /** * set background blur duration. * @param duration */ public void setBlurDuration(long duration){ if(duration > 100) mBlurDuration = duration; } /** * set background blur radius. * @param radius radius to be used for the gaussian blur operation, integer between 0 and 25 (inclusive) */ public void setBlurRadius(int radius) { if(radius < 0 || radius > 25){ throw new IllegalArgumentException("Radius must be between 0 and 25 (inclusive)"); } this.mBlurRadius = radius; } /** * bind a hover view with BlurLayout. * @param hover */ public void setHoverView(final View hover){ mHoverView = hover; if(mHoverView == null) return; if(mHoverAppearAnimator != null) mHoverAppearAnimator.setTarget(mHoverView); mHoverView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dismissHover(); } }); } /** * Sets whether or not touching the BlurLayout will trigger the Hover View and blur effect * @param enableTouchEvent */ public void enableTouchEvent(boolean enableTouchEvent) { this.enableTouchEvent = enableTouchEvent; } public void enableBlurBackground(boolean enable){ enableBlurBackground = enable; } public void enableZoomBackground(boolean enable) { enableBackgroundZoom = enable; } public void setBlurZoomRatio(float ratio){ if(ratio < 0) throw new IllegalArgumentException("Can not set ratio less than 0"); mZoomRatio = ratio; } private void startBlurImageAppearAnimator(){ if(!enableBlurBackground || mBlurImage == null) return; AnimatorSet set = new AnimatorSet(); if(enableBackgroundZoom){ set.playTogether( ObjectAnimator.ofFloat(mBlurImage, "alpha", 0.8f, 1f), ObjectAnimator.ofFloat(mBlurImage, "scaleX", 1f, mZoomRatio), ObjectAnimator.ofFloat(mBlurImage, "scaleY", 1f, mZoomRatio) ); } else{ set.playTogether( ObjectAnimator.ofFloat(mBlurImage, "alpha", 0f, 1f) ); } set.addListener(mGlobalListener); set.addListener(mGlobalAppearingAnimators); set.setDuration(mBlurDuration); set.start(); } private void startBlurImageDisappearAnimator(){ if(!enableBlurBackground || mBlurImage == null) return; AnimatorSet set = new AnimatorSet(); if(enableBackgroundZoom) set.playTogether( ObjectAnimator.ofFloat(mBlurImage, "alpha", 1f, 0.8f), ObjectAnimator.ofFloat(mBlurImage, "scaleX", mZoomRatio, 1f), ObjectAnimator.ofFloat(mBlurImage, "scaleY", mZoomRatio, 1f) ); else set.playTogether( ObjectAnimator.ofFloat(mBlurImage, "alpha", 1f, 0f) ); set.addListener(mGlobalListener); set.addListener(mGlobalDisappearAnimators); set.setDuration(mBlurDuration); set.start(); } private void startHoverAppearAnimator(){ if(mHoverAppearAnimator != null) mHoverAppearAnimator.start(); if(mHoverAppearAnimationComposer != null) mHoverAppearAnimationComposer.playOn(mHoverView); } private void startHoverDisappearAnimator(){ if(mHoverDisappearAnimator != null) mHoverDisappearAnimator.start(); if(mHoverDisappearAnimationComposer != null) mHoverDisappearAnimationComposer.playOn(mHoverView); } private void startChildrenAppearAnimations(){ for(Map.Entry<View, ArrayList<AnimationProxy>> entry : mChildAppearAnimators.entrySet()){ for(AnimationProxy animator : entry.getValue()){ animator.start(); } } } private void startChildrenDisappearAnimations(){ for(View view : mChildDisappearAnimators.keySet()){ for(AnimationProxy animator : mChildDisappearAnimators.get(view)){ animator.start(); } } } public interface AppearListener { public void onStart(); public void onEnd(); } public interface DisappearListener { public void onStart(); public void onEnd(); } public void addAppearListener(AppearListener l){ mAppearListeners.add(l); } public void removeAppearListener(AppearListener l){ mAppearListeners.remove(l); } public void addDisappearListener(DisappearListener l){ mDisappearListeners.add(l); } public void removeDisappearListener(DisappearListener l){ mDisappearingAnimators.remove(l); } public Animator.AnimatorListener mGlobalAppearingAnimators = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mAppearingAnimators.add(animation); if(mAppearingAnimators.size() == 1){ mHoverStatus = HOVER_STATUS.APPEARING; for(AppearListener l : mAppearListeners){ l.onStart(); } } } @Override public void onAnimationEnd(Animator animation) { mAppearingAnimators.remove(animation); if(mAppearListeners.isEmpty()){ mHoverStatus = HOVER_STATUS.APPEARED; for(AppearListener l : mAppearListeners){ l.onEnd(); } } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }; public Animator.AnimatorListener mGlobalDisappearAnimators = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mDisappearingAnimators.add(animation); if(mDisappearListeners.size() == 1){ for(DisappearListener l : mDisappearListeners){ mHoverStatus = HOVER_STATUS.DISAPPEARING; l.onStart(); } } } @Override public void onAnimationEnd(Animator animation) { mDisappearingAnimators.remove(animation); if(mPlayingAnimators.isEmpty()){ mHoverStatus = HOVER_STATUS.DISAPPEARED; removeView(mBlurImage); removeView(mHoverView); for(DisappearListener l : mDisappearListeners){ l.onEnd(); } } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }; public Animator.AnimatorListener mGlobalListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mPlayingAnimators.add(animation); } @Override public void onAnimationEnd(Animator animation) { mPlayingAnimators.remove(animation); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }; public void setHoverAppearAnimator(Techniques technique){ setHoverAppearAnimator(technique, DURATION); } public void setHoverAppearAnimator(Techniques technique, long duration){ setHoverAppearAnimator(technique, duration, 0, null); } public void setHoverAppearAnimator(Techniques technique, long duration, long delay, Interpolator interpolator){ setHoverAppearAnimator(technique, duration, delay, interpolator, new Animator.AnimatorListener[]{}); } public void setHoverAppearAnimator(Techniques technique, long duration, long delay, Interpolator interpolator, Animator.AnimatorListener... listeners){ mHoverAppearAnimator = null; mHoverAppearAnimationComposer = YoYo.with(technique).delay(delay).duration(duration).interpolate(interpolator); for(Animator.AnimatorListener l : listeners) mHoverAppearAnimationComposer.withListener(l); mHoverAppearAnimationComposer.withListener(mGlobalListener); mHoverAppearAnimationComposer.withListener(mGlobalAppearingAnimators); } public void setHoverAppearAnimator(Animator animator){ mHoverAppearAnimationComposer = null; mHoverAppearAnimator = animator; animator.addListener(mGlobalListener); animator.addListener(mGlobalAppearingAnimators); } public void setHoverDisappearAnimator(Techniques technique){ setHoverDisappearAnimator(technique, DURATION); } public void setHoverDisappearAnimator(Techniques technique, long duration){ setHoverDisappearAnimator(technique, duration, 0, null); } public void setHoverDisappearAnimator(Techniques technique, long duration, long delay, Interpolator interpolator){ setHoverDisappearAnimator(technique, duration, delay, interpolator, new Animator.AnimatorListener[]{}); } public void setHoverDisappearAnimator(Techniques technique, long duration, long delay, Interpolator interpolator, Animator.AnimatorListener... listeners){ mHoverDisappearAnimator = null; mHoverDisappearAnimationComposer = YoYo.with(technique).delay(delay).duration(duration).interpolate(interpolator); for(Animator.AnimatorListener l : listeners) mHoverDisappearAnimationComposer.withListener(l); mHoverDisappearAnimationComposer.withListener(mGlobalListener); mHoverDisappearAnimationComposer.withListener(mGlobalDisappearAnimators); } public void setHoverDisappearAnimator(Animator animator){ mHoverDisappearAnimationComposer = null; mHoverDisappearAnimator = animator; mHoverDisappearAnimator.addListener(mGlobalListener); mHoverDisappearAnimator.addListener(mGlobalDisappearAnimators); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique){ addChildAppearAnimator(hoverView, resId, technique, DURATION); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique, long duration){ addChildAppearAnimator(hoverView, resId, technique, duration, 0); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay){ addChildAppearAnimator(hoverView, resId, technique, duration, delay, true, null); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean hiddenWhenDelaying){ addChildAppearAnimator(hoverView, resId, technique, duration, delay, hiddenWhenDelaying, null); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean hiddenWhenDelaying, Interpolator interpolator){ addChildAppearAnimator(hoverView, resId, technique, duration, delay, hiddenWhenDelaying, interpolator, new Animator.AnimatorListener[]{}); } public void addChildAppearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean hiddenWhenDelaying, Interpolator interpolator, Animator.AnimatorListener... listeners){ AnimationProxy executor = AnimationProxy.build(hoverView, resId, technique, duration, delay, hiddenWhenDelaying, interpolator, listeners); View child = executor.getTarget(); if(mChildAppearAnimators.get(child) == null) mChildAppearAnimators.put(child, new ArrayList<AnimationProxy>()); executor.withListener(mGlobalListener); executor.withListener(mGlobalAppearingAnimators); child.setVisibility(INVISIBLE); mChildAppearAnimators.get(child).add(executor); } public void addChildAppearAnimator(View hoverView, int childId, Animator animator){ AnimationProxy executor = AnimationProxy.build(hoverView, childId, animator); View child = executor.getTarget(); if(mChildAppearAnimators.get(child) == null) mChildAppearAnimators.put(child, new ArrayList<AnimationProxy>()); executor.withListener(mGlobalListener); executor.withListener(mGlobalAppearingAnimators); mChildAppearAnimators.get(child).add(executor); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique){ addChildDisappearAnimator(hoverView, resId, technique, DURATION); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique, long duration){ addChildDisappearAnimator(hoverView, resId, technique, duration, 0, false); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay){ addChildDisappearAnimator(hoverView, resId, technique, duration, delay, false, null); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean invisibleWhenDelaying){ addChildDisappearAnimator(hoverView, resId, technique, duration, delay,invisibleWhenDelaying, null); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean invisibleWhenDelaying, Interpolator interpolator){ addChildDisappearAnimator(hoverView, resId, technique, duration, delay, invisibleWhenDelaying, interpolator, new Animator.AnimatorListener[]{}); } public void addChildDisappearAnimator(View hoverView, int resId, Techniques technique, long duration, long delay, boolean invisibleWhenDelaying, Interpolator interpolator, Animator.AnimatorListener... listeners){ AnimationProxy executor = AnimationProxy.build(hoverView, resId, technique, duration, delay, invisibleWhenDelaying, interpolator, listeners); View child = executor.getTarget(); if(mChildDisappearAnimators.containsKey(child) == false) mChildDisappearAnimators.put(child, new ArrayList<AnimationProxy>()); executor.withListener(mGlobalListener); executor.withListener(mGlobalDisappearAnimators); mChildDisappearAnimators.get(child).add(executor); } public void addChildDisappearAnimator(View hoverView, int childId, Animator animator){ AnimationProxy executor = AnimationProxy.build(hoverView, childId, animator); View child = executor.getTarget(); if(mChildDisappearAnimators.get(child) == null) mChildDisappearAnimators.put(child, new ArrayList<AnimationProxy>()); executor.withListener(mGlobalListener); executor.withListener(mGlobalDisappearAnimators); mChildDisappearAnimators.get(child).add(executor); } public LayoutParams getFullParentSizeLayoutParams(){ LayoutParams pm = (LayoutParams)this.generateDefaultLayoutParams(); pm.width = this.getWidth(); pm.height = this.getHeight(); return pm; } public static void setGlobalDefaultDuration(long duration){ if(duration < 100) throw new IllegalArgumentException("Duration can not be set to less than 100"); DURATION = duration; } }