区块链技术博客
www.b2bchain.cn

自定义 DialogX 主题求职学习资料

D0b2wT.gif

本文介绍了自定义 DialogX 主题求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

自定义 DialogX 主题

DialogX 支持自定义 DialogX 的主题效果,你可以在 https://github.com/kongzue/DialogX 的项目源代码中看到由我已经实现的三套主题,这些主题都支持从外部进行引入使用,但因为是预置的主题,不一定能完全满足开发者的实际需求,在保证整体框架不会臃肿的情况下,DialogX 提供了专用的主题接口用于提供更丰富的主题可定制性。

要制作自定义主题,首先你需要了解 DialogX 主题接口,该接口是一个 jar 包,你可以在这里下载到它:点击下载DialogXInterface.jar,其源代码在:点击查看 DialogXInterface 接口源代码

DialogX 提供了一个接口 DialogXStyle,开发者只需要实现其中的方法并重写相关代码即可完成自定义主题的制作。

此文档适用于 DialogXInterface 版本:1

DialogXStyle解析

以下以 iOS 主题样式为例,介绍重写 DialogXStyle 接口后的范例源代码:

public class IOSStyle implements DialogXStyle {      private IOSStyle() {     }      public static IOSStyle style() {         return new IOSStyle();     }           public int layout(boolean light) {         return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark;     }           public int enterAnimResId() {         return R.anim.anim_dialogx_ios_enter;     }           public int exitAnimResId() {         return 0;     }           public int[] verticalButtonOrder() {         return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL};     }           public int[] horizontalButtonOrder() {         return new int[]{BUTTON_CANCEL, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_OK};     }           public int splitWidthPx() {         return 1;     }           public int splitColorRes(boolean light) {         return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark;     }           public BlurBackgroundSetting messageDialogBlurSettings() {         return new BlurBackgroundSetting() {                          public boolean blurBackground() {                 return true;             }                           public int blurForwardColorRes(boolean light) {                 return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;             }                           public int blurBackgroundRoundRadiusPx() {                 return dip2px(15);             }         };     }      private int dip2px(float dpValue) {         final float scale = Resources.getSystem().getDisplayMetrics().density;         return (int) (dpValue * scale + 0.5f);     }           public HorizontalButtonRes overrideHorizontalButtonRes() {         return new HorizontalButtonRes() {                          public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 if (visibleButtonCount == 1) {                     return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;                 } else {                     return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;                 }             }                           public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;             }                           public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public VerticalButtonRes overrideVerticalButtonRes() {         return new VerticalButtonRes() {                          public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }                           public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             }                           public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public WaitTipRes overrideWaitTipRes() {         return new WaitTipRes() {                          public boolean blurBackground() {                 return true;             }                           public int overrideBackgroundColorRes(boolean light) {                 return 0;             }                           public int overrideTextColorRes(boolean light) {                 return light ? R.color.white : R.color.black;             }                           public ProgressViewInterface overrideWaitView(Context context, boolean light) {                 return new ProgressView(context).setLightMode(light);             }         };     }           public BottomDialogRes overrideBottomDialogRes() {         return new BottomDialogRes() {                           public boolean touchSlide() {                 return false;             }                           public int overrideDialogLayout(boolean light) {                 return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;             }                           public int overrideMenuDividerDrawableRes(boolean light) {                 return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;             }                           public int overrideMenuDividerHeight(boolean light) {                 return 1;             }                           public int overrideMenuTextColor(boolean light) {                 return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;             }                           public float overrideBottomDialogMaxHeight() {                 return 0f;             }                           public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {                 if (light) {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_light;                     }                 } else {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_dark;                     }                 }             }                           public int overrideSelectionMenuBackgroundColor(boolean light) {                 return 0;             }                           public boolean selectionImageTint(boolean light) {                 return true;             }                           public int overrideSelectionImage(boolean light, boolean isSelected) {                 return 0;             }         };     }           public PopTipSettings popTipSettings() {         return new PopTipSettings() {                          public int layout(boolean light) {                 return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;             }                           public ALIGN align() {                 return ALIGN.TOP;             }                           public int enterAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_enter;             }                           public int exitAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_exit;             }         };     } }

大部分接口都可以 return null 来使用默认样式不做修改,以下会逐一介绍相关接口的方法。

首先提供一个实例化方法,用于设置给 DialogX:

public static IOSStyle style() {     return new IOSStyle(); }

设置时,使用以下方法即可:

DialogX.globalStyle = IOSStyle.style();

修改消息对话框 layout 布局

 public int layout(boolean light) {     return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark; }

重写 layout 方法用于设置默认的消息对话框的 layout 布局文件,其中 light 参数用于判断是否为亮色模式,你可以在这里查看亮色默认主题布局文件的实现 和 暗色默认主题布局文件的实现,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件。

修改默认对话框启动、关闭动画效果

 public int enterAnimResId() {     return R.anim.anim_dialogx_ios_enter; }  public int exitAnimResId() {     return 0; }

你可以自定义消息对话框的启动、关闭动画效果,当你return 0时则采用默认实现,要自定义动画文件,可以参考:默认对话框启动动画文件和默认对话框关闭动画文件

修改默认按钮排序

在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在横向时,我们一般采用“其他、取消、确定”的逻辑,在纵向时,我们可以调整为“确定、其他、取消”的顺序,要修改按钮排序顺序,你可以重写以下接口。

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK}; }   public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; }

另外,如果需要分隔线,可以使用在按钮间加入“SPLIT”,例如在 iOS 的主题样式中,按钮排序之间加入了分隔线:

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL}; }

若需要调整分隔线的宽度和颜色,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。

 public int splitWidthPx() {     return 1; }   public int splitColorRes(boolean light) {     return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark; }

