Browse Source

0.0.50.beta34.pre
- Lifecycle.State.DESTROYED 时机调整到销毁 dialogImpl 之前;
- CustomDialog 中监听 baseView 位置变化的 OnDrawListener 修改为 OnPreDrawListener 以解决部分系统存在的兼容性问题;
- BaseDialog 新增 callDialogDismiss() 用于调用对应对话框实例的 dismiss()
- 新增 bindDismissWithLifecycleOwner(LifecycleOwner) 用于绑定具备特定生命周期的组件,当组件关闭时自动关闭对话框;
- PopTip 增加了限制最大可显示数量设置,例如限制只能显示最多四个提示 `PopTip.maxShowCount = 4;`;
- PopTip 增加了让位位移动画控制拦截器 PopMoveDisplacementInterceptor,可通过 `PopTip.moveUpDisplacementInterceptor` 进行设置;

Kongzue 2 months ago
parent
commit
45e2e51dcf

+ 149 - 30
DialogX/src/main/java/com/kongzue/dialogx/dialogs/PopTip.java

@@ -38,6 +38,7 @@ import com.kongzue.dialogx.interfaces.NoTouchInterface;
 import com.kongzue.dialogx.interfaces.OnBindView;
 import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener;
 import com.kongzue.dialogx.interfaces.OnSafeInsetsChangeListener;
+import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor;
 import com.kongzue.dialogx.util.TextInfo;
 import com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout;
 
@@ -66,6 +67,8 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
     public static long overrideExitDuration = -1;
     public static int overrideEnterAnimRes = 0;
     public static int overrideExitAnimRes = 0;
+    public static int maxShowCount = Integer.MAX_VALUE;
+    public static PopMoveDisplacementInterceptor<PopTip> moveUpDisplacementInterceptor;
 
     protected OnBindView<PopTip> onBindView;
     protected DialogLifecycleCallback<PopTip> dialogLifecycleCallback;
@@ -315,9 +318,19 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                 }
             } else {
                 if (popTipList != null) {
-                    for (int i = 0; i < popTipList.size(); i++) {
-                        PopTip popInstance = popTipList.get(i);
-                        popInstance.moveUp();
+                    CopyOnWriteArrayList<PopTip> copyPopTipList = new CopyOnWriteArrayList<>(popTipList);
+                    for (int i = 0; i < copyPopTipList.size(); i++) {
+                        PopTip popInstance = copyPopTipList.get(i);
+                        if (copyPopTipList.size() < maxShowCount) {
+                            popInstance.moveBack();
+                        } else {
+                            if (i <= copyPopTipList.size() - maxShowCount) {
+                                popInstance.dismiss();
+                                popTipList.remove(popInstance);
+                            } else {
+                                popInstance.moveBack();
+                            }
+                        }
                     }
                 }
             }
@@ -373,9 +386,19 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                 }
             } else {
                 if (popTipList != null) {
-                    for (int i = 0; i < popTipList.size(); i++) {
-                        PopTip popInstance = popTipList.get(i);
-                        popInstance.moveUp();
+                    CopyOnWriteArrayList<PopTip> copyPopTipList = new CopyOnWriteArrayList<>(popTipList);
+                    for (int i = 0; i < copyPopTipList.size(); i++) {
+                        PopTip popInstance = copyPopTipList.get(i);
+                        if (copyPopTipList.size() < maxShowCount) {
+                            popInstance.moveBack();
+                        } else {
+                            if (i <= copyPopTipList.size() - maxShowCount) {
+                                popInstance.dismiss();
+                                popTipList.remove(popInstance);
+                            } else {
+                                popInstance.moveBack();
+                            }
+                        }
                     }
                 }
             }
@@ -720,12 +743,22 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                     public void run() {
                         getDialogXAnimImpl().doExitAnim(me, boxBody);
 
+                        preRecycle = true;
                         runOnMainDelay(new Runnable() {
                             @Override
                             public void run() {
                                 waitForDismiss();
                             }
                         }, getExitAnimationDuration(null));
+
+                        if (popTipList != null) {
+                            //使位于自己之前的PopTip moveDown
+                            int index = popTipList.indexOf(me);
+                            for (int i = 0; i < index; i++) {
+                                PopTip popTip = popTipList.get(i);
+                                popTip.moveFront();
+                            }
+                        }
                     }
                 });
             }
