فهرست منبع

0.0.46.beta12 ready
- BottomDialog、BottomMenu、CustomDialog、FullScreenDialog、GuideDialog、InputDialog、MessageDialog、PopMenu、PopNotification 新增 `.setDialogXAnimImpl(DialogXAnimInterface)` 用于完全自定义启动和关闭动画(PopTip、TipDialog、WaitDialog 施工中);
- PopMenu 增加 `.hideWithExitAnim()` 方法用于模拟关闭菜单动画的隐藏功能;

kongzue 2 سال پیش
والد
کامیت
e4b49454ca

+ 99 - 77
DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomDialog.java

@@ -28,6 +28,7 @@ import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -70,6 +71,7 @@ public class BottomDialog extends BaseDialog {
     protected boolean bkgInterceptTouch = true;
     protected float backgroundRadius = -1;
     protected Drawable titleIcon;
+    protected DialogXAnimInterface<BottomDialog> dialogXAnimImpl;
     
     protected TextInfo titleTextInfo;
     protected TextInfo messageTextInfo;
@@ -191,12 +193,11 @@ public class BottomDialog extends BaseDialog {
     
     public BottomDialog show() {
         if (isHide && getDialogView() != null && isShow) {
-            if (hideWithExitAnim && getDialogImpl()!=null) {
+            if (hideWithExitAnim && getDialogImpl() != null) {
                 getDialogView().setVisibility(View.VISIBLE);
-                getDialogImpl().doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogImpl().getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
-                        float value = (float) valueAnimator.getAnimatedValue();
+                    public void run(Float value) {
                         getDialogImpl().boxRoot.setBkgAlpha(value);
                     }
                 });
@@ -443,10 +444,9 @@ public class BottomDialog extends BaseDialog {
             boxBkg.post(new Runnable() {
                 @Override
                 public void run() {
-                    doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                    getDialogXAnimImpl().doShowAnim(BottomDialog.this, new ObjectRunnable<Float>() {
                         @Override
-                        public void run(ValueAnimator valueAnimator) {
-                            float value = (float) valueAnimator.getAnimatedValue();
+                        public void run(Float value) {
                             boxRoot.setBkgAlpha(value);
                             if (value == 1f) {
                                 bottomDialogTouchEventInterceptor = new BottomDialogTouchEventInterceptor(me, dialogImpl);
@@ -593,13 +593,17 @@ public class BottomDialog extends BaseDialog {
             if (!dismissAnimFlag) {
                 dismissAnimFlag = true;
                 
-                doExitAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogXAnimImpl().doExitAnim(BottomDialog.this, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
+                    public void run(Float animatedValue) {
                         if (boxRoot != null) {
-                            float value = (float) valueAnimator.getAnimatedValue();
-                            boxRoot.setBkgAlpha(value);
-                            if (value == 0) boxRoot.setVisibility(View.GONE);
+                            boxRoot.setBkgAlpha(animatedValue);
+                        }
+                        if (animatedValue == 0) {
+                            if (boxRoot != null) {
+                                boxRoot.setVisibility(View.GONE);
+                            }
+                            dismiss(dialogView);
                         }
                     }
                 });
@@ -607,73 +611,12 @@ public class BottomDialog extends BaseDialog {
                 runOnMainDelay(new Runnable() {
                     @Override
                     public void run() {
-                        dismiss(dialogView);
                     }
                 }, exitAnimDurationTemp);
             }
         }
         
         long exitAnimDurationTemp = 300;
-    
-        protected void doExitAnim(ObjectRunnable<ValueAnimator> objectRunnable) {
-            if (overrideExitDuration >= 0) {
-                exitAnimDurationTemp = overrideExitDuration;
-            }
-            if (exitAnimDuration >= 0) {
-                exitAnimDurationTemp = exitAnimDuration;
-            }
-            
-            ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxBkg.getHeight());
-            exitAnim.setDuration(exitAnimDurationTemp);
-            exitAnim.start();
-            
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
-            bkgAlpha.setDuration(exitAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
-        }
-    
-        protected void doShowAnim(ObjectRunnable<ValueAnimator> objectRunnable){
-            long enterAnimDurationTemp = 300;
-    
-            float customDialogTop = 0;
-            if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) {
-                customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight();
-            } else if (bottomDialogMaxHeight > 1) {
-                customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight;
-            }
-    
-            //上移动画
-            ObjectAnimator enterAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(),
-                    bkgEnterAimY = boxRoot.getUnsafePlace().top + customDialogTop
-            );
-            if (overrideEnterDuration >= 0) {
-                enterAnimDurationTemp = overrideEnterDuration;
-            }
-            if (enterAnimDuration >= 0) {
-                enterAnimDurationTemp = enterAnimDuration;
-            }
-            enterAnim.setDuration(enterAnimDurationTemp);
-            enterAnim.setAutoCancel(true);
-            enterAnim.setInterpolator(new DecelerateInterpolator(2f));
-            enterAnim.start();
-    
-            //遮罩层动画
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-            bkgAlpha.setDuration(enterAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
-        }
         
         public void preDismiss() {
             if (isCancelable()) {
@@ -691,6 +634,75 @@ public class BottomDialog extends BaseDialog {
                 exitAnim.start();
             }
         }
+        
+        protected DialogXAnimInterface<BottomDialog> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<BottomDialog>() {
+                    @Override
+                    public void doShowAnim(BottomDialog dialog, ObjectRunnable<Float> animProgress) {
+                        long enterAnimDurationTemp = 300;
+                        
+                        float customDialogTop = 0;
+                        if (bottomDialogMaxHeight > 0 && bottomDialogMaxHeight <= 1) {
+                            customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight * boxBkg.getHeight();
+                        } else if (bottomDialogMaxHeight > 1) {
+                            customDialogTop = boxBkg.getHeight() - bottomDialogMaxHeight;
+                        }
+                        
+                        //上移动画
+                        ObjectAnimator enterAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(),
+                                bkgEnterAimY = boxRoot.getUnsafePlace().top + customDialogTop
+                        );
+                        if (overrideEnterDuration >= 0) {
+                            enterAnimDurationTemp = overrideEnterDuration;
+                        }
+                        if (enterAnimDuration >= 0) {
+                            enterAnimDurationTemp = enterAnimDuration;
+                        }
+                        enterAnim.setDuration(enterAnimDurationTemp);
+                        enterAnim.setAutoCancel(true);
+                        enterAnim.setInterpolator(new DecelerateInterpolator(2f));
+                        enterAnim.start();
+                        
+                        //遮罩层动画
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
+                        bkgAlpha.setDuration(enterAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                    
+                    @Override
+                    public void doExitAnim(BottomDialog dialog, ObjectRunnable<Float> animProgress) {
+                        if (overrideExitDuration >= 0) {
+                            exitAnimDurationTemp = overrideExitDuration;
+                        }
+                        if (exitAnimDuration >= 0) {
+                            exitAnimDurationTemp = exitAnimDuration;
+                        }
+                        
+                        ObjectAnimator exitAnim = ObjectAnimator.ofFloat(boxBkg, "y", boxBkg.getY(), boxBkg.getHeight());
+                        exitAnim.setDuration(exitAnimDurationTemp);
+                        exitAnim.start();
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
+                        bkgAlpha.setDuration(exitAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                };
+            }
+            return dialogXAnimImpl;
+        }
     }
     
     public void refreshUI() {
@@ -1051,11 +1063,12 @@ public class BottomDialog extends BaseDialog {
         hideWithExitAnim = true;
         isHide = true;
         if (getDialogImpl() != null) {
-            getDialogImpl().doExitAnim(new ObjectRunnable<ValueAnimator>() {
+            getDialogImpl().getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
                 @Override
-                public void run(ValueAnimator valueAnimator) {
-                    float value = (float) valueAnimator.getAnimatedValue();
-                    getDialogImpl().boxRoot.setBkgAlpha(value);
+                public void run(Float value) {
+                    if (getDialogImpl().boxRoot != null) {
+                        getDialogImpl().boxRoot.setBkgAlpha(value);
+                    }
                     if (value == 0 && getDialogView() != null) {
                         getDialogView().setVisibility(View.GONE);
                     }
@@ -1138,4 +1151,13 @@ public class BottomDialog extends BaseDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<BottomDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public BottomDialog setDialogXAnimImpl(DialogXAnimInterface<BottomDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 10 - 0
DialogX/src/main/java/com/kongzue/dialogx/dialogs/BottomMenu.java

@@ -17,6 +17,7 @@ import com.kongzue.dialogx.DialogX;
 import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BottomMenuListViewTouchEvent;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
@@ -1170,4 +1171,13 @@ public class BottomMenu extends BottomDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<BottomDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public BottomMenu setDialogXAnimImpl(DialogXAnimInterface<BottomDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 167 - 138
DialogX/src/main/java/com/kongzue/dialogx/dialogs/CustomDialog.java

@@ -20,6 +20,7 @@ import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -57,6 +58,7 @@ public class CustomDialog extends BaseDialog {
     protected BOOLEAN privateCancelable;
     protected boolean bkgInterceptTouch = true;
     protected OnBackgroundMaskClickListener<CustomDialog> onBackgroundMaskClickListener;
+    protected DialogXAnimInterface<CustomDialog> dialogXAnimImpl;
     
     protected View baseView;
     protected int alignViewGravity = -1;                                    //指定菜单相对 baseView 的位置
@@ -116,15 +118,16 @@ public class CustomDialog extends BaseDialog {
     
     public CustomDialog show() {
         if (isHide && getDialogView() != null && isShow) {
-            if (hideWithExitAnim && getDialogImpl()!=null) {
+            if (hideWithExitAnim && getDialogImpl() != null) {
                 getDialogView().setVisibility(View.VISIBLE);
-                getDialogImpl().doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogImpl().getDialogXAnimImpl().doShowAnim(CustomDialog.this, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
-                        float value = (float) valueAnimator.getAnimatedValue();
+                    public void run(Float animProgress) {
+                        float value = animProgress;
                         getDialogImpl().boxRoot.setBkgAlpha(value);
                     }
                 });
+                getDialogImpl().boxCustom.setVisibility(View.VISIBLE);
             } else {
                 getDialogView().setVisibility(View.VISIBLE);
             }
@@ -214,13 +217,14 @@ public class CustomDialog extends BaseDialog {
             boxRoot.post(new Runnable() {
                 @Override
                 public void run() {
-                    doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                    getDialogXAnimImpl().doShowAnim(CustomDialog.this, new ObjectRunnable<Float>() {
                         @Override
-                        public void run(ValueAnimator valueAnimator) {
-                            float value = (float) valueAnimator.getAnimatedValue();
+                        public void run(Float animProgress) {
+                            float value = animProgress;
                             boxRoot.setBkgAlpha(value);
                         }
                     });
+                    getDialogImpl().boxCustom.setVisibility(View.VISIBLE);
                 }
             });
             
@@ -386,9 +390,13 @@ public class CustomDialog extends BaseDialog {
                 boxCustom.setMinimumHeight(height);
             }
             
+            boxRoot.setBackgroundColor(getMaskColor());
+            
             onDialogRefreshUI();
         }
         
+        long exitAnimDurationTemp = -1;
+        
         @Override
         public void doDismiss(View v) {
             if (v != null) v.setEnabled(false);
@@ -397,143 +405,147 @@ public class CustomDialog extends BaseDialog {
                 boxCustom.post(new Runnable() {
                     @Override
                     public void run() {
-                        doExitAnim(new ObjectRunnable<ValueAnimator>() {
+                        getDialogXAnimImpl().doExitAnim(CustomDialog.this, new ObjectRunnable<Float>() {
+                            
                             @Override
-                            public void run(ValueAnimator valueAnimator) {
+                            public void run(Float animProgress) {
+                                float value = animProgress;
                                 if (boxRoot != null) {
-                                    float value = (float) valueAnimator.getAnimatedValue();
                                     boxRoot.setBkgAlpha(value);
-                                    if (value == 0) boxRoot.setVisibility(View.GONE);
+                                }
+                                if (value == 0) {
+                                    if (boxRoot != null) boxRoot.setVisibility(View.GONE);
+                                    dismiss(dialogView);
                                 }
                             }
                         });
-    
-                        runOnMainDelay(new Runnable() {
-                            @Override
-                            public void run() {
-                                dismiss(dialogView);
-                            }
-                        },exitAnimDurationTemp);
                     }
                 });
             }
         }
-    
-        long exitAnimDurationTemp;
-    
-        protected void doExitAnim(ObjectRunnable<ValueAnimator> objectRunnable) {
-            int exitAnimResIdTemp = R.anim.anim_dialogx_default_exit;
-            if (overrideExitAnimRes != 0) {
-                exitAnimResIdTemp = overrideExitAnimRes;
-            }
-            if (exitAnimResId != 0) {
-                exitAnimResIdTemp = exitAnimResId;
-            }
-    
-            Animation exitAnim = AnimationUtils.loadAnimation(getTopActivity() == null ? boxCustom.getContext() : getTopActivity(), exitAnimResIdTemp);
-            exitAnimDurationTemp = exitAnim.getDuration();
-            if (overrideExitDuration >= 0) {
-                exitAnimDurationTemp = overrideExitDuration;
-            }
-            if (exitAnimDuration >= 0) {
-                exitAnimDurationTemp = exitAnimDuration;
-            }
-            exitAnim.setDuration(exitAnimDurationTemp);
-            boxCustom.startAnimation(exitAnim);
-    
-            if (overrideMaskExitAnimRes != 0) {
-                Animation maskExitAnim = AnimationUtils.loadAnimation(getTopActivity(), overrideMaskExitAnimRes);
-                maskExitAnim.setDuration(exitAnimDurationTemp);
-                maskExitAnim.setInterpolator(new DecelerateInterpolator(2f));
-                boxRoot.startAnimation(maskExitAnim);
-            }
-    
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
-            bkgAlpha.setDuration(exitAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
-        }
-    
-        protected void doShowAnim(ObjectRunnable<ValueAnimator> objectRunnable){
-            Animation enterAnim;
-            if (enterAnimResId == R.anim.anim_dialogx_default_enter &&
-                    exitAnimResId == R.anim.anim_dialogx_default_exit &&
-                    baseView == null) {
-                switch (align) {
-                    case TOP:
-                    case TOP_CENTER:
-                    case TOP_LEFT:
-                    case TOP_RIGHT:
-                        enterAnimResId = R.anim.anim_dialogx_top_enter;
-                        exitAnimResId = R.anim.anim_dialogx_top_exit;
-                        break;
-                    case BOTTOM:
-                    case BOTTOM_CENTER:
-                    case BOTTOM_LEFT:
-                    case BOTTOM_RIGHT:
-                        enterAnimResId = R.anim.anim_dialogx_bottom_enter;
-                        exitAnimResId = R.anim.anim_dialogx_bottom_exit;
-                        break;
-                    case LEFT:
-                    case LEFT_CENTER:
-                    case LEFT_TOP:
-                    case LEFT_BOTTOM:
-                        enterAnimResId = R.anim.anim_dialogx_left_enter;
-                        exitAnimResId = R.anim.anim_dialogx_left_exit;
-                        break;
-                    case RIGHT:
-                    case RIGHT_CENTER:
-                    case RIGHT_TOP:
-                    case RIGHT_BOTTOM:
-                        enterAnimResId = R.anim.anim_dialogx_right_enter;
-                        exitAnimResId = R.anim.anim_dialogx_right_exit;
-                        break;
-                }
-                enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId);
-                enterAnim.setInterpolator(new DecelerateInterpolator(2f));
-            } else {
-                int enterAnimResIdTemp = R.anim.anim_dialogx_default_enter;
-                if (overrideEnterAnimRes != 0) {
-                    enterAnimResIdTemp = overrideEnterAnimRes;
-                }
-                if (enterAnimResId != 0) {
-                    enterAnimResIdTemp = enterAnimResId;
-                }
-                enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResIdTemp);
-            }
-            long enterAnimDurationTemp = enterAnim.getDuration();
-            if (overrideEnterDuration >= 0) {
-                enterAnimDurationTemp = overrideEnterDuration;
-            }
-            if (enterAnimDuration >= 0) {
-                enterAnimDurationTemp = enterAnimDuration;
-            }
-            enterAnim.setDuration(enterAnimDurationTemp);
-            boxCustom.setVisibility(View.VISIBLE);
-            boxCustom.startAnimation(enterAnim);
-    
-            boxRoot.setBackgroundColor(maskColor);
-            if (overrideMaskEnterAnimRes != 0) {
-                Animation maskEnterAnim = AnimationUtils.loadAnimation(getTopActivity(), overrideMaskEnterAnimRes);
-                maskEnterAnim.setInterpolator(new DecelerateInterpolator(2f));
-                maskEnterAnim.setDuration(enterAnimDurationTemp);
-                boxRoot.startAnimation(maskEnterAnim);
+        
+        protected DialogXAnimInterface<CustomDialog> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<CustomDialog>() {
+                    @Override
+                    public void doShowAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
+                        Animation enterAnim;
+                        if (enterAnimResId == R.anim.anim_dialogx_default_enter &&
+                                exitAnimResId == R.anim.anim_dialogx_default_exit &&
+                                baseView == null) {
+                            switch (align) {
+                                case TOP:
+                                case TOP_CENTER:
+                                case TOP_LEFT:
+                                case TOP_RIGHT:
+                                    enterAnimResId = R.anim.anim_dialogx_top_enter;
+                                    exitAnimResId = R.anim.anim_dialogx_top_exit;
+                                    break;
+                                case BOTTOM:
+                                case BOTTOM_CENTER:
+                                case BOTTOM_LEFT:
+                                case BOTTOM_RIGHT:
+                                    enterAnimResId = R.anim.anim_dialogx_bottom_enter;
+                                    exitAnimResId = R.anim.anim_dialogx_bottom_exit;
+                                    break;
+                                case LEFT:
+                                case LEFT_CENTER:
+                                case LEFT_TOP:
+                                case LEFT_BOTTOM:
+                                    enterAnimResId = R.anim.anim_dialogx_left_enter;
+                                    exitAnimResId = R.anim.anim_dialogx_left_exit;
+                                    break;
+                                case RIGHT:
+                                case RIGHT_CENTER:
+                                case RIGHT_TOP:
+                                case RIGHT_BOTTOM:
+                                    enterAnimResId = R.anim.anim_dialogx_right_enter;
+                                    exitAnimResId = R.anim.anim_dialogx_right_exit;
+                                    break;
+                            }
+                            enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId);
+                            enterAnim.setInterpolator(new DecelerateInterpolator(2f));
+                        } else {
+                            int enterAnimResIdTemp = R.anim.anim_dialogx_default_enter;
+                            if (overrideEnterAnimRes != 0) {
+                                enterAnimResIdTemp = overrideEnterAnimRes;
+                            }
+                            if (enterAnimResId != 0) {
+                                enterAnimResIdTemp = enterAnimResId;
+                            }
+                            enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResIdTemp);
+                        }
+                        long enterAnimDurationTemp = enterAnim.getDuration();
+                        if (overrideEnterDuration >= 0) {
+                            enterAnimDurationTemp = overrideEnterDuration;
+                        }
+                        if (enterAnimDuration >= 0) {
+                            enterAnimDurationTemp = enterAnimDuration;
+                        }
+                        enterAnim.setDuration(enterAnimDurationTemp);
+                        boxCustom.setVisibility(View.VISIBLE);
+                        boxCustom.startAnimation(enterAnim);
+                        
+                        boxRoot.setBackgroundColor(maskColor);
+                        if (overrideMaskEnterAnimRes != 0) {
+                            Animation maskEnterAnim = AnimationUtils.loadAnimation(getTopActivity(), overrideMaskEnterAnimRes);
+                            maskEnterAnim.setInterpolator(new DecelerateInterpolator(2f));
+                            maskEnterAnim.setDuration(enterAnimDurationTemp);
+                            boxRoot.startAnimation(maskEnterAnim);
+                        }
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
+                        bkgAlpha.setDuration(enterAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                    
+                    @Override
+                    public void doExitAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
+                        int exitAnimResIdTemp = R.anim.anim_dialogx_default_exit;
+                        if (overrideExitAnimRes != 0) {
+                            exitAnimResIdTemp = overrideExitAnimRes;
+                        }
+                        if (exitAnimResId != 0) {
+                            exitAnimResIdTemp = exitAnimResId;
+                        }
+                        
+                        Animation exitAnim = AnimationUtils.loadAnimation(getTopActivity() == null ? boxCustom.getContext() : getTopActivity(), exitAnimResIdTemp);
+                        exitAnimDurationTemp = exitAnim.getDuration();
+                        if (overrideExitDuration >= 0) {
+                            exitAnimDurationTemp = overrideExitDuration;
+                        }
+                        if (exitAnimDuration >= 0) {
+                            exitAnimDurationTemp = exitAnimDuration;
+                        }
+                        exitAnim.setDuration(exitAnimDurationTemp);
+                        boxCustom.startAnimation(exitAnim);
+                        
+                        if (overrideMaskExitAnimRes != 0) {
+                            Animation maskExitAnim = AnimationUtils.loadAnimation(getTopActivity(), overrideMaskExitAnimRes);
+                            maskExitAnim.setDuration(exitAnimDurationTemp);
+                            maskExitAnim.setInterpolator(new DecelerateInterpolator(2f));
+                            boxRoot.startAnimation(maskExitAnim);
+                        }
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
+                        bkgAlpha.setDuration(exitAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                };
             }
-    
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-            bkgAlpha.setDuration(enterAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
+            return dialogXAnimImpl;
         }
     }
     
@@ -689,6 +701,10 @@ public class CustomDialog extends BaseDialog {
         return this;
     }
     
+    public int getMaskColor() {
+        return maskColor;
+    }
+    
     public long getEnterAnimDuration() {
         return enterAnimDuration;
     }
@@ -740,13 +756,17 @@ public class CustomDialog extends BaseDialog {
         hideWithExitAnim = true;
         isHide = true;
         if (getDialogImpl() != null) {
-            getDialogImpl().doExitAnim(new ObjectRunnable<ValueAnimator>() {
+            getDialogImpl().getDialogXAnimImpl().doExitAnim(CustomDialog.this, new ObjectRunnable<Float>() {
                 @Override
-                public void run(ValueAnimator valueAnimator) {
-                    float value = (float) valueAnimator.getAnimatedValue();
-                    getDialogImpl().boxRoot.setBkgAlpha(value);
+                public void run(Float animProgress) {
+                    float value = animProgress;
+                    if (getDialogImpl().boxRoot != null) {
+                        getDialogImpl().boxRoot.setBkgAlpha(value);
+                    }
                     if (value == 0 && getDialogView() != null) {
-                        getDialogView().setVisibility(View.GONE);
+                        if (getDialogView() != null) {
+                            getDialogView().setVisibility(View.GONE);
+                        }
                     }
                 }
             });
@@ -922,4 +942,13 @@ public class CustomDialog extends BaseDialog {
         this.onBackgroundMaskClickListener = onBackgroundMaskClickListener;
         return this;
     }
+    
+    public DialogXAnimInterface<CustomDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public CustomDialog setDialogXAnimImpl(DialogXAnimInterface<CustomDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 65 - 51
DialogX/src/main/java/com/kongzue/dialogx/dialogs/FullScreenDialog.java

@@ -21,6 +21,7 @@ import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -51,6 +52,7 @@ public class FullScreenDialog extends BaseDialog {
     protected boolean hideZoomBackground;
     protected float backgroundRadius = -1;
     protected boolean allowInterceptTouch = true;
+    protected DialogXAnimInterface<FullScreenDialog> dialogXAnimImpl;
     
     protected DialogLifecycleCallback<FullScreenDialog> dialogLifecycleCallback;
     protected OnBackgroundMaskClickListener<FullScreenDialog> onBackgroundMaskClickListener;
@@ -83,9 +85,9 @@ public class FullScreenDialog extends BaseDialog {
     
     public FullScreenDialog show() {
         if (isHide && getDialogView() != null && isShow) {
-            if (hideWithExitAnim && getDialogImpl()!=null) {
+            if (hideWithExitAnim && getDialogImpl() != null) {
                 getDialogView().setVisibility(View.VISIBLE);
-                getDialogImpl().doShowAnim();
+                getDialogImpl().getDialogXAnimImpl().doShowAnim(me, null);
             } else {
                 getDialogView().setVisibility(View.VISIBLE);
             }
@@ -208,7 +210,7 @@ public class FullScreenDialog extends BaseDialog {
             boxRoot.post(new Runnable() {
                 @Override
                 public void run() {
-                    doShowAnim();
+                    getDialogXAnimImpl().doShowAnim(me, null);
                 }
             });
             
@@ -333,23 +335,20 @@ public class FullScreenDialog extends BaseDialog {
             
             if (!dismissAnimFlag) {
                 dismissAnimFlag = true;
-                doExitAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
+                    public void run(Float value) {
                         if (boxRoot != null) {
-                            float value = (float) valueAnimator.getAnimatedValue();
                             boxRoot.setBkgAlpha(value);
-                            if (value == 0) boxRoot.setVisibility(View.GONE);
+                        }
+                        if (value == 0) {
+                            if (boxRoot != null) {
+                                boxRoot.setVisibility(View.GONE);
+                            }
+                            dismiss(dialogView);
                         }
                     }
                 });
-    
-                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        dismiss(dialogView);
-                    }
-                }, exitAnimDurationTemp);
             }
         }
         
@@ -371,41 +370,48 @@ public class FullScreenDialog extends BaseDialog {
             }
         }
         
-        protected void doShowAnim() {
-            int customViewHeight = boxCustom.getHeight();
-            if (customViewHeight == 0) {
-                //实测在 Android 10 中,离屏情况下 View可能无法得到正确高度(恒 0),此时直接按照全屏高度处理
-                //其他版本 Android 未发现此问题
-                showEnterAnim((int) boxRoot.getSafeHeight());
-            } else {
-                showEnterAnim(customViewHeight);
-            }
-        }
-        
-        long exitAnimDurationTemp;
-        
-        protected void doExitAnim(ObjectRunnable<ValueAnimator> objectRunnable) {
-            exitAnimDurationTemp = 300;
-            if (overrideExitDuration >= 0) {
-                exitAnimDurationTemp = overrideExitDuration;
-            }
-            if (exitAnimDuration >= 0) {
-                exitAnimDurationTemp = exitAnimDuration;
+        protected DialogXAnimInterface<FullScreenDialog> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<FullScreenDialog>() {
+                    @Override
+                    public void doShowAnim(FullScreenDialog dialog, ObjectRunnable<Float> animProgress) {
+                        int customViewHeight = boxCustom.getHeight();
+                        if (customViewHeight == 0) {
+                            //实测在 Android 10 中,离屏情况下 View可能无法得到正确高度(恒 0),此时直接按照全屏高度处理
+                            //其他版本 Android 未发现此问题
+                            showEnterAnim((int) boxRoot.getSafeHeight());
+                        } else {
+                            showEnterAnim(customViewHeight);
+                        }
+                    }
+                    
+                    @Override
+                    public void doExitAnim(FullScreenDialog dialog, ObjectRunnable<Float> animProgress) {
+                        long exitAnimDurationTemp = 300;
+                        if (overrideExitDuration >= 0) {
+                            exitAnimDurationTemp = overrideExitDuration;
+                        }
+                        if (exitAnimDuration >= 0) {
+                            exitAnimDurationTemp = exitAnimDuration;
+                        }
+                        
+                        ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), boxBkg.getHeight());
+                        exitAnim.setDuration(exitAnimDurationTemp);
+                        exitAnim.start();
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
+                        bkgAlpha.setDuration(exitAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                };
             }
-            
-            ObjectAnimator exitAnim = ObjectAnimator.ofFloat(bkg, "y", bkg.getY(), boxBkg.getHeight());
-            exitAnim.setDuration(exitAnimDurationTemp);
-            exitAnim.start();
-            
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
-            bkgAlpha.setDuration(exitAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
+            return dialogXAnimImpl;
         }
     }
     
@@ -578,10 +584,9 @@ public class FullScreenDialog extends BaseDialog {
         hideWithExitAnim = true;
         isHide = true;
         if (getDialogImpl() != null) {
-            getDialogImpl().doExitAnim(new ObjectRunnable<ValueAnimator>() {
+            getDialogImpl().getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
                 @Override
-                public void run(ValueAnimator valueAnimator) {
-                    float value = (float) valueAnimator.getAnimatedValue();
+                public void run(Float value) {
                     getDialogImpl().boxRoot.setBkgAlpha(value);
                     if (value == 0 && getDialogView() != null) {
                         getDialogView().setVisibility(View.GONE);
@@ -635,4 +640,13 @@ public class FullScreenDialog extends BaseDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<FullScreenDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public FullScreenDialog setDialogXAnimImpl(DialogXAnimInterface<FullScreenDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 10 - 0
DialogX/src/main/java/com/kongzue/dialogx/dialogs/GuideDialog.java

@@ -22,6 +22,7 @@ import androidx.annotation.ColorInt;
 import com.kongzue.dialogx.DialogX;
 import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -688,4 +689,13 @@ public class GuideDialog extends CustomDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<CustomDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public GuideDialog setDialogXAnimImpl(DialogXAnimInterface<CustomDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 10 - 0
DialogX/src/main/java/com/kongzue/dialogx/dialogs/InputDialog.java

@@ -10,6 +10,7 @@ import androidx.annotation.ColorRes;
 
 import com.kongzue.dialogx.DialogX;
 import com.kongzue.dialogx.R;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -630,4 +631,13 @@ public class InputDialog extends MessageDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<MessageDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public InputDialog setDialogXAnimImpl(DialogXAnimInterface<MessageDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 93 - 81
DialogX/src/main/java/com/kongzue/dialogx/dialogs/MessageDialog.java

@@ -34,6 +34,7 @@ import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.BaseOnDialogClickCallback;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
@@ -68,6 +69,7 @@ public class MessageDialog extends BaseDialog {
     protected BOOLEAN privateCancelable;
     protected int customEnterAnimResId;
     protected int customExitAnimResId;
+    protected DialogXAnimInterface<MessageDialog> dialogXAnimImpl;
     
     protected DialogLifecycleCallback<MessageDialog> dialogLifecycleCallback;
     protected OnBackgroundMaskClickListener<MessageDialog> onBackgroundMaskClickListener;
@@ -220,10 +222,9 @@ public class MessageDialog extends BaseDialog {
         if (isHide && getDialogView() != null && isShow) {
             if (hideWithExitAnim && getDialogImpl() != null) {
                 getDialogView().setVisibility(View.VISIBLE);
-                getDialogImpl().doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogImpl().getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
-                        float value = (float) valueAnimator.getAnimatedValue();
+                    public void run(Float value) {
                         getDialogImpl().boxRoot.setBkgAlpha(value);
                     }
                 });
@@ -329,10 +330,9 @@ public class MessageDialog extends BaseDialog {
                     isShow = true;
                     preShow = false;
                     
-                    doShowAnim(new ObjectRunnable<ValueAnimator>() {
+                    getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
                         @Override
-                        public void run(ValueAnimator valueAnimator) {
-                            float value = (float) valueAnimator.getAnimatedValue();
+                        public void run(Float value) {
                             boxRoot.setBkgAlpha(value);
                         }
                     });
@@ -718,88 +718,92 @@ public class MessageDialog extends BaseDialog {
             if (!dismissAnimFlag) {
                 dismissAnimFlag = true;
                 
-                doExitAnim(new ObjectRunnable<ValueAnimator>() {
+                getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
                     @Override
-                    public void run(ValueAnimator valueAnimator) {
+                    public void run(Float value) {
                         if (boxRoot != null) {
-                            float value = (float) valueAnimator.getAnimatedValue();
                             boxRoot.setBkgAlpha(value);
-                            if (value == 0) boxRoot.setVisibility(View.GONE);
+                        }
+                        if (value == 0) {
+                            if (boxRoot != null) {
+                                boxRoot.setVisibility(View.GONE);
+                            }
+                            dismiss(dialogView);
                         }
                     }
                 });
-                
-                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        dismiss(dialogView);
-                    }
-                }, exitAnimDurationTemp);
-            }
-        }
-        
-        protected void doShowAnim(ObjectRunnable<ValueAnimator> objectRunnable) {
-            int enterAnimResId = style.enterAnimResId() == 0 ? R.anim.anim_dialogx_default_enter : style.enterAnimResId();
-            if (overrideEnterAnimRes != 0) {
-                enterAnimResId = overrideEnterAnimRes;
-            }
-            if (customEnterAnimResId != 0) {
-                enterAnimResId = customEnterAnimResId;
             }
-            Animation enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId);
-            long enterAnimDurationTemp = enterAnim.getDuration();
-            if (overrideEnterDuration >= 0) {
-                enterAnimDurationTemp = overrideEnterDuration;
-            }
-            if (enterAnimDuration >= 0) {
-                enterAnimDurationTemp = enterAnimDuration;
-            }
-            enterAnim.setDuration(enterAnimDurationTemp);
-            enterAnim.setInterpolator(new DecelerateInterpolator());
-            bkg.startAnimation(enterAnim);
-            
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-            bkgAlpha.setDuration(enterAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
         }
         
-        long exitAnimDurationTemp;
-        
-        protected void doExitAnim(ObjectRunnable<ValueAnimator> objectRunnable) {
-            int exitAnimResId = style.exitAnimResId() == 0 ? R.anim.anim_dialogx_default_exit : style.exitAnimResId();
-            if (overrideExitAnimRes != 0) {
-                exitAnimResId = overrideExitAnimRes;
-            }
-            if (customExitAnimResId != 0) {
-                exitAnimResId = customExitAnimResId;
-            }
-            Animation exitAnim = AnimationUtils.loadAnimation(getTopActivity(), exitAnimResId);
-            exitAnimDurationTemp = exitAnim.getDuration();
-            exitAnim.setInterpolator(new AccelerateInterpolator());
-            if (overrideExitDuration >= 0) {
-                exitAnimDurationTemp = overrideExitDuration;
-            }
-            if (exitAnimDuration >= 0) {
-                exitAnimDurationTemp = exitAnimDuration;
+        protected DialogXAnimInterface<MessageDialog> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<MessageDialog>() {
+                    @Override
+                    public void doShowAnim(MessageDialog dialog, ObjectRunnable<Float> animProgress) {
+                        int enterAnimResId = style.enterAnimResId() == 0 ? R.anim.anim_dialogx_default_enter : style.enterAnimResId();
+                        if (overrideEnterAnimRes != 0) {
+                            enterAnimResId = overrideEnterAnimRes;
+                        }
+                        if (customEnterAnimResId != 0) {
+                            enterAnimResId = customEnterAnimResId;
+                        }
+                        Animation enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId);
+                        long enterAnimDurationTemp = enterAnim.getDuration();
+                        if (overrideEnterDuration >= 0) {
+                            enterAnimDurationTemp = overrideEnterDuration;
+                        }
+                        if (enterAnimDuration >= 0) {
+                            enterAnimDurationTemp = enterAnimDuration;
+                        }
+                        enterAnim.setDuration(enterAnimDurationTemp);
+                        enterAnim.setInterpolator(new DecelerateInterpolator());
+                        bkg.startAnimation(enterAnim);
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
+                        bkgAlpha.setDuration(enterAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                    
+                    @Override
+                    public void doExitAnim(MessageDialog dialog, ObjectRunnable<Float> animProgress) {
+                        int exitAnimResId = style.exitAnimResId() == 0 ? R.anim.anim_dialogx_default_exit : style.exitAnimResId();
+                        if (overrideExitAnimRes != 0) {
+                            exitAnimResId = overrideExitAnimRes;
+                        }
+                        if (customExitAnimResId != 0) {
+                            exitAnimResId = customExitAnimResId;
+                        }
+                        Animation exitAnim = AnimationUtils.loadAnimation(getTopActivity(), exitAnimResId);
+                        long exitAnimDurationTemp = exitAnim.getDuration();
+                        exitAnim.setInterpolator(new AccelerateInterpolator());
+                        if (overrideExitDuration >= 0) {
+                            exitAnimDurationTemp = overrideExitDuration;
+                        }
+                        if (exitAnimDuration >= 0) {
+                            exitAnimDurationTemp = exitAnimDuration;
+                        }
+                        exitAnim.setDuration(exitAnimDurationTemp);
+                        bkg.startAnimation(exitAnim);
+                        
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
+                        bkgAlpha.setDuration(exitAnimDurationTemp);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                            @Override
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
+                            }
+                        });
+                        bkgAlpha.start();
+                    }
+                };
             }
-            exitAnim.setDuration(exitAnimDurationTemp);
-            bkg.startAnimation(exitAnim);
-            
-            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
-            bkgAlpha.setDuration(exitAnimDurationTemp);
-            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    objectRunnable.run(animation);
-                }
-            });
-            bkgAlpha.start();
+            return dialogXAnimImpl;
         }
     }
     
@@ -1192,10 +1196,9 @@ public class MessageDialog extends BaseDialog {
         hideWithExitAnim = true;
         isHide = true;
         if (getDialogImpl() != null) {
-            getDialogImpl().doExitAnim(new ObjectRunnable<ValueAnimator>() {
+            getDialogImpl().getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
                 @Override
-                public void run(ValueAnimator valueAnimator) {
-                    float value = (float) valueAnimator.getAnimatedValue();
+                public void run(Float value) {
                     getDialogImpl().boxRoot.setBkgAlpha(value);
                     if (value == 0 && getDialogView() != null) {
                         getDialogView().setVisibility(View.GONE);
@@ -1286,4 +1289,13 @@ public class MessageDialog extends BaseDialog {
         refreshUI();
         return this;
     }
+    
+    public DialogXAnimInterface<MessageDialog> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public MessageDialog setDialogXAnimImpl(DialogXAnimInterface<MessageDialog> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 278 - 239
DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopMenu.java

@@ -28,12 +28,14 @@ import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBackgroundMaskClickListener;
 import com.kongzue.dialogx.interfaces.OnBindView;
 import com.kongzue.dialogx.interfaces.OnIconChangeCallBack;
 import com.kongzue.dialogx.interfaces.OnMenuItemClickListener;
+import com.kongzue.dialogx.util.ObjectRunnable;
 import com.kongzue.dialogx.util.PopMenuArrayAdapter;
 import com.kongzue.dialogx.util.TextInfo;
 import com.kongzue.dialogx.util.views.BlurView;
@@ -74,6 +76,7 @@ public class PopMenu extends BaseDialog {
     protected TextInfo menuTextInfo;
     protected boolean offScreen = false;                                    //超出屏幕
     protected float backgroundRadius = -1;
+    protected DialogXAnimInterface<PopMenu> dialogXAnimImpl;
     
     protected int alignGravity = -1;                                        //指定菜单相对 baseView 的位置
     
@@ -198,10 +201,21 @@ public class PopMenu extends BaseDialog {
     }
     
     public PopMenu show() {
-        if (isHide && getDialogView() != null) {
-            getDialogView().setVisibility(View.VISIBLE);
+        if (isHide && getDialogView() != null && isShow) {
+            if (hideWithExitAnim && getDialogImpl() != null) {
+                getDialogView().setVisibility(View.VISIBLE);
+                getDialogImpl().getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
+                    @Override
+                    public void run(Float value) {
+                        getDialogImpl().boxRoot.setBkgAlpha(value);
+                    }
+                });
+            } else {
+                getDialogView().setVisibility(View.VISIBLE);
+            }
             return this;
         }
+        
         super.beforeShow();
         if (getDialogView() == null) {
             int layoutId = isLightTheme() ? R.layout.layout_dialogx_popmenu_material : R.layout.layout_dialogx_popmenu_material_dark;
@@ -317,228 +331,14 @@ public class PopMenu extends BaseDialog {
             boxBody.setVisibility(View.INVISIBLE);
             boxBody.post(new Runnable() {
                 
-                int selectMenuIndex = -1;
-                
                 @Override
                 public void run() {
-                    long enterAnimDurationTemp = enterAnimDuration != -1 ? enterAnimDuration : (overrideEnterDuration == -1 ? 150 : overrideEnterDuration);
-                    
-                    if (baseView != null) {
-                        //有绑定按钮的情况下
-                        int targetHeight = getBodyRealHeight();
-                        boxBody.getLayoutParams().height = 1;
-                        
-                        if (overlayBaseView && !listMenu.isCanScroll()) {
-                            if (baseView instanceof TextView) {
-                                String baseText = ((TextView) baseView).getText().toString();
-                                for (CharSequence c : menuList) {
-                                    if (TextUtils.equals(c.toString(), baseText)) {
-                                        selectMenuIndex = menuList.indexOf(c);
-                                        break;
-                                    }
-                                }
-                            }
-                            //找到已选中的项目
-                            if (selectMenuIndex != -1) {
-                                int[] viewLoc = new int[2];
-                                if (listMenu.getChildAt(selectMenuIndex) != null) {
-                                    int itemHeight = listMenu.getChildAt(selectMenuIndex).getMeasuredHeight();
-                                    listMenu.getChildAt(selectMenuIndex).getLocationOnScreen(viewLoc);
-                                    boxBody.setY(baseViewLoc[1] + (baseView.getMeasuredHeight() / 2f) - (viewLoc[1] - boxBody.getY()) - (itemHeight / 2f));
-                                }
-                            }
-                        }
-                        
-                        //菜单位置计算逻辑
-                        int baseViewLeft = baseViewLoc[0];
-                        int baseViewTop = baseViewLoc[1];
-                        int calX = 0, calY = 0;
-                        if (alignGravity != -1) {
-                            if (isAlignGravity(Gravity.CENTER_VERTICAL)) {
-                                calY = (Math.max(0, baseViewTop + baseView.getMeasuredHeight() / 2 - boxBody.getHeight() / 2));
-                            }
-                            if (isAlignGravity(Gravity.CENTER_HORIZONTAL)) {
-                                calX = (Math.max(0, baseViewLeft + (
-                                        getWidth() > 0 ? baseView.getMeasuredWidth() / 2 - getWidth() / 2 : 0
-                                )));
-                            }
-                            if (isAlignGravity(Gravity.CENTER)) {
-                                calX = (Math.max(0, baseViewLeft + (
-                                        getWidth() > 0 ? (baseView.getMeasuredWidth() / 2 - getWidth() / 2) : 0
-                                )));
-                                calY = (Math.max(0, baseViewTop + baseView.getMeasuredHeight() / 2 - boxBody.getHeight() / 2));
-                            }
-                            if (overlayBaseView) {
-                                //菜单覆盖在 baseView 上时
-                                if (isAlignGravity(Gravity.TOP)) {
-                                    calY = (baseViewTop - boxBody.getHeight() + baseView.getHeight());
-                                }
-                                if (isAlignGravity(Gravity.LEFT)) {
-                                    calX = (baseViewLeft);
-                                }
-                                if (isAlignGravity(Gravity.RIGHT)) {
-                                    calX = (baseViewLeft + (getWidth() > 0 ? baseView.getMeasuredWidth() - width : 0));
-                                }
-                                if (isAlignGravity(Gravity.BOTTOM)) {
-                                    calY = (baseViewTop);
-                                }
-                            } else {
-                                if (isAlignGravity(Gravity.TOP)) {
-                                    calY = (Math.max(0, baseViewTop - boxBody.getHeight()));
-                                }
-                                if (isAlignGravity(Gravity.LEFT)) {
-                                    calX = (Math.max(0, baseViewLeft - boxBody.getWidth()));
-                                }
-                                if (isAlignGravity(Gravity.RIGHT)) {
-                                    calX = (Math.max(0, baseViewLeft + baseView.getWidth()));
-                                }
-                                if (isAlignGravity(Gravity.BOTTOM)) {
-                                    calY = (Math.max(0, baseViewTop + baseView.getHeight()));
-                                }
-                            }
-                            if (!offScreen) {
-                                if (calX < 0) {
-                                    calX = 0;
-                                }
-                                if ((calX + boxBody.getWidth()) > boxRoot.getWidth()) {
-                                    calX = boxRoot.getWidth() - boxBody.getWidth();
-                                }
-                                if (calY < 0) {
-                                    calY = 0;
-                                }
-                                if ((calY + boxBody.getHeight()) > boxRoot.getHeight()) {
-                                    calY = boxRoot.getHeight() - boxBody.getHeight();
-                                }
-                            }
-                            
-                            if (calX != 0) boxBody.setX(calX);
-                            if (calY != 0) boxBody.setY(calY);
-                        }
-                        
-                        //展开动画
-                        Animation enterAnim = new Animation() {
-                            @Override
-                            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                                int aimHeight = interpolatedTime == 1 ? ViewGroup.LayoutParams.WRAP_CONTENT : (int) (targetHeight * interpolatedTime);
-                                boxBody.getLayoutParams().height = aimHeight;
-                                boxBody.getLayoutParams().width = getWidth() == -1 ? baseView.getWidth() : getWidth();
-                                if ((boxBody.getY() + aimHeight) > boxRoot.getSafeHeight()) {
-                                    boxBody.setY(boxRoot.getSafeHeight() - aimHeight);
-                                }
-                                
-                                if (!offScreen) {
-                                    float calX = boxBody.getX();
-                                    float calY = boxBody.getY();
-                                    if (calX < 0) {
-                                        calX = 0;
-                                    }
-                                    if ((calX + boxBody.getWidth()) > boxRoot.getWidth()) {
-                                        calX = boxRoot.getWidth() - boxBody.getWidth();
-                                    }
-                                    if (calY < 0) {
-                                        calY = 0;
-                                    }
-                                    if ((calY + boxBody.getHeight()) > boxRoot.getHeight()) {
-                                        calY = boxRoot.getHeight() - boxBody.getHeight();
-                                    }
-                                    boxBody.setX(calX);
-                                    boxBody.setY(calY);
-                                }
-                                
-                                boxBody.requestLayout();
-                            }
-                            
-                            @Override
-                            public boolean willChangeBounds() {
-                                return true;
-                            }
-                        };
-                        enterAnim.setInterpolator(new DecelerateInterpolator(2f));
-                        enterAnim.setDuration(enterAnimDurationTemp);
-                        boxBody.startAnimation(enterAnim);
-                        boxBody.setVisibility(View.VISIBLE);
-                        
-                        //模糊背景
-                        if (getStyle().popMenuSettings() != null &&
-                                getStyle().popMenuSettings().blurBackgroundSettings() != null &&
-                                getStyle().popMenuSettings().blurBackgroundSettings().blurBackground()
-                        ) {
-                            int blurFrontColor = getResources().getColor(getStyle().popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()));
-                            blurView = new BlurView(boxBody.getContext(), null);
-                            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(boxBody.getWidth(), targetHeight);
-                            blurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
-                            blurView.setTag("blurView");
-                            blurView.setRadiusPx(getStyle().popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx());
-                            boxBody.addView(blurView, 0, params);
-                        }
-                        if (getStyle().popMenuSettings() != null &&
-                                getStyle().popMenuSettings().backgroundMaskColorRes() != 0) {
-                            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-                            bkgAlpha.setDuration(enterAnimDurationTemp);
-                            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                                @Override
-                                public void onAnimationUpdate(ValueAnimator animation) {
-                                    float value = (float) animation.getAnimatedValue();
-                                    boxRoot.setBkgAlpha(value);
-                                }
-                            });
-                            bkgAlpha.start();
-                        }
-                    } else {
-                        //无绑定按钮的情况下
-                        RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) boxBody.getLayoutParams();
-                        rLp.addRule(RelativeLayout.CENTER_IN_PARENT);
-                        rLp.width = getWidth() == -1 ? RelativeLayout.LayoutParams.MATCH_PARENT : getWidth();
-                        rLp.leftMargin = dip2px(50);
-                        rLp.rightMargin = dip2px(50);
-                        boxBody.setLayoutParams(rLp);
-                        boxBody.setAlpha(0f);
-                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-                            boxBody.setElevation(dip2px(20));
+                    getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
+                        @Override
+                        public void run(Float value) {
+                            boxRoot.setBkgAlpha(value);
                         }
-                        boxBody.setVisibility(View.VISIBLE);
-                        boxBody.animate().alpha(1f).setDuration(enterAnimDurationTemp);
-                        boxBody.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                //模糊背景
-                                if (getStyle().popMenuSettings() != null &&
-                                        getStyle().popMenuSettings().blurBackgroundSettings() != null &&
-                                        getStyle().popMenuSettings().blurBackgroundSettings().blurBackground()
-                                ) {
-                                    int blurFrontColor = getResources().getColor(getStyle().popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()));
-                                    blurView = new BlurView(boxBody.getContext(), null);
-                                    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(boxBody.getWidth(), boxBody.getHeight());
-                                    blurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
-                                    blurView.setTag("blurView");
-                                    blurView.setRadiusPx(getStyle().popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx());
-                                    boxBody.addView(blurView, 0, params);
-                                    
-                                    ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-                                    bkgAlpha.setDuration(enterAnimDurationTemp);
-                                    bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                                        @Override
-                                        public void onAnimationUpdate(ValueAnimator animation) {
-                                            float value = (float) animation.getAnimatedValue();
-                                            boxRoot.setBkgAlpha(value);
-                                        }
-                                    });
-                                    bkgAlpha.start();
-                                }
-                            }
-                        });
-                        
-                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
-                        bkgAlpha.setDuration(enterAnimDurationTemp);
-                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                            @Override
-                            public void onAnimationUpdate(ValueAnimator animation) {
-                                float value = (float) animation.getAnimatedValue();
-                                boxRoot.setBkgAlpha(value);
-                            }
-                        });
-                        bkgAlpha.start();
-                    }
+                    });
                 }
             });
             
@@ -647,7 +447,227 @@ public class PopMenu extends BaseDialog {
                 boxRoot.post(new Runnable() {
                     @Override
                     public void run() {
+                        getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
+                            @Override
+                            public void run(Float value) {
+                                if (boxRoot != null && baseView == null) {
+                                    boxRoot.setBkgAlpha(value);
+                                }
+                                if (value == 0f) {
+                                    dismiss(dialogView);
+                                }
+                            }
+                        });
+                    }
+                });
+            }
+        }
+        
+        protected DialogXAnimInterface<PopMenu> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<PopMenu>() {
+                    
+                    int selectMenuIndex = -1;
+                    
+                    @Override
+                    public void doShowAnim(PopMenu dialog, ObjectRunnable<Float> animProgress) {
+                        long enterAnimDurationTemp = enterAnimDuration != -1 ? enterAnimDuration : (overrideEnterDuration == -1 ? 150 : overrideEnterDuration);
                         
+                        if (baseView != null) {
+                            //有绑定按钮的情况下
+                            int targetHeight = getBodyRealHeight();
+                            boxBody.getLayoutParams().height = 1;
+                            
+                            if (overlayBaseView && !listMenu.isCanScroll()) {
+                                if (baseView instanceof TextView) {
+                                    String baseText = ((TextView) baseView).getText().toString();
+                                    for (CharSequence c : menuList) {
+                                        if (TextUtils.equals(c.toString(), baseText)) {
+                                            selectMenuIndex = menuList.indexOf(c);
+                                            break;
+                                        }
+                                    }
+                                }
+                                //找到已选中的项目
+                                if (selectMenuIndex != -1) {
+                                    int[] viewLoc = new int[2];
+                                    if (listMenu.getChildAt(selectMenuIndex) != null) {
+                                        int itemHeight = listMenu.getChildAt(selectMenuIndex).getMeasuredHeight();
+                                        listMenu.getChildAt(selectMenuIndex).getLocationOnScreen(viewLoc);
+                                        boxBody.setY(baseViewLoc[1] + (baseView.getMeasuredHeight() / 2f) - (viewLoc[1] - boxBody.getY()) - (itemHeight / 2f));
+                                    }
+                                }
+                            }
+                            
+                            //菜单位置计算逻辑
+                            int baseViewLeft = baseViewLoc[0];
+                            int baseViewTop = baseViewLoc[1];
+                            int calX = 0, calY = 0;
+                            if (alignGravity != -1) {
+                                if (isAlignGravity(Gravity.CENTER_VERTICAL)) {
+                                    calY = (Math.max(0, baseViewTop + baseView.getMeasuredHeight() / 2 - boxBody.getHeight() / 2));
+                                }
+                                if (isAlignGravity(Gravity.CENTER_HORIZONTAL)) {
+                                    calX = (Math.max(0, baseViewLeft + (
+                                            getWidth() > 0 ? baseView.getMeasuredWidth() / 2 - getWidth() / 2 : 0
+                                    )));
+                                }
+                                if (isAlignGravity(Gravity.CENTER)) {
+                                    calX = (Math.max(0, baseViewLeft + (
+                                            getWidth() > 0 ? (baseView.getMeasuredWidth() / 2 - getWidth() / 2) : 0
+                                    )));
+                                    calY = (Math.max(0, baseViewTop + baseView.getMeasuredHeight() / 2 - boxBody.getHeight() / 2));
+                                }
+                                if (overlayBaseView) {
+                                    //菜单覆盖在 baseView 上时
+                                    if (isAlignGravity(Gravity.TOP)) {
+                                        calY = (baseViewTop - boxBody.getHeight() + baseView.getHeight());
+                                    }
+                                    if (isAlignGravity(Gravity.LEFT)) {
+                                        calX = (baseViewLeft);
+                                    }
+                                    if (isAlignGravity(Gravity.RIGHT)) {
+                                        calX = (baseViewLeft + (getWidth() > 0 ? baseView.getMeasuredWidth() - width : 0));
+                                    }
+                                    if (isAlignGravity(Gravity.BOTTOM)) {
+                                        calY = (baseViewTop);
+                                    }
+                                } else {
+                                    if (isAlignGravity(Gravity.TOP)) {
+                                        calY = (Math.max(0, baseViewTop - boxBody.getHeight()));
+                                    }
+                                    if (isAlignGravity(Gravity.LEFT)) {
+                                        calX = (Math.max(0, baseViewLeft - boxBody.getWidth()));
+                                    }
+                                    if (isAlignGravity(Gravity.RIGHT)) {
+                                        calX = (Math.max(0, baseViewLeft + baseView.getWidth()));
+                                    }
+                                    if (isAlignGravity(Gravity.BOTTOM)) {
+                                        calY = (Math.max(0, baseViewTop + baseView.getHeight()));
+                                    }
+                                }
+                                if (!offScreen) {
+                                    if (calX < 0) {
+                                        calX = 0;
+                                    }
+                                    if ((calX + boxBody.getWidth()) > boxRoot.getWidth()) {
+                                        calX = boxRoot.getWidth() - boxBody.getWidth();
+                                    }
+                                    if (calY < 0) {
+                                        calY = 0;
+                                    }
+                                    if ((calY + boxBody.getHeight()) > boxRoot.getHeight()) {
+                                        calY = boxRoot.getHeight() - boxBody.getHeight();
+                                    }
+                                }
+                                
+                                if (calX != 0) boxBody.setX(calX);
+                                if (calY != 0) boxBody.setY(calY);
+                            }
+                            
+                            //展开动画
+                            Animation enterAnim = new Animation() {
+                                @Override
+                                protected void applyTransformation(float interpolatedTime, Transformation t) {
+                                    int aimHeight = interpolatedTime == 1 ? ViewGroup.LayoutParams.WRAP_CONTENT : (int) (targetHeight * interpolatedTime);
+                                    boxBody.getLayoutParams().height = aimHeight;
+                                    boxBody.getLayoutParams().width = getWidth() == -1 ? baseView.getWidth() : getWidth();
+                                    if ((boxBody.getY() + aimHeight) > boxRoot.getSafeHeight()) {
+                                        boxBody.setY(boxRoot.getSafeHeight() - aimHeight);
+                                    }
+                                    
+                                    if (!offScreen) {
+                                        float calX = boxBody.getX();
+                                        float calY = boxBody.getY();
+                                        if (calX < 0) {
+                                            calX = 0;
+                                        }
+                                        if ((calX + boxBody.getWidth()) > boxRoot.getWidth()) {
+                                            calX = boxRoot.getWidth() - boxBody.getWidth();
+                                        }
+                                        if (calY < 0) {
+                                            calY = 0;
+                                        }
+                                        if ((calY + boxBody.getHeight()) > boxRoot.getHeight()) {
+                                            calY = boxRoot.getHeight() - boxBody.getHeight();
+                                        }
+                                        boxBody.setX(calX);
+                                        boxBody.setY(calY);
+                                    }
+                                    
+                                    boxBody.requestLayout();
+                                }
+                                
+                                @Override
+                                public boolean willChangeBounds() {
+                                    return true;
+                                }
+                            };
+                            enterAnim.setInterpolator(new DecelerateInterpolator(2f));
+                            enterAnim.setDuration(enterAnimDurationTemp);
+                            boxBody.startAnimation(enterAnim);
+                            boxBody.setVisibility(View.VISIBLE);
+                            
+                            //模糊背景
+                            if (getStyle().popMenuSettings() != null &&
+                                    getStyle().popMenuSettings().blurBackgroundSettings() != null &&
+                                    getStyle().popMenuSettings().blurBackgroundSettings().blurBackground()
+                            ) {
+                                int blurFrontColor = getResources().getColor(getStyle().popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()));
+                                blurView = new BlurView(boxBody.getContext(), null);
+                                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(boxBody.getWidth(), targetHeight);
+                                blurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
+                                blurView.setTag("blurView");
+                                blurView.setRadiusPx(getStyle().popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx());
+                                boxBody.addView(blurView, 0, params);
+                            }
+                        } else {
+                            //无绑定按钮的情况下
+                            RelativeLayout.LayoutParams rLp = (RelativeLayout.LayoutParams) boxBody.getLayoutParams();
+                            rLp.addRule(RelativeLayout.CENTER_IN_PARENT);
+                            rLp.width = getWidth() == -1 ? RelativeLayout.LayoutParams.MATCH_PARENT : getWidth();
+                            rLp.leftMargin = dip2px(50);
+                            rLp.rightMargin = dip2px(50);
+                            boxBody.setLayoutParams(rLp);
+                            boxBody.setAlpha(0f);
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                                boxBody.setElevation(dip2px(20));
+                            }
+                            boxBody.setVisibility(View.VISIBLE);
+                            boxBody.animate().alpha(1f).setDuration(enterAnimDurationTemp);
+                            boxBody.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    //模糊背景
+                                    if (getStyle().popMenuSettings() != null &&
+                                            getStyle().popMenuSettings().blurBackgroundSettings() != null &&
+                                            getStyle().popMenuSettings().blurBackgroundSettings().blurBackground()
+                                    ) {
+                                        int blurFrontColor = getResources().getColor(getStyle().popMenuSettings().blurBackgroundSettings().blurForwardColorRes(isLightTheme()));
+                                        blurView = new BlurView(boxBody.getContext(), null);
+                                        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(boxBody.getWidth(), boxBody.getHeight());
+                                        blurView.setOverlayColor(backgroundColor == -1 ? blurFrontColor : backgroundColor);
+                                        blurView.setTag("blurView");
+                                        blurView.setRadiusPx(getStyle().popMenuSettings().blurBackgroundSettings().blurBackgroundRoundRadiusPx());
+                                        boxBody.addView(blurView, 0, params);
+                                    }
+                                }
+                            });
+                            
+                            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
+                            bkgAlpha.setDuration(enterAnimDurationTemp);
+                            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                                @Override
+                                public void onAnimationUpdate(ValueAnimator animation) {
+                                    animProgress.run((Float) animation.getAnimatedValue());
+                                }
+                            });
+                            bkgAlpha.start();
+                        }
+                    }
+                    
+                    @Override
+                    public void doExitAnim(PopMenu dialog, ObjectRunnable<Float> animProgress) {
                         if (overrideExitDuration != -1) {
                             exitAnimDuration = overrideExitDuration;
                         }
@@ -662,28 +682,19 @@ public class PopMenu extends BaseDialog {
                                 .setInterpolator(new AccelerateInterpolator())
                                 .setDuration(exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
                         
-                        if (baseView == null) {
-                            ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1, 0f);
-                            bkgAlpha.setDuration(exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
-                            bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                                @Override
-                                public void onAnimationUpdate(ValueAnimator animation) {
-                                    float value = (float) animation.getAnimatedValue();
-                                    boxRoot.setBkgAlpha(value);
-                                }
-                            });
-                            bkgAlpha.start();
-                        }
-                        
-                        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+                        ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1, 0f);
+                        bkgAlpha.setDuration(exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
+                        bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                             @Override
-                            public void run() {
-                                dismiss(dialogView);
+                            public void onAnimationUpdate(ValueAnimator animation) {
+                                animProgress.run((Float) animation.getAnimatedValue());
                             }
-                        }, exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
+                        });
+                        bkgAlpha.start();
                     }
-                });
+                };
             }
+            return dialogXAnimImpl;
         }
     }
     
@@ -983,8 +994,36 @@ public class PopMenu extends BaseDialog {
     
     public void hide() {
         isHide = true;
+        hideWithExitAnim = false;
         if (getDialogView() != null) {
             getDialogView().setVisibility(View.GONE);
         }
     }
+    
+    protected boolean hideWithExitAnim;
+    
+    public void hideWithExitAnim() {
+        hideWithExitAnim = true;
+        isHide = true;
+        if (getDialogImpl() != null) {
+            getDialogImpl().getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
+                @Override
+                public void run(Float value) {
+                    getDialogImpl().boxRoot.setBkgAlpha(value);
+                    if (value == 0 && getDialogView() != null) {
+                        getDialogView().setVisibility(View.GONE);
+                    }
+                }
+            });
+        }
+    }
+    
+    public DialogXAnimInterface<PopMenu> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public PopMenu setDialogXAnimImpl(DialogXAnimInterface<PopMenu> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 58 - 17
DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopNotification.java

@@ -29,12 +29,14 @@ import com.kongzue.dialogx.R;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogConvertViewInterface;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.NoTouchInterface;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
 import com.kongzue.dialogx.interfaces.OnBindView;
 import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener;
 import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener;
+import com.kongzue.dialogx.util.ObjectRunnable;
 import com.kongzue.dialogx.util.PopValueAnimator;
 import com.kongzue.dialogx.util.TextInfo;
 import com.kongzue.dialogx.util.views.BlurView;
@@ -77,6 +79,7 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
     protected boolean autoTintIconInLightOrDarkMode = true;
     protected BOOLEAN tintIcon;
     protected float backgroundRadius = -1;
+    protected DialogXAnimInterface<PopNotification> dialogXAnimImpl;
     
     protected int iconResId;
     protected Bitmap iconBitmap;
@@ -338,7 +341,7 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
     }
     
     public PopNotification show() {
-        if (isHide && getDialogView() != null){
+        if (isHide && getDialogView() != null) {
             getDialogView().setVisibility(View.VISIBLE);
             return this;
         }
@@ -588,19 +591,12 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
             boxRoot.post(new Runnable() {
                 @Override
                 public void run() {
-                    Animation enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId == 0 ? R.anim.anim_dialogx_notification_enter : enterAnimResId);
-                    enterAnim.setInterpolator(new DecelerateInterpolator(2f));
-                    if (enterAnimDuration != -1) {
-                        enterAnim.setDuration(enterAnimDuration);
-                    }
-                    enterAnim.setFillAfter(true);
-                    boxBody.startAnimation(enterAnim);
-                    
-                    boxRoot.animate()
-                            .setDuration(enterAnimDuration == -1 ? enterAnim.getDuration() : enterAnimDuration)
-                            .alpha(1f)
-                            .setInterpolator(new DecelerateInterpolator())
-                            .setListener(null);
+                    getDialogXAnimImpl().doShowAnim(me, new ObjectRunnable<Float>() {
+                        @Override
+                        public void run(Float aFloat) {
+        
+                        }
+                    });
                     
                     if (!DialogX.onlyOnePopNotification) {
                         if (popNotificationList != null) {
@@ -746,6 +742,41 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
                 boxRoot.post(new Runnable() {
                     @Override
                     public void run() {
+                        getDialogXAnimImpl().doExitAnim(me, new ObjectRunnable<Float>() {
+                            @Override
+                            public void run(Float value) {
+                                if (value == 0f) {
+                                    waitForDismiss();
+                                }
+                            }
+                        });
+                    }
+                });
+            }
+        }
+        
+        protected DialogXAnimInterface<PopNotification> getDialogXAnimImpl() {
+            if (dialogXAnimImpl == null) {
+                dialogXAnimImpl = new DialogXAnimInterface<PopNotification>() {
+                    @Override
+                    public void doShowAnim(PopNotification dialog, ObjectRunnable<Float> animProgress) {
+                        Animation enterAnim = AnimationUtils.loadAnimation(getTopActivity(), enterAnimResId == 0 ? R.anim.anim_dialogx_notification_enter : enterAnimResId);
+                        enterAnim.setInterpolator(new DecelerateInterpolator(2f));
+                        if (enterAnimDuration != -1) {
+                            enterAnim.setDuration(enterAnimDuration);
+                        }
+                        enterAnim.setFillAfter(true);
+                        boxBody.startAnimation(enterAnim);
+    
+                        boxRoot.animate()
+                                .setDuration(enterAnimDuration == -1 ? enterAnim.getDuration() : enterAnimDuration)
+                                .alpha(1f)
+                                .setInterpolator(new DecelerateInterpolator())
+                                .setListener(null);
+                    }
+                    
+                    @Override
+                    public void doExitAnim(PopNotification dialog, ObjectRunnable<Float> animProgress) {
                         Animation exitAnim = AnimationUtils.loadAnimation(getTopActivity() == null ? boxRoot.getContext() : getTopActivity(), exitAnimResId == 0 ? R.anim.anim_dialogx_notification_exit : exitAnimResId);
                         if (exitAnimDuration != -1) {
                             exitAnim.setDuration(exitAnimDuration);
@@ -758,15 +789,16 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
                                 .setInterpolator(new AccelerateInterpolator())
                                 .setDuration(exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
                         
-                        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+                        runOnMainDelay(new Runnable() {
                             @Override
                             public void run() {
-                                waitForDismiss();
+                                animProgress.run(0f);
                             }
                         }, exitAnimDuration == -1 ? exitAnim.getDuration() : exitAnimDuration);
                     }
-                });
+                };
             }
+            return dialogXAnimImpl;
         }
     }
     
@@ -1288,4 +1320,13 @@ public class PopNotification extends BaseDialog implements NoTouchInterface {
     public float getRadius() {
         return backgroundRadius;
     }
+    
+    public DialogXAnimInterface<PopNotification> getDialogXAnimImpl() {
+        return dialogXAnimImpl;
+    }
+    
+    public PopNotification setDialogXAnimImpl(DialogXAnimInterface<PopNotification> dialogXAnimImpl) {
+        this.dialogXAnimImpl = dialogXAnimImpl;
+        return this;
+    }
 }

+ 3 - 0
DialogX/src/main/java/com/kongzue/dialogx/interfaces/BaseDialog.java

@@ -674,6 +674,9 @@ public abstract class BaseDialog {
     }
     
     protected static void runOnMainDelay(Runnable runnable, long delay) {
+        if (delay < 0) {
+            return;
+        }
         if (!DialogX.autoRunOnUIThread) {
             runnable.run();
         }

+ 21 - 0
DialogX/src/main/java/com/kongzue/dialogx/interfaces/DialogXAnimInterface.java

@@ -0,0 +1,21 @@
+package com.kongzue.dialogx.interfaces;
+
+import android.animation.ValueAnimator;
+
+import com.kongzue.dialogx.util.ObjectRunnable;
+
+/**
+ * @author: Kongzue
+ * @github: https://github.com/kongzue/
+ * @homepage: http://kongzue.com/
+ * @mail: myzcxhh@live.cn
+ * @createTime: 2022/9/5 9:21
+ */
+public abstract class DialogXAnimInterface<D> {
+    
+    public void doShowAnim(D dialog, ObjectRunnable<Float> animProgress) {
+    }
+    
+    public void doExitAnim(D dialog, ObjectRunnable<Float> animProgress) {
+    }
+}

+ 0 - 2
DialogX/src/main/java/com/kongzue/dialogx/util/views/ProgressView.java

@@ -222,7 +222,6 @@ public class ProgressView extends View implements ProgressViewInterface {
     }
     
     private void drawDoneMark(int status, Canvas canvas) {
-        Log.e(">>>", "drawDoneMark.status: "+status );
         if (rotateAnimator.getInterpolator() != interpolator) {
             rotateAnimator.setInterpolator(interpolator);
         }
@@ -408,7 +407,6 @@ public class ProgressView extends View implements ProgressViewInterface {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 tickAnimatorValue = (float) animation.getAnimatedValue();
-                Log.e(">>>", "onAnimationUpdate: " + tickAnimatorValue);
                 invalidate();
             }
         });

+ 2 - 1
DialogX/src/main/res/anim/anim_dialogx_default_enter.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fillAfter="true">
 
     <scale
         android:duration="300"

+ 2 - 1
DialogX/src/main/res/anim/anim_dialogx_default_exit.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android">
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fillAfter="true">
 
     <alpha
         android:duration="300"

+ 53 - 2
app/src/main/java/com/kongzue/dialogxdemo/activity/MainActivity.java

@@ -1,5 +1,6 @@
 package com.kongzue.dialogxdemo.activity;
 
+import android.animation.ValueAnimator;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -16,6 +17,9 @@ import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
@@ -58,6 +62,7 @@ import com.kongzue.dialogx.dialogs.TipDialog;
 import com.kongzue.dialogx.dialogs.WaitDialog;
 import com.kongzue.dialogx.interfaces.BaseDialog;
 import com.kongzue.dialogx.interfaces.DialogLifecycleCallback;
+import com.kongzue.dialogx.interfaces.DialogXAnimInterface;
 import com.kongzue.dialogx.interfaces.DialogXStyle;
 import com.kongzue.dialogx.interfaces.MenuItemTextInfoInterceptor;
 import com.kongzue.dialogx.interfaces.OnBackPressedListener;
@@ -72,6 +77,7 @@ import com.kongzue.dialogx.style.IOSStyle;
 import com.kongzue.dialogx.style.KongzueStyle;
 import com.kongzue.dialogx.style.MIUIStyle;
 import com.kongzue.dialogx.style.MaterialStyle;
+import com.kongzue.dialogx.util.ObjectRunnable;
 import com.kongzue.dialogx.util.TextInfo;
 import com.kongzue.dialogx.util.views.ActivityScreenShotImageView;
 import com.kongzue.dialogxdemo.BuildConfig;
@@ -373,7 +379,7 @@ public class MainActivity extends BaseActivity {
                             @Override
                             public boolean onClick(PopMenu dialog, CharSequence text, int index) {
                                 if (index == 0) {
-                                    dialog.setMenuList(new String[]{"A", "B", "C"});
+                                    dialog.setMenuList(new String[]{"产品A", "产品B", "产品C"});
                                     return true;
                                 }
                                 return false;
@@ -933,7 +939,52 @@ public class MainActivity extends BaseActivity {
                                 });
                             }
                         })
-                        .setMaskColor(getResources().getColor(R.color.black30));
+                        .setMaskColor(getResources().getColor(R.color.black30))
+                        //实现完全自定义动画效果
+//                        .setDialogXAnimImpl(new DialogXAnimInterface<CustomDialog>() {
+//                            @Override
+//                            public void doShowAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
+//                                Animation enterAnim;
+//
+//                                int enterAnimResId = com.kongzue.dialogx.R.anim.anim_dialogx_top_enter;
+//                                enterAnim = AnimationUtils.loadAnimation(me, enterAnimResId);
+//                                enterAnim.setInterpolator(new DecelerateInterpolator(2f));
+//
+//                                long enterAnimDurationTemp = enterAnim.getDuration();
+//
+//                                enterAnim.setDuration(enterAnimDurationTemp);
+//                                customDialog.getDialogImpl().boxCustom.startAnimation(enterAnim);
+//
+//                                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
+//                                bkgAlpha.setDuration(enterAnimDurationTemp);
+//                                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+//                                    @Override
+//                                    public void onAnimationUpdate(ValueAnimator animation) {
+//                                        animProgress.run((Float) animation.getAnimatedValue());
+//                                    }
+//                                });
+//                                bkgAlpha.start();
+//                            }
+//
+//                            @Override
+//                            public void doExitAnim(CustomDialog customDialog, ObjectRunnable<Float> animProgress) {
+//                                int exitAnimResIdTemp = com.kongzue.dialogx.R.anim.anim_dialogx_default_exit;
+//
+//                                Animation exitAnim = AnimationUtils.loadAnimation(me, exitAnimResIdTemp);
+//                                customDialog.getDialogImpl().boxCustom.startAnimation(exitAnim);
+//
+//                                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
+//                                bkgAlpha.setDuration(exitAnim.getDuration());
+//                                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+//                                    @Override
+//                                    public void onAnimationUpdate(ValueAnimator animation) {
+//                                        animProgress.run((Float) animation.getAnimatedValue());
+//                                    }
+//                                });
+//                                bkgAlpha.start();
+//                            }
+//                        })
+                ;
             }
         });