在 Material 主题风格中,按钮之间并不是分隔线,而是一段空白的间距,你可以使用“SPACE”设置间距,如下:

 public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; }

自定义按钮样式

一些主题要求我们在显示按钮时提供不同的样式,你可以通过两个接口来进行自定义:当对话框按钮处于横向/纵向时,或者当按钮显示数量为1个、2个或3个时,呈现不同的样式。

例如 iOS 主题,横向且只显示一个按钮(OkButton)时,需要左下角和右下角都为圆角的按钮样式,当显示多个按钮时则为仅右下角为圆角的按钮样式,如果需要基于不同的情况进行相应按钮样式调整,你可以重写以下接口:

 public HorizontalButtonRes overrideHorizontalButtonRes() {     return new HorizontalButtonRes() {                  public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             if (visibleButtonCount == 1) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             } else {                 return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;             }         }                   public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;         }                   public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }  public VerticalButtonRes overrideVerticalButtonRes() {     return new VerticalButtonRes() {                  public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }                   public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;         }                   public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }

其中visibleButtonCount参数为当前显示按钮的数量,light参数为当前对话框的亮/暗色模式。

drawable 只接受 xml 配置,可以定义不同状态时的按钮样式效果,如有需要请参考 iOS drawable 样式

overrideHorizontalButtonResoverrideVerticalButtonRes返回值return null时默认不进行样式修改设置。

模糊背景

默认对话框支持模糊化背景,如果需要模糊的背景效果,你可以重写以下接口:

 public BlurBackgroundSetting messageDialogBlurSettings() {     return new BlurBackgroundSetting() {                  public boolean blurBackground() {             return true;         }                   public int blurForwardColorRes(boolean light) {             return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;         }                   public int blurBackgroundRoundRadiusPx() {             return dip2px(15);         }     }; }

如果不需要可以return null进行默认处理。

此接口需要重写一个BlurBackgroundSetting,其中包含三个子接口,分别是blurBackground()用于判断是否开启模糊,blurForwardColorRes(boolean light)用于处理前景色(建议设置一定的透明度保证可以看到背后的模糊效果),blurBackgroundRoundRadiusPx()用于给定模糊效果的圆角半径,单位为像素(Px)。

修改等待/提示框的样式

如果需要修改等待/提示框的样式效果,你可以重写这个接口:

 public WaitTipRes overrideWaitTipRes() {     return new WaitTipRes() {                  public boolean blurBackground() {             return true;         }                   public int overrideBackgroundColorRes(boolean light) {             return 0;         }                   public int overrideTextColorRes(boolean light) {             return light ? R.color.white : R.color.black;         }                  public ProgressViewInterface overrideWaitView(Context context, boolean light) {             return new ProgressView(context).setLightMode(light);         }     }; }

此接口return null时采用默认样式。

WaitTipRes接口中第一个子接口blurBackground()用于判断是否需要模糊背景效果

第二个接口overrideBackgroundColorRes(boolean light)用于重新设置前景色

第三个接口overrideTextColorRes(boolean light)用于设置在亮/暗色时的文字颜色,注意,此颜色也将修改进度动画的颜色。

第四个接口overrideWaitView(Context context, boolean light)比较特殊,当return null时使用默认动画效果,如果需要自定义动画,则需要一个实现了 ProgressViewInterface 接口的 View 组件,具体方法请参考下一章节。

自定义等待提示动画组件 ProgressViewInterface

ProgressViewInterface 的接口定义如下:

public interface ProgressViewInterface {      //停止加载动画     void noLoading();      //切换至完成状态     void success();      //切换至警告状态     void warning();      //切换至错误状态     void error();      //切换至进度(取值 0f-1f)     void progress(float progress);      //切换至加载状态     void loading();      //不同状态切换时,衔接动画完成后执行     ProgressViewInterface whenShowTick(Runnable runnable);      //设置颜色     ProgressViewInterface setColor(int color); }

需要在自定义的 View 中实现这些方法,以实现在等待提示框处于不同状态时调整显示的样式。

需要注意的是,默认等待提示框在加载中到完成/警告/错误等状态时,会有衔接过渡的过程,这个过程导致目标状态可能会延后几十到几百毫秒后才真正切换,因此有一个whenShowTick(Runnable runnable)接口需要实现,在衔接过程结束后执行runnable.run()即可,若无衔接过程,直接在重写后的方法中执行runnable.run()即可。

在亮暗色模式切换后,等待提示框组件应该遵循颜色的变化调整颜色,这个颜色会从setColor(int color)接口中给出,注意此处的参数color是色值,并非资源值,建议直接赋值给画笔 Paint 进行绘制操作。

如需参照 Demo,您可以查看 iOS 样式的 ProgressView 实现:ProgressView.java

自定义底部对话框/菜单样式

底部对话框是 DialogX 中第二个具有丰富功能的组件,您可以实现overrideBottomDialogRes()方法以自定义底部对话框的样式细节。

同样的,当return null时使用默认样式(Material 主题)。

以下基于 iOS 样式的配置进行讲解:

 public BottomDialogRes overrideBottomDialogRes() {     return new BottomDialogRes() {                   public boolean touchSlide() {             return false;         }                   public int overrideDialogLayout(boolean light) {             return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;         }                   public int overrideMenuDividerDrawableRes(boolean light) {             return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;         }                   public int overrideMenuDividerHeight(boolean light) {             return 1;         }                   public int overrideMenuTextColor(boolean light) {             return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;         }                   public float overrideBottomDialogMaxHeight() {             return 0f;         }                   public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {             if (light) {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_light;                 }             } else {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_dark;                 }             }         }                  public int overrideSelectionMenuBackgroundColor(boolean light) {             return 0;         }                  public boolean selectionImageTint(boolean light) {             return true;         }                   public int overrideSelectionImage(boolean light, boolean isSelected) {             return 0;         }     }; }