@@ -809,10 +842,11 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
             return;
         }
         preRecycle = true;
+        getDialogView().setVisibility(View.GONE);
         CopyOnWriteArrayList<PopTip> copyPopTipList = new CopyOnWriteArrayList<>(popTipList);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             copyPopTipList.removeIf(Objects::isNull);
-        }else{
+        } else {
             Iterator<PopTip> iterator = copyPopTipList.iterator();
             while (iterator.hasNext()) {
                 if (iterator.next() == null) {
@@ -820,21 +854,10 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                 }
             }
         }
-        boolean allPreRecycled = true;
-        for (PopTip popTip : copyPopTipList) {
-            if (!popTip.preRecycle) {
-                allPreRecycled = false;
-                break;
-            }
-        }
-        if (allPreRecycled) {
-            for (PopTip popTip : copyPopTipList) {
-                dismiss(popTip.getDialogView());
-            }
-        }
+        dismiss(getDialogView());
     }
 
-    private void moveUp() {
+    private void moveBack() {
         if (getDialogImpl() != null && getDialogImpl().boxBody != null) {
             if (getDialogImpl() == null || getDialogImpl().boxBody == null) return;
             View bodyView = getDialogImpl().boxBody;
@@ -862,11 +885,16 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                             moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f;
                             break;
                     }
+                    if (moveUpDisplacementInterceptor != null) {
+                        moveAimTop = moveUpDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), true);
+                    }
                     if (bodyView.getTag() instanceof ValueAnimator) {
                         ((ValueAnimator) bodyView.getTag()).end();
                     }
-                    log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop);
-                    ValueAnimator valueAnimator = ValueAnimator.ofFloat(bodyView.getY(), moveAimTop);
+                    //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop);
+                    final float fromY = bodyView.getY();
+                    float toY = moveAimTop;
+                    ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY);
                     bodyView.setTag(valueAnimator);
                     valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
@@ -876,8 +904,13 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
                                 return;
                             }
                             View bodyView = getDialogImpl().boxBody;
+                            float value = (Float) animation.getAnimatedValue();
+                            float totalDistance = toY - fromY;
+                            if (moveUpDisplacementInterceptor != null && moveUpDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), true)) {
+                                return;
+                            }
                             if (bodyView != null && bodyView.isAttachedToWindow()) {
-                                bodyView.setY((Float) animation.getAnimatedValue());
+                                bodyView.setY(value);
                             }
                         }
                     });
@@ -888,6 +921,82 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
         }
     }
 
