让DialogX的消息提示玩出花 - 自定义PopTip和 PopNotification的避让动画

让DialogX的消息提示玩出花 - 自定义PopTip和 PopNotification的避让动画

DialogX 的 PopTip 组件提供了类似于安卓原生 Toast 的非阻断式简单提示效果,更进一步提供了更多的扩展功能,通常情况下,PopTip 能够同时显示多个吐司消息提示,但默认情况下堆叠多个提示时会占用很大的屏幕空间面积,遮挡原本的 UI,造成视觉上的干扰。其次,PopTip 也可以通过 .noAutoDismiss() 方法实现持续显示而不自动关闭,同时也可以通过 .setButton(...) 方法设置一个按钮,常被用于展示待处理事务的场景,此时如果同时需要等待用户处理的事务过多则会导致界面遮挡严重无法正常使用(如下图)

那么是否有一种办法能解决这个问题呢?在最新的 DialogX 测试版本中提供了避让动画控制器 PopMoveDisplacementInterceptor 就可以轻松解决这个问题。

避让动画控制器是一个组件全局接口,提供了两个可覆写方法,其中 resetAnimY 方法会在执行动画前进行,目的是预算避让动画的目标位置,animUpdater 方法则会在动画执行过程中不断执行,目的是动态调整动画效果,请勿在此方法中执行过于耗时的操作,具体含义如下:

java 复制代码
/**
 * 重置提示对话框新增时,旧的对话框让位位移的动画具体参数
 *
 * @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;
}

根据这两个方法所提供的参数即可实时对 PopTip 的避让动画进行修改,例如我们可以在新的 PopTip 弹出时,让旧 PopTip 减少位移缩小一些挪至新 PopTip 的背后,以此类推即可让多个 PopTip 的显示不再占用太大的用户操作空间(如下图)

同时,在用户通过操作关闭前层的 PopTip 时,则需要将后层的 PopTip 恢复到原有的位置,而这个想法通过避让动画控制器 PopMoveDisplacementInterceptor 就可以轻松实现。

根据上边的接口,通过如下代码实现:

java 复制代码
PopTip.moveDisplacementInterceptor = 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);
        } else {    //如果是前移
            float originalScale = 1f - zoomRatio * (allTipSize - index - 1);
            float currentScale = originalScale * (1f - zoomRatio * 1);
            float targetScale = currentScale + (originalScale - currentScale) * progress;
        }
        if (targetScale > 1) targetScale = 1;
        dialogBody.setScaleX(targetScale);
        dialogBody.setScaleY(targetScale);
        dialogBody.setAlpha(targetScale);
        return false;
    }
};

同样的,PopNotification 对话框组件可以在无需通知权限的情况下在app内实现通知提示效果,这个对话框组件也支持避让动画控制器 PopMoveDisplacementInterceptor ,可以实现相似的效果(如下图)

具体实现代码如下:

ini 复制代码
PopNotification.moveDisplacementInterceptor=new PopMoveDisplacementInterceptor<PopNotification>() {
    @Override
    public float resetAnimY(int index, PopNotification dialog, float fromY, float toY, int dialogHeight, int allTipSize, boolean moveBack) {
        if (moveBack) {
            return fromY + dialogHeight * 0.25f - 0.1f * (allTipSize - index - 1) * dialogHeight;
        } else {
            return fromY - dialogHeight * 0.25f+ 0.1f * (allTipSize - index - 1) * dialogHeight;
        }
    }

    float zoomRatio = 0.03f;

    @Override
    public boolean animUpdater(int index, PopNotification 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 (targetScale > 1) targetScale = 1;
            dialogBody.setScaleX(targetScale);
            dialogBody.setScaleY(targetScale);
            dialogBody.setAlpha(targetScale);
        }
        return false;
    }
};

如此即可轻松完成避让动画的改进,以解决占用很大的屏幕空间面积的问题。

上述代码中 PopTip 的样式为模仿 Snackbar 的样式实现,要将默认的 PopTip 改为这个样式,可以增加一个布局文件 layout_dialogx_poptip_snackbar.xml,具体实现代码如下:

ini 复制代码
<?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>

通过以下代码修改默认主题 MaterialStyle 的设置,使用其他主题可以参考修改:

typescript 复制代码
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;
            }
        };
    }
};

相关参考:使用 DialogX 快速构建 Android App 对话框使用 DialogX 快速构建 Android App 对话框 - 掘金

相关推荐
天河归来16 分钟前
springboot框架redis开启管道批量写入数据
java·spring boot·redis
没有了遇见18 分钟前
Android 通过 SO 库安全存储敏感数据,解决接口劫持问题
android
hsx66619 分钟前
使用一个 RecyclerView 构建复杂多类型布局
android
hsx66621 分钟前
利用 onMeasure、onLayout、onDraw 创建自定义 View
android
守城小轩23 分钟前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
张先shen25 分钟前
Elasticsearch RESTful API入门:全文搜索实战
java·大数据·elasticsearch·搜索引擎·全文检索·restful
codervibe26 分钟前
如何用 Spring Security 构建无状态权限控制系统(含角色菜单控制)
java·后端
codervibe29 分钟前
项目中如何用策略模式实现多角色登录解耦?(附实战代码)
java·后端
TCChzp31 分钟前
synchronized全链路解析:从字节码到JVM内核的锁实现与升级策略
java·jvm
大葱白菜32 分钟前
🧩 Java 枚举详解:从基础到实战,掌握类型安全与优雅设计
java·程序员