touchSlide()接口定义了是否支持滑动关闭操作。

overrideDialogLayout(boolean light)接口用于设置底部对话框的布局,如需修改布局样式,请参照 底部对话框默认亮色布局 和 底部对话框默认暗色布局,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件,当return 0时使用默认实现。

overrideMenuDividerDrawableRes(boolean light)用于修改底部菜单分隔线的样式,可以自定义实行 drawable 资源,具体实现可以参考底部菜单分隔线默认实现的 drawable 文件,当return 0时使用默认实现。

overrideMenuDividerHeight(boolean light)用于修改默认分隔线的粗细,单位像素。

overrideMenuTextColor(boolean light)用于修改默认菜单文字的颜色,值采用为 color 的资源 ID。

overrideBottomDialogMaxHeight()用于设置默认情况下,当底部对话框内容大于屏幕可显示高度时,默认启动后显示的高度比例,值为浮点型,例如设置为 0.6f 时,则当内容大于可显示高度时,启动后对话框只从屏幕底部弹出 0.6×屏幕高度的大小,需要再次向上拖拽才能展开全部对话框,此功能需要和 touchSlide() 配合使用。

overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility)接口用于定义菜单条目的布局样式。其中参数layout用于判断亮/暗色模式,参数index为当前菜单项的索引值,count为菜单数量,isContentVisibility用于确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局),例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时,则第一条菜单使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时,最后一个菜单应该采用左下角和右下角都为圆角的样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。此接口return 0时使用默认实现。

overrideSelectionMenuBackgroundColor(boolean light)接口用于定义已选中的菜单默认背景颜色,例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。

selectionImageTint(boolean light)接口用于确定使用此主题时,默认会不会重定义图标的演示,若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。

overrideSelectionImage(boolean light, boolean isSelected)用于设置默认菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。

自定义 PopTip 样式

以下是 iOS 主题样式的 PopTip 接口实现:

 public PopTipSettings popTipSettings() {     return new PopTipSettings() {                  public int layout(boolean light) {             return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;         }                   public ALIGN align() {             return ALIGN.TOP;         }                   public int enterAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_enter;         }                   public int exitAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_exit;         }     }; }

同样的,当return null时使用默认样式。

此接口包含的子接口如下:

layout(boolean light)PopTip 的默认布局样式,请参考具体布局实现:PopTip 默认亮色布局 和 PopTip 默认暗色布局

align()接口用于判断 PopTip 的弹出规则,支持的值如下:

ALIGN {     CENTER:屏幕中央弹出     TOP:屏幕顶端弹出(非安全区)     BOTTOM:屏幕底部弹出(非安全区)     TOP_INSIDE:屏幕顶端安全区内弹出     BOTTOM_INSIDE:屏幕底部安全区内弹出 }

enterAnimResId(boolean light)设置启动动画效果。

exitAnimResId(boolean light)设置关闭动画效果。

其他提示

自定义 DialogX 主题是 DialogX 组件中较难的操作,建议多参考现有提供的主题实现源码:

Material 主题源码

iOS 主题源码

Kongzue 主题源码

MIUI 主题源码

您也可以加入 DialogX 的 讨论群 获得第一时间的技术支持。

自定义 DialogX 主题

DialogX 支持自定义 DialogX 的主题效果,你可以在 https://github.com/kongzue/DialogX 的项目源代码中看到由我已经实现的三套主题,这些主题都支持从外部进行引入使用,但因为是预置的主题,不一定能完全满足开发者的实际需求,在保证整体框架不会臃肿的情况下,DialogX 提供了专用的主题接口用于提供更丰富的主题可定制性。

要制作自定义主题,首先你需要了解 DialogX 主题接口,该接口是一个 jar 包,你可以在这里下载到它:点击下载DialogXInterface.jar,其源代码在:点击查看 DialogXInterface 接口源代码

DialogX 提供了一个接口 DialogXStyle,开发者只需要实现其中的方法并重写相关代码即可完成自定义主题的制作。

此文档适用于 DialogXInterface 版本:1

DialogXStyle解析

以下以 iOS 主题样式为例,介绍重写 DialogXStyle 接口后的范例源代码:

public class IOSStyle implements DialogXStyle {      private IOSStyle() {     }      public static IOSStyle style() {         return new IOSStyle();     }           public int layout(boolean light) {         return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark;     }           public int enterAnimResId() {         return R.anim.anim_dialogx_ios_enter;     }           public int exitAnimResId() {         return 0;     }           public int[] verticalButtonOrder() {         return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL};     }           public int[] horizontalButtonOrder() {         return new int[]{BUTTON_CANCEL, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_OK};     }           public int splitWidthPx() {         return 1;     }           public int splitColorRes(boolean light) {         return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark;     }           public BlurBackgroundSetting messageDialogBlurSettings() {         return new BlurBackgroundSetting() {                          public boolean blurBackground() {                 return true;             }                           public int blurForwardColorRes(boolean light) {                 return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;             }                           public int blurBackgroundRoundRadiusPx() {                 return dip2px(15);             }         };     }      private int dip2px(float dpValue) {         final float scale = Resources.getSystem().getDisplayMetrics().density;         return (int) (dpValue * scale + 0.5f);     }           public HorizontalButtonRes overrideHorizontalButtonRes() {         return new HorizontalButtonRes() {                          public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 if (visibleButtonCount == 1) {                     return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;                 } else {                     return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;                 }             }                           public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;             }                           public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public VerticalButtonRes overrideVerticalButtonRes() {         return new VerticalButtonRes() {                          public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }                           public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             }                           public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public WaitTipRes overrideWaitTipRes() {         return new WaitTipRes() {                          public boolean blurBackground() {                 return true;             }                           public int overrideBackgroundColorRes(boolean light) {                 return 0;             }                           public int overrideTextColorRes(boolean light) {                 return light ? R.color.white : R.color.black;             }                           public ProgressViewInterface overrideWaitView(Context context, boolean light) {                 return new ProgressView(context).setLightMode(light);             }         };     }           public BottomDialogRes overrideBottomDialogRes() {         return new BottomDialogRes() {                           public boolean touchSlide() {                 return false;             }                           public int overrideDialogLayout(boolean light) {                 return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;             }                           public int overrideMenuDividerDrawableRes(boolean light) {                 return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;             }                           public int overrideMenuDividerHeight(boolean light) {                 return 1;             }                           public int overrideMenuTextColor(boolean light) {                 return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;             }                           public float overrideBottomDialogMaxHeight() {                 return 0f;             }                           public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {                 if (light) {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_light;                     }                 } else {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_dark;                     }                 }             }                           public int overrideSelectionMenuBackgroundColor(boolean light) {                 return 0;             }                           public boolean selectionImageTint(boolean light) {                 return true;             }                           public int overrideSelectionImage(boolean light, boolean isSelected) {                 return 0;             }         };     }           public PopTipSettings popTipSettings() {         return new PopTipSettings() {                          public int layout(boolean light) {                 return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;             }                           public ALIGN align() {                 return ALIGN.TOP;             }                           public int enterAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_enter;             }                           public int exitAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_exit;             }         };     } }