+    private void moveFront() {
+        if (getDialogImpl() != null && getDialogImpl().boxBody != null) {
+            if (getDialogImpl() == null || getDialogImpl().boxBody == null) return;
+            View bodyView = getDialogImpl().boxBody;
+            bodyView.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (getDialogImpl() == null) {
+                        return;
+                    }
+                    if (align == null && style.popTipSettings() != null) {
+                        align = style.popTipSettings().align();
+                    }
+                    if (align == null) align = DialogXStyle.PopTipSettings.ALIGN.TOP;
+                    float moveAimTop = 0;
+                    switch (align) {
+                        case TOP:
+                            moveAimTop = bodyView.getY() - bodyView.getHeight() * 1.3f;
+                            break;
+                        case TOP_INSIDE:
+                            moveAimTop = bodyView.getY() - bodyView.getHeight() + bodyView.getPaddingTop();
+                            break;
+                        case CENTER:
+                        case BOTTOM:
+                        case BOTTOM_INSIDE:
+                            moveAimTop = bodyView.getY() + bodyView.getHeight() * 1.3f;
+                            break;
+                    }
+                    if (moveUpDisplacementInterceptor != null) {
+                        moveAimTop = moveUpDisplacementInterceptor.resetAnimY(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView.getY(), moveAimTop, (int) (bodyView.getHeight() / bodyView.getScaleY()), popTipList == null ? 1 : popTipList.size(), false);
+                    }
+                    if (bodyView.getTag() instanceof ValueAnimator) {
+                        ((ValueAnimator) bodyView.getTag()).end();
+                    }
+                    //log("#Animation from:" + bodyView.getY() + " to:" + moveAimTop);
+                    final float fromY = bodyView.getY();
+                    float toY = moveAimTop;
+                    ValueAnimator valueAnimator = ValueAnimator.ofFloat(fromY, toY);
+                    bodyView.setTag(valueAnimator);
+                    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            if (getDialogImpl() == null || !isShow) {
+                                animation.cancel();
+                                return;
+                            }
+                            View bodyView = getDialogImpl().boxBody;
+                            float value = (Float) animation.getAnimatedValue();
+                            float totalDistance = toY - fromY;
+                            if (moveUpDisplacementInterceptor != null && moveUpDisplacementInterceptor.animUpdater(popTipList == null ? 0 : popTipList.indexOf(me), me, bodyView, fromY, toY, Math.max(0f, Math.min(1f, (totalDistance == 0f ? 1f : (value - fromY) / totalDistance))), animation, popTipList == null ? 1 : countDisplayPopTipsNum(), false)) {
+                                return;
+                            }
+                            if (bodyView != null && bodyView.isAttachedToWindow()) {
+                                bodyView.setY(value);
+                            }
+                        }
+                    });
+                    valueAnimator.setDuration(enterAnimDuration == -1 ? 300 : enterAnimDuration).setInterpolator(new DecelerateInterpolator(2f));
+                    valueAnimator.start();
+                }
+            });
+        }
+    }
+
+    private int countDisplayPopTipsNum() {
+        if (popTipList == null) return 0;
+        int count = 0;
+        for (int i = 0; i < popTipList.size(); i++) {
+            PopTip tips = popTipList.get(i);
+            if (tips != null && !tips.preRecycle) {
+                count++;
+            }
+        }
+        return count;
+    }
+
     public void refreshUI() {
         if (getDialogImpl() == null) return;
         runOnMain(new Runnable() {
@@ -1127,9 +1236,19 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
             }
         } else {
             if (popTipList != null) {
-                for (int i = 0; i < popTipList.size(); i++) {
-                    PopTip popInstance = popTipList.get(i);
-                    popInstance.moveUp();
+                CopyOnWriteArrayList<PopTip> copyPopTipList = new CopyOnWriteArrayList<>(popTipList);
+                for (int i = 0; i < copyPopTipList.size(); i++) {
+                    PopTip popInstance = copyPopTipList.get(i);
+                    if (copyPopTipList.size() < maxShowCount) {
+                        popInstance.moveBack();
+                    } else {
+                        if (i <= copyPopTipList.size() - maxShowCount) {
+                            popInstance.dismiss();
+                            popTipList.remove(popInstance);
+                        } else {
+                            popInstance.moveBack();
+                        }
+                    }
                 }
             }
         }
@@ -1423,22 +1542,22 @@ public class PopTip extends BaseDialog implements NoTouchInterface {
         return this;
     }
 
-    public PopTip cleanAction(int actionId){
+    public PopTip cleanAction(int actionId) {
         dialogActionRunnableMap.remove(actionId);
         return this;
     }
 
-    public PopTip cleanAllAction(){
+    public PopTip cleanAllAction() {
         dialogActionRunnableMap.clear();
         return this;
     }
 
     // for BaseDialog use
-    public void callDialogDismiss(){
+    public void callDialogDismiss() {
         dismiss();
     }
 
-    public PopTip bindDismissWithLifecycleOwner(LifecycleOwner owner){
+    public PopTip bindDismissWithLifecycleOwner(LifecycleOwner owner) {
         super.bindDismissWithLifecycleOwnerPrivate(owner);
         return this;
     }

+ 41 - 0
DialogX/src/main/java/com/kongzue/dialogx/interfaces/PopMoveDisplacementInterceptor.java

@@ -0,0 +1,41 @@
+package com.kongzue.dialogx.interfaces;
+
+import android.animation.ValueAnimator;
+import android.view.View;
+
+public abstract class PopMoveDisplacementInterceptor<D extends BaseDialog> {
+
+    /**
+     * 重置提示对话框新增时,旧的对话框让位位移的动画具体参数
+     *
+     * @param index        对话框索引
+     * @param dialog       对话框
+     * @param fromY        从哪来
+     * @param toY          往哪去
+     * @param dialogHeight 对话框本身的高度
+     * @param allTipSize   提示框总数
+     * @param moveBack     是否向后位移
+     * @return 你要改为往哪去
+     */
+    public float resetAnimY(int index, D dialog, float fromY, float toY, int dialogHeight, int allTipSize, boolean moveBack) {
+        return toY;
+    }
+
+    /**
+     * 动画更新器
+     *
+     * @param index         对话框索引
+     * @param dialog        对话框
+     * @param dialogBody    对话框内容布局
+     * @param fromY         从哪来
+     * @param toY           往哪去
+     * @param progress      动画进度(0f~1f)
+     * @param animation     动画执行器
+     * @param allTipSize    提示框总数
+     * @param moveBack      是否向后位移
+     * @return 返回true表示拦截处理,否则依然会执行原本的动画
+     */
+    public boolean animUpdater(int index, D dialog, View dialogBody, float fromY, float toY, float progress, ValueAnimator animation, int allTipSize, boolean moveBack) {
+        return false;
+    }
+}

+ 73 - 6
app/src/main/java/com/kongzue/dialogxdemo/activity/MainActivity.java

@@ -42,8 +42,6 @@ import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -86,6 +84,7 @@ import com.kongzue.dialogx.interfaces.OnInputDialogButtonClickListener;
 import com.kongzue.dialogx.interfaces.OnMenuButtonClickListener;
 import com.kongzue.dialogx.interfaces.OnMenuItemClickListener;
 import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener;
+import com.kongzue.dialogx.interfaces.PopMoveDisplacementInterceptor;
 import com.kongzue.dialogx.style.IOSStyle;
 import com.kongzue.dialogx.style.KongzueStyle;
 import com.kongzue.dialogx.style.MIUIStyle;
@@ -474,9 +473,6 @@ public class MainActivity extends BaseActivity {
             }
         });
 
-        BottomDialog dialog = null;
-        dialog.callDialogDismiss();
-        
         btnFullScreenDialogFragment.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -1261,10 +1257,81 @@ public class MainActivity extends BaseActivity {
             }
         });
 
+        PopTip.maxShowCount = 4;        //限制最大可显示数量
+
+        DialogX.globalStyle = new MaterialStyle() {
+            @Override
+            public PopTipSettings popTipSettings() {
+                return new PopTipSettings() {
+                    @Override
+                    public int layout(boolean light) {
+                        return R.layout.layout_dialogx_poptip_snackbar;
+                    }
+
+                    @Override
+                    public ALIGN align() {
+                        return ALIGN.BOTTOM;
+                    }
+
+                    @Override
+                    public int enterAnimResId(boolean light) {
+                        return com.kongzue.dialogx.R.anim.anim_dialogx_default_enter;
+                    }
+
+                    @Override
+                    public int exitAnimResId(boolean light) {
+                        return com.kongzue.dialogx.R.anim.anim_dialogx_default_exit;
+                    }
+                };
+            }
+        };
+
+        //重置位移动画位置
+        PopTip.moveUpDisplacementInterceptor = new PopMoveDisplacementInterceptor<PopTip>() {
+            @Override
+            public float resetAnimY(int index, PopTip dialog, float fromY, float toY, int dialogHeight, int allTipSize, boolean moveBack) {
+                if (moveBack) {
+                    return fromY - dialogHeight * 0.5f + 0.15f * (allTipSize - index - 1) * dialogHeight;
+                }else{
+                    return fromY + dialogHeight * 0.5f - 0.15f * (allTipSize - index - 1) * dialogHeight;
+                }
+            }
+
+            float zoomRatio = 0.03f;
+
+            @Override
+            public boolean animUpdater(int index, PopTip dialog, View dialogBody, float fromY, float toY, float progress, ValueAnimator animation, int allTipSize, boolean moveBack) {
+                if (moveBack){
+                    float originalScale = 1f - zoomRatio * (allTipSize - index - 1);
+                    float targetScale = originalScale * (1f - zoomRatio * progress);
+                    if (targetScale>1)targetScale=1;
+                    dialogBody.setScaleX(targetScale);
+                    dialogBody.setScaleY(targetScale);
+                    dialogBody.setAlpha(targetScale);
+                }else{
+                    float originalScale = 1f - zoomRatio * (allTipSize - index - 1);
+                    float currentScale = originalScale * (1f - zoomRatio * 1);
+                    float targetScale = currentScale + (originalScale - currentScale) * progress;
+
+                    if (index==0){
+                        log("originalScale=" + originalScale + " ("+index + "/" + allTipSize + ")"  + " targetScale="+targetScale + " progress="+progress);
+                    }
+
+                    if (targetScale > 1) targetScale = 1;
+                    dialogBody.setScaleX(targetScale);
+                    dialogBody.setScaleY(targetScale);
+                    dialogBody.setAlpha(targetScale);
+                }
+                return false;
+            }
+        };
+
         btnPoptip.setOnClickListener(new View.OnClickListener() {
+            int index;
             @Override
             public void onClick(View v) {
-                PopTip.show("这是一个提示");
+                index++;
+                PopTip.show("任务 " + index + " 已完成处理","撤销").setEnterAnimDuration(500).iconSuccess().noAutoDismiss();
             }
         });
 

+ 73 - 0
app/src/main/res/layout/layout_dialogx_poptip_snackbar.xml

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/box_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    app:baseFocusable="false">
+
+    <LinearLayout
+        android:id="@+id/box_body"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        android:layout_marginBottom="10dp"
+        android:background="@drawable/rect_dialogx_material_poptip_bkg_night"
+        android:elevation="5dp"
+        android:gravity="center_vertical"
+        android:minHeight="50dp"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/img_dialogx_pop_icon"
+            android:layout_width="26dp"
+            android:layout_height="26dp"
+            android:layout_marginLeft="15dp"
+            android:layout_marginRight="-5dp"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/txt_dialogx_pop_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="15dp"
+            android:layout_marginTop="10dp"
+            android:layout_marginRight="15dp"
+            android:layout_marginBottom="10dp"
+            android:layout_weight="1"
+            android:gravity="left|center_vertical"
+            android:text="Sure?"
+            android:textColor="@color/white"
+            android:textSize="14dp" />
+
+        <RelativeLayout
+            android:id="@+id/box_custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+
+        <TextView
+            android:id="@+id/txt_dialogx_button"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="-15dp"
+            android:layout_marginTop="5dp"
+            android:layout_marginRight="5dp"
+            android:layout_marginBottom="5dp"
+            android:background="@drawable/button_dialogx_material_night"
+            android:gravity="left|center_vertical"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:singleLine="true"
+            android:text="Dismiss"
+            android:textColor="@color/dialogxPopButtonBlueDark"
+            android:textSize="14dp"
+            android:visibility="gone" />
+
+    </LinearLayout>
+
+</com.kongzue.dialogx.util.views.DialogXBaseRelativeLayout>