大部分接口都可以 return null 来使用默认样式不做修改,以下会逐一介绍相关接口的方法。

首先提供一个实例化方法,用于设置给 DialogX:

public static IOSStyle style() {     return new IOSStyle(); }

设置时,使用以下方法即可:

DialogX.globalStyle = IOSStyle.style();

修改消息对话框 layout 布局

 public int layout(boolean light) {     return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark; }

重写 layout 方法用于设置默认的消息对话框的 layout 布局文件,其中 light 参数用于判断是否为亮色模式,你可以在这里查看亮色默认主题布局文件的实现 和 暗色默认主题布局文件的实现,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件。

修改默认对话框启动、关闭动画效果

 public int enterAnimResId() {     return R.anim.anim_dialogx_ios_enter; }  public int exitAnimResId() {     return 0; }

你可以自定义消息对话框的启动、关闭动画效果,当你return 0时则采用默认实现,要自定义动画文件,可以参考:默认对话框启动动画文件和默认对话框关闭动画文件

修改默认按钮排序

在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在横向时,我们一般采用“其他、取消、确定”的逻辑,在纵向时,我们可以调整为“确定、其他、取消”的顺序,要修改按钮排序顺序,你可以重写以下接口。

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK}; }   public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; }

另外,如果需要分隔线,可以使用在按钮间加入“SPLIT”,例如在 iOS 的主题样式中,按钮排序之间加入了分隔线:

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL}; }

若需要调整分隔线的宽度和颜色,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。

 public int splitWidthPx() {     return 1; }   public int splitColorRes(boolean light) {     return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark; }

在 Material 主题风格中,按钮之间并不是分隔线,而是一段空白的间距,你可以使用“SPACE”设置间距,如下:

 public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; }

自定义按钮样式

一些主题要求我们在显示按钮时提供不同的样式,你可以通过两个接口来进行自定义:当对话框按钮处于横向/纵向时,或者当按钮显示数量为1个、2个或3个时,呈现不同的样式。

例如 iOS 主题,横向且只显示一个按钮(OkButton)时,需要左下角和右下角都为圆角的按钮样式,当显示多个按钮时则为仅右下角为圆角的按钮样式,如果需要基于不同的情况进行相应按钮样式调整,你可以重写以下接口:

 public HorizontalButtonRes overrideHorizontalButtonRes() {     return new HorizontalButtonRes() {                  public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             if (visibleButtonCount == 1) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             } else {                 return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;             }         }                   public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;         }                   public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }  public VerticalButtonRes overrideVerticalButtonRes() {     return new VerticalButtonRes() {                  public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }                   public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;         }                   public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }

其中visibleButtonCount参数为当前显示按钮的数量,light参数为当前对话框的亮/暗色模式。

drawable 只接受 xml 配置,可以定义不同状态时的按钮样式效果,如有需要请参考 iOS drawable 样式

overrideHorizontalButtonResoverrideVerticalButtonRes返回值return null时默认不进行样式修改设置。

模糊背景

默认对话框支持模糊化背景,如果需要模糊的背景效果,你可以重写以下接口:

 public BlurBackgroundSetting messageDialogBlurSettings() {     return new BlurBackgroundSetting() {                  public boolean blurBackground() {             return true;         }                   public int blurForwardColorRes(boolean light) {             return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;         }                   public int blurBackgroundRoundRadiusPx() {             return dip2px(15);         }     }; }

如果不需要可以return null进行默认处理。

此接口需要重写一个BlurBackgroundSetting,其中包含三个子接口,分别是blurBackground()用于判断是否开启模糊,blurForwardColorRes(boolean light)用于处理前景色(建议设置一定的透明度保证可以看到背后的模糊效果),blurBackgroundRoundRadiusPx()用于给定模糊效果的圆角半径,单位为像素(Px)。

修改等待/提示框的样式

如果需要修改等待/提示框的样式效果,你可以重写这个接口:

 public WaitTipRes overrideWaitTipRes() {     return new WaitTipRes() {                  public boolean blurBackground() {             return true;         }                   public int overrideBackgroundColorRes(boolean light) {             return 0;         }                   public int overrideTextColorRes(boolean light) {             return light ? R.color.white : R.color.black;         }                  public ProgressViewInterface overrideWaitView(Context context, boolean light) {             return new ProgressView(context).setLightMode(light);         }     }; }

此接口return null时采用默认样式。

WaitTipRes接口中第一个子接口blurBackground()用于判断是否需要模糊背景效果

第二个接口overrideBackgroundColorRes(boolean light)用于重新设置前景色

第三个接口overrideTextColorRes(boolean light)用于设置在亮/暗色时的文字颜色,注意,此颜色也将修改进度动画的颜色。

第四个接口overrideWaitView(Context context, boolean light)比较特殊,当return null时使用默认动画效果,如果需要自定义动画,则需要一个实现了 ProgressViewInterface 接口的 View 组件,具体方法请参考下一章节。

自定义等待提示动画组件 ProgressViewInterface

ProgressViewInterface 的接口定义如下:

public interface ProgressViewInterface {      //停止加载动画     void noLoading();      //切换至完成状态     void success();      //切换至警告状态     void warning();      //切换至错误状态     void error();      //切换至进度(取值 0f-1f)     void progress(float progress);      //切换至加载状态     void loading();      //不同状态切换时,衔接动画完成后执行     ProgressViewInterface whenShowTick(Runnable runnable);      //设置颜色     ProgressViewInterface setColor(int color); }

需要在自定义的 View 中实现这些方法,以实现在等待提示框处于不同状态时调整显示的样式。

需要注意的是,默认等待提示框在加载中到完成/警告/错误等状态时,会有衔接过渡的过程,这个过程导致目标状态可能会延后几十到几百毫秒后才真正切换,因此有一个whenShowTick(Runnable runnable)接口需要实现,在衔接过程结束后执行runnable.run()即可,若无衔接过程,直接在重写后的方法中执行runnable.run()即可。

在亮暗色模式切换后,等待提示框组件应该遵循颜色的变化调整颜色,这个颜色会从setColor(int color)接口中给出,注意此处的参数color是色值,并非资源值,建议直接赋值给画笔 Paint 进行绘制操作。

如需参照 Demo,您可以查看 iOS 样式的 ProgressView 实现:ProgressView.java

自定义底部对话框/菜单样式

底部对话框是 DialogX 中第二个具有丰富功能的组件,您可以实现overrideBottomDialogRes()方法以自定义底部对话框的样式细节。

同样的,当return null时使用默认样式(Material 主题)。

以下基于 iOS 样式的配置进行讲解:

 public BottomDialogRes overrideBottomDialogRes() {     return new BottomDialogRes() {                   public boolean touchSlide() {             return false;         }                   public int overrideDialogLayout(boolean light) {             return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;         }                   public int overrideMenuDividerDrawableRes(boolean light) {             return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;         }                   public int overrideMenuDividerHeight(boolean light) {             return 1;         }                   public int overrideMenuTextColor(boolean light) {             return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;         }                   public float overrideBottomDialogMaxHeight() {             return 0f;         }                   public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {             if (light) {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_light;                 }             } else {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_dark;                 }             }         }                  public int overrideSelectionMenuBackgroundColor(boolean light) {             return 0;         }                  public boolean selectionImageTint(boolean light) {             return true;         }                   public int overrideSelectionImage(boolean light, boolean isSelected) {             return 0;         }     }; }

touchSlide()接口定义了是否支持滑动关闭操作。

overrideDialogLayout(boolean light)接口用于设置底部对话框的布局,如需修改布局样式,请参照 底部对话框默认亮色布局 和 底部对话框默认暗色布局,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件,当return 0时使用默认实现。

overrideMenuDividerDrawableRes(boolean light)用于修改底部菜单分隔线的样式,可以自定义实行 drawable 资源,具体实现可以参考底部菜单分隔线默认实现的 drawable 文件,当return 0时使用默认实现。

overrideMenuDividerHeight(boolean light)用于修改默认分隔线的粗细,单位像素。

overrideMenuTextColor(boolean light)用于修改默认菜单文字的颜色,值采用为 color 的资源 ID。

overrideBottomDialogMaxHeight()用于设置默认情况下,当底部对话框内容大于屏幕可显示高度时,默认启动后显示的高度比例,值为浮点型,例如设置为 0.6f 时,则当内容大于可显示高度时,启动后对话框只从屏幕底部弹出 0.6×屏幕高度的大小,需要再次向上拖拽才能展开全部对话框,此功能需要和 touchSlide() 配合使用。

overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility)接口用于定义菜单条目的布局样式。其中参数layout用于判断亮/暗色模式,参数index为当前菜单项的索引值,count为菜单数量,isContentVisibility用于确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局),例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时,则第一条菜单使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时,最后一个菜单应该采用左下角和右下角都为圆角的样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。此接口return 0时使用默认实现。

overrideSelectionMenuBackgroundColor(boolean light)接口用于定义已选中的菜单默认背景颜色,例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。

selectionImageTint(boolean light)接口用于确定使用此主题时,默认会不会重定义图标的演示,若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。

overrideSelectionImage(boolean light, boolean isSelected)用于设置默认菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。

自定义 PopTip 样式

以下是 iOS 主题样式的 PopTip 接口实现:

 public PopTipSettings popTipSettings() {     return new PopTipSettings() {                  public int layout(boolean light) {             return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;         }                   public ALIGN align() {             return ALIGN.TOP;         }                   public int enterAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_enter;         }                   public int exitAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_exit;         }     }; }

同样的,当return null时使用默认样式。

此接口包含的子接口如下:

layout(boolean light)PopTip 的默认布局样式,请参考具体布局实现:PopTip 默认亮色布局 和 PopTip 默认暗色布局

align()接口用于判断 PopTip 的弹出规则,支持的值如下:

ALIGN {     CENTER:屏幕中央弹出     TOP:屏幕顶端弹出(非安全区)     BOTTOM:屏幕底部弹出(非安全区)     TOP_INSIDE:屏幕顶端安全区内弹出     BOTTOM_INSIDE:屏幕底部安全区内弹出 }

enterAnimResId(boolean light)设置启动动画效果。

exitAnimResId(boolean light)设置关闭动画效果。

其他提示

自定义 DialogX 主题是 DialogX 组件中较难的操作,建议多参考现有提供的主题实现源码:

Material 主题源码

iOS 主题源码

Kongzue 主题源码

MIUI 主题源码

您也可以加入 DialogX 的 讨论群 获得第一时间的技术支持。

自定义 DialogX 主题

DialogX 支持自定义 DialogX 的主题效果,你可以在 https://github.com/kongzue/DialogX 的项目源代码中看到由我已经实现的三套主题,这些主题都支持从外部进行引入使用,但因为是预置的主题,不一定能完全满足开发者的实际需求,在保证整体框架不会臃肿的情况下,DialogX 提供了专用的主题接口用于提供更丰富的主题可定制性。

要制作自定义主题,首先你需要了解 DialogX 主题接口,该接口是一个 jar 包,你可以在这里下载到它:点击下载DialogXInterface.jar,其源代码在:点击查看 DialogXInterface 接口源代码

DialogX 提供了一个接口 DialogXStyle,开发者只需要实现其中的方法并重写相关代码即可完成自定义主题的制作。

此文档适用于 DialogXInterface 版本:1

DialogXStyle解析

以下以 iOS 主题样式为例,介绍重写 DialogXStyle 接口后的范例源代码:

public class IOSStyle implements DialogXStyle {      private IOSStyle() {     }      public static IOSStyle style() {         return new IOSStyle();     }           public int layout(boolean light) {         return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark;     }           public int enterAnimResId() {         return R.anim.anim_dialogx_ios_enter;     }           public int exitAnimResId() {         return 0;     }           public int[] verticalButtonOrder() {         return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL};     }           public int[] horizontalButtonOrder() {         return new int[]{BUTTON_CANCEL, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_OK};     }           public int splitWidthPx() {         return 1;     }           public int splitColorRes(boolean light) {         return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark;     }           public BlurBackgroundSetting messageDialogBlurSettings() {         return new BlurBackgroundSetting() {                          public boolean blurBackground() {                 return true;             }                           public int blurForwardColorRes(boolean light) {                 return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;             }                           public int blurBackgroundRoundRadiusPx() {                 return dip2px(15);             }         };     }      private int dip2px(float dpValue) {         final float scale = Resources.getSystem().getDisplayMetrics().density;         return (int) (dpValue * scale + 0.5f);     }           public HorizontalButtonRes overrideHorizontalButtonRes() {         return new HorizontalButtonRes() {                          public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 if (visibleButtonCount == 1) {                     return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;                 } else {                     return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;                 }             }                           public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;             }                           public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public VerticalButtonRes overrideVerticalButtonRes() {         return new VerticalButtonRes() {                          public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }                           public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             }                           public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {                 return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;             }         };     }           public WaitTipRes overrideWaitTipRes() {         return new WaitTipRes() {                          public boolean blurBackground() {                 return true;             }                           public int overrideBackgroundColorRes(boolean light) {                 return 0;             }                           public int overrideTextColorRes(boolean light) {                 return light ? R.color.white : R.color.black;             }                           public ProgressViewInterface overrideWaitView(Context context, boolean light) {                 return new ProgressView(context).setLightMode(light);             }         };     }           public BottomDialogRes overrideBottomDialogRes() {         return new BottomDialogRes() {                           public boolean touchSlide() {                 return false;             }                           public int overrideDialogLayout(boolean light) {                 return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;             }                           public int overrideMenuDividerDrawableRes(boolean light) {                 return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;             }                           public int overrideMenuDividerHeight(boolean light) {                 return 1;             }                           public int overrideMenuTextColor(boolean light) {                 return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;             }                           public float overrideBottomDialogMaxHeight() {                 return 0f;             }                           public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {                 if (light) {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_light;                     }                 } else {                     if (index == 0) {                         return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                     } else if (index == count - 1) {                         return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                     } else {                         return R.layout.item_dialogx_ios_bottom_menu_center_dark;                     }                 }             }                           public int overrideSelectionMenuBackgroundColor(boolean light) {                 return 0;             }                           public boolean selectionImageTint(boolean light) {                 return true;             }                           public int overrideSelectionImage(boolean light, boolean isSelected) {                 return 0;             }         };     }           public PopTipSettings popTipSettings() {         return new PopTipSettings() {                          public int layout(boolean light) {                 return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;             }                           public ALIGN align() {                 return ALIGN.TOP;             }                           public int enterAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_enter;             }                           public int exitAnimResId(boolean b) {                 return R.anim.anim_dialogx_ios_top_exit;             }         };     } }

大部分接口都可以 return null 来使用默认样式不做修改,以下会逐一介绍相关接口的方法。

首先提供一个实例化方法,用于设置给 DialogX:

public static IOSStyle style() {     return new IOSStyle(); }

设置时,使用以下方法即可:

DialogX.globalStyle = IOSStyle.style();

修改消息对话框 layout 布局

 public int layout(boolean light) {     return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark; }

重写 layout 方法用于设置默认的消息对话框的 layout 布局文件,其中 light 参数用于判断是否为亮色模式,你可以在这里查看亮色默认主题布局文件的实现 和 暗色默认主题布局文件的实现,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件。

修改默认对话框启动、关闭动画效果

 public int enterAnimResId() {     return R.anim.anim_dialogx_ios_enter; }  public int exitAnimResId() {     return 0; }

你可以自定义消息对话框的启动、关闭动画效果,当你return 0时则采用默认实现,要自定义动画文件,可以参考:默认对话框启动动画文件和默认对话框关闭动画文件

修改默认按钮排序

在特殊情况下,你可以调整对话框的按钮布局顺序,例如,在横向时,我们一般采用“其他、取消、确定”的逻辑,在纵向时,我们可以调整为“确定、其他、取消”的顺序,要修改按钮排序顺序,你可以重写以下接口。

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OTHER, BUTTON_CANCEL, BUTTON_OK}; }   public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OK, BUTTON_OTHER, BUTTON_CANCEL}; }

另外,如果需要分隔线,可以使用在按钮间加入“SPLIT”,例如在 iOS 的主题样式中,按钮排序之间加入了分隔线:

 public int[] verticalButtonOrder() {     return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL}; }

若需要调整分隔线的宽度和颜色,可以重写以下接口进行设置,这些设置也可以使用 return 0不进行设置。

 public int splitWidthPx() {     return 1; }   public int splitColorRes(boolean light) {     return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark; }

在 Material 主题风格中,按钮之间并不是分隔线,而是一段空白的间距,你可以使用“SPACE”设置间距,如下:

 public int[] horizontalButtonOrder() {     return new int[]{BUTTON_OTHER, SPACE, BUTTON_CANCEL, BUTTON_OK}; }

自定义按钮样式

一些主题要求我们在显示按钮时提供不同的样式,你可以通过两个接口来进行自定义:当对话框按钮处于横向/纵向时,或者当按钮显示数量为1个、2个或3个时,呈现不同的样式。

例如 iOS 主题,横向且只显示一个按钮(OkButton)时,需要左下角和右下角都为圆角的按钮样式,当显示多个按钮时则为仅右下角为圆角的按钮样式,如果需要基于不同的情况进行相应按钮样式调整,你可以重写以下接口:

 public HorizontalButtonRes overrideHorizontalButtonRes() {     return new HorizontalButtonRes() {                  public int overrideHorizontalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             if (visibleButtonCount == 1) {                 return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;             } else {                 return light ? R.drawable.button_dialogx_ios_right_light : R.drawable.button_dialogx_ios_right_night;             }         }                   public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;         }                   public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }  public VerticalButtonRes overrideVerticalButtonRes() {     return new VerticalButtonRes() {                  public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }                   public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;         }                   public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {             return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;         }     }; }

其中visibleButtonCount参数为当前显示按钮的数量,light参数为当前对话框的亮/暗色模式。

drawable 只接受 xml 配置,可以定义不同状态时的按钮样式效果,如有需要请参考 iOS drawable 样式

overrideHorizontalButtonResoverrideVerticalButtonRes返回值return null时默认不进行样式修改设置。

模糊背景

默认对话框支持模糊化背景,如果需要模糊的背景效果,你可以重写以下接口:

 public BlurBackgroundSetting messageDialogBlurSettings() {     return new BlurBackgroundSetting() {                  public boolean blurBackground() {             return true;         }                   public int blurForwardColorRes(boolean light) {             return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;         }                   public int blurBackgroundRoundRadiusPx() {             return dip2px(15);         }     }; }

如果不需要可以return null进行默认处理。

此接口需要重写一个BlurBackgroundSetting,其中包含三个子接口,分别是blurBackground()用于判断是否开启模糊,blurForwardColorRes(boolean light)用于处理前景色(建议设置一定的透明度保证可以看到背后的模糊效果),blurBackgroundRoundRadiusPx()用于给定模糊效果的圆角半径,单位为像素(Px)。

修改等待/提示框的样式

如果需要修改等待/提示框的样式效果,你可以重写这个接口:

 public WaitTipRes overrideWaitTipRes() {     return new WaitTipRes() {                  public boolean blurBackground() {             return true;         }                   public int overrideBackgroundColorRes(boolean light) {             return 0;         }                   public int overrideTextColorRes(boolean light) {             return light ? R.color.white : R.color.black;         }                  public ProgressViewInterface overrideWaitView(Context context, boolean light) {             return new ProgressView(context).setLightMode(light);         }     }; }

此接口return null时采用默认样式。

WaitTipRes接口中第一个子接口blurBackground()用于判断是否需要模糊背景效果

第二个接口overrideBackgroundColorRes(boolean light)用于重新设置前景色

第三个接口overrideTextColorRes(boolean light)用于设置在亮/暗色时的文字颜色,注意,此颜色也将修改进度动画的颜色。

第四个接口overrideWaitView(Context context, boolean light)比较特殊,当return null时使用默认动画效果,如果需要自定义动画,则需要一个实现了 ProgressViewInterface 接口的 View 组件,具体方法请参考下一章节。

自定义等待提示动画组件 ProgressViewInterface

ProgressViewInterface 的接口定义如下:

public interface ProgressViewInterface {      //停止加载动画     void noLoading();      //切换至完成状态     void success();      //切换至警告状态     void warning();      //切换至错误状态     void error();      //切换至进度(取值 0f-1f)     void progress(float progress);      //切换至加载状态     void loading();      //不同状态切换时,衔接动画完成后执行     ProgressViewInterface whenShowTick(Runnable runnable);      //设置颜色     ProgressViewInterface setColor(int color); }

需要在自定义的 View 中实现这些方法,以实现在等待提示框处于不同状态时调整显示的样式。

需要注意的是,默认等待提示框在加载中到完成/警告/错误等状态时,会有衔接过渡的过程,这个过程导致目标状态可能会延后几十到几百毫秒后才真正切换,因此有一个whenShowTick(Runnable runnable)接口需要实现,在衔接过程结束后执行runnable.run()即可,若无衔接过程,直接在重写后的方法中执行runnable.run()即可。

在亮暗色模式切换后,等待提示框组件应该遵循颜色的变化调整颜色,这个颜色会从setColor(int color)接口中给出,注意此处的参数color是色值,并非资源值,建议直接赋值给画笔 Paint 进行绘制操作。

如需参照 Demo,您可以查看 iOS 样式的 ProgressView 实现:ProgressView.java

自定义底部对话框/菜单样式

底部对话框是 DialogX 中第二个具有丰富功能的组件,您可以实现overrideBottomDialogRes()方法以自定义底部对话框的样式细节。

同样的,当return null时使用默认样式(Material 主题)。

以下基于 iOS 样式的配置进行讲解:

 public BottomDialogRes overrideBottomDialogRes() {     return new BottomDialogRes() {                   public boolean touchSlide() {             return false;         }                   public int overrideDialogLayout(boolean light) {             return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;         }                   public int overrideMenuDividerDrawableRes(boolean light) {             return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;         }                   public int overrideMenuDividerHeight(boolean light) {             return 1;         }                   public int overrideMenuTextColor(boolean light) {             return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;         }                   public float overrideBottomDialogMaxHeight() {             return 0f;         }                   public int overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) {             if (light) {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_light : R.layout.item_dialogx_ios_bottom_menu_top_light;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_light;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_light;                 }             } else {                 if (index == 0) {                     return isContentVisibility ? R.layout.item_dialogx_ios_bottom_menu_center_dark : R.layout.item_dialogx_ios_bottom_menu_top_dark;                 } else if (index == count - 1) {                     return R.layout.item_dialogx_ios_bottom_menu_bottom_dark;                 } else {                     return R.layout.item_dialogx_ios_bottom_menu_center_dark;                 }             }         }                  public int overrideSelectionMenuBackgroundColor(boolean light) {             return 0;         }                  public boolean selectionImageTint(boolean light) {             return true;         }                   public int overrideSelectionImage(boolean light, boolean isSelected) {             return 0;         }     }; }

touchSlide()接口定义了是否支持滑动关闭操作。

overrideDialogLayout(boolean light)接口用于设置底部对话框的布局,如需修改布局样式,请参照 底部对话框默认亮色布局 和 底部对话框默认暗色布局,请参照 Demo 布局的格式进行布局设计,不建议修改或去掉布局中的含有 id 的组件,当return 0时使用默认实现。

overrideMenuDividerDrawableRes(boolean light)用于修改底部菜单分隔线的样式,可以自定义实行 drawable 资源,具体实现可以参考底部菜单分隔线默认实现的 drawable 文件,当return 0时使用默认实现。

overrideMenuDividerHeight(boolean light)用于修改默认分隔线的粗细,单位像素。

overrideMenuTextColor(boolean light)用于修改默认菜单文字的颜色,值采用为 color 的资源 ID。

overrideBottomDialogMaxHeight()用于设置默认情况下,当底部对话框内容大于屏幕可显示高度时,默认启动后显示的高度比例,值为浮点型,例如设置为 0.6f 时,则当内容大于可显示高度时,启动后对话框只从屏幕底部弹出 0.6×屏幕高度的大小,需要再次向上拖拽才能展开全部对话框,此功能需要和 touchSlide() 配合使用。

overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility)接口用于定义菜单条目的布局样式。其中参数layout用于判断亮/暗色模式,参数index为当前菜单项的索引值,count为菜单数量,isContentVisibility用于确认当菜单显示时,是否还有其他内容显示(例如对话框标题、正文或自定义布局),例如当使用 iOS 样式时,第一条菜单默认采用左上角和右上角都为圆角的样式,当显示菜单标题、正文或自定义布局时,则第一条菜单使用无圆角样式。当index == count - 1时为最后一个菜单,使用 iOS 样式时,最后一个菜单应该采用左下角和右下角都为圆角的样式。菜单的布局请参照 底部菜单亮色样式参考布局 和 底部菜单暗色样式参考布局 。此接口return 0时使用默认实现。

overrideSelectionMenuBackgroundColor(boolean light)接口用于定义已选中的菜单默认背景颜色,例如在使用 MIUI 主题样式且开启了单选模式时,默认打开菜单后会选中上次已选择的条目,此接口用预设定已选中菜单的背景颜色。

selectionImageTint(boolean light)接口用于确定使用此主题时,默认会不会重定义图标的演示,若开启,那么所有菜单图标会根据主题的亮/暗色的文字颜色重新覆盖颜色,若关闭,则使用图标原本的颜色。

overrideSelectionImage(boolean light, boolean isSelected)用于设置默认菜单已选择/未选择时的图标资源,可使用 mipmap 图像或者 drawable 资源。

自定义 PopTip 样式

以下是 iOS 主题样式的 PopTip 接口实现:

 public PopTipSettings popTipSettings() {     return new PopTipSettings() {                  public int layout(boolean light) {             return light?R.layout.layout_dialogx_poptip_ios :R.layout.layout_dialogx_poptip_ios_dark;         }                   public ALIGN align() {             return ALIGN.TOP;         }                   public int enterAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_enter;         }                   public int exitAnimResId(boolean light) {             return R.anim.anim_dialogx_ios_top_exit;         }     }; }

同样的,当return null时使用默认样式。

此接口包含的子接口如下:

layout(boolean light)PopTip 的默认布局样式,请参考具体布局实现:PopTip 默认亮色布局 和 PopTip 默认暗色布局

align()接口用于判断 PopTip 的弹出规则,支持的值如下:

ALIGN {     CENTER:屏幕中央弹出     TOP:屏幕顶端弹出(非安全区)     BOTTOM:屏幕底部弹出(非安全区)     TOP_INSIDE:屏幕顶端安全区内弹出     BOTTOM_INSIDE:屏幕底部安全区内弹出 }

enterAnimResId(boolean light)设置启动动画效果。

exitAnimResId(boolean light)设置关闭动画效果。

其他提示

自定义 DialogX 主题是 DialogX 组件中较难的操作,建议多参考现有提供的主题实现源码:

Material 主题源码

iOS 主题源码

Kongzue 主题源码

MIUI 主题源码

您也可以加入 DialogX 的 讨论群 获得第一时间的技术支持。

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 自定义 DialogX 主题求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们