Skip to content

CustomThemeStyle_en

Kongzue edited this page Nov 16, 2023 · 1 revision

🌐 View 简体中文文档 | 繁體中文文檔

🎽 Custom DialogX Themes

DialogX supports customizing DialogX theme effects. You can see three sets of themes already implemented by me in the project source code at https://github.com/kongzue/DialogX. These themes are optional and support being independently imported for use from outside. However, as they are preset themes, they may not fully meet developers' actual needs. To ensure the overall framework is not bloated, DialogX provides a dedicated theme interface for richer theme customization.

To create a custom theme, you first need to understand the DialogX theme interface. This interface is a jar package, which you can download here: Download DialogXInterface.jar. Its source code can be found here: View DialogXInterface Interface Source Code

Or manually import:

MavenCentral Source:

Latest Version: DialogXInterface Release
implementation 'com.kongzue.dialogx.style:DialogXInterface:5'

DialogX provides an interface, DialogXStyle, where developers only need to implement its methods and override the relevant code to complete the creation of a custom theme.

This document is applicable to DialogXInterface Version: 5

DialogXStyle Interface Analysis

The following is an example source code explanation of overriding the DialogXStyle interface, using the iOS theme style as an example:

public class IOSStyle extends DialogXStyle {
    
    public static IOSStyle style() {
        return new IOSStyle();
    }
    
    @Override
    public int layout(boolean light) {
        return light ? R.layout.layout_dialogx_ios : R.layout.layout_dialogx_ios_dark;
    }
    
    @Override
    public int enterAnimResId() {
        return R.anim.anim_dialogx_ios_enter;
    }
    
    @Override
    public int exitAnimResId() {
        return 0;
    }
    
    @Override
    public int[] verticalButtonOrder() {
        return new int[]{BUTTON_OK, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_CANCEL};
    }
    
    @Override
    public int[] horizontalButtonOrder() {
        return new int[]{BUTTON_CANCEL, SPLIT, BUTTON_OTHER, SPLIT, BUTTON_OK};
    }
    
    @Override
    public int splitWidthPx() {
        return 1;
    }
    
    @Override
    public int splitColorRes(boolean light) {
        return light ? R.color.dialogxIOSSplitLight : R.color.dialogxIOSSplitDark;
    }
    
    @Override
    public BlurBackgroundSetting messageDialogBlurSettings() {
        return new BlurBackgroundSetting() {
            @Override
            public boolean blurBackground() {
                return true;
            }
            
            @Override
            public int blurForwardColorRes(boolean light) {
                return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;
            }
            
            @Override
            public int blurBackgroundRoundRadiusPx() {
                return dip2px(15);
            }
        };
    }
    
    private int dip2px(float dpValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
    
    @Override
    public HorizontalButtonRes overrideHorizontalButtonRes() {
        return new HorizontalButtonRes() {
            @Override
            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;
                }
            }
            
            @Override
            public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {
                return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;
            }
            
            @Override
            public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {
                return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
            }
        };
    }
    
    @Override
    public VerticalButtonRes overrideVerticalButtonRes() {
        return new VerticalButtonRes() {
            @Override
            public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {
                return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
            }
            
            @Override
            public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {
                return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;
            }
            
            @Override
            public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {
                return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
            }
        };
    }
    
    @Override
    public WaitTipRes overrideWaitTipRes() {
        return new WaitTipRes() {
            @Override
            public int overrideWaitLayout(boolean light) {
                return 0;
            }
            
            @Override
            public int overrideRadiusPx() {
                return -1;
            }
            
            @Override
            public boolean blurBackground() {
                return true;
            }
            
            @Override
            public int overrideBackgroundColorRes(boolean light) {
                return light ? R.color.dialogxIOSWaitBkgDark : R.color.dialogxIOSWaitBkgLight;
            }
            
            @Override
            public int overrideTextColorRes(boolean light) {
                return 0;
            }
            
            @Override
            public ProgressViewInterface overrideWaitView(Context context, boolean light) {
                return new ProgressView(context).setLightMode(light);
            }
        };
    }
    
    @Override
    public BottomDialogRes overrideBottomDialogRes() {
        return new BottomDialogRes() {
            
            @Override
            public boolean touchSlide() {
                return false;
            }
            
            @Override
            public int overrideDialogLayout(boolean light) {
                //return light ? R.layout.layout_dialogx_bottom_material : R.layout.layout_dialogx_bottom_material_dark;
                return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;
            }
            
            @Override
            public int overrideMenuDividerDrawableRes(boolean light) {
                return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;
            }
            
            @Override
            public int overrideMenuDividerHeight(boolean light) {
                return 1;
            }
            
            @Override
            public int overrideMenuTextColor(boolean light) {
                return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;
            }
            
            @Override
            public float overrideBottomDialogMaxHeight() {
                return 0f;
            }
            
            @Override
            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;
                    }
                }
            }
            
            @Override
            public int overrideSelectionMenuBackgroundColor(boolean light) {
                return 0;
            }
            
            @Override
            public boolean selectionImageTint(boolean light) {
                return true;
            }
            
            @Override
            public int overrideSelectionImage(boolean light, boolean isSelected) {
                return 0;
            }
            
            @Override
            public int overrideMultiSelectionImage(boolean light, boolean isSelected) {
                return 0;
            }
        };
    }
    
    @Override
    public PopTipSettings popTipSettings() {
        return new PopTipSettings() {
            @Override
            public int layout(boolean light) {
                return light ? R.layout.layout_dialogx_poptip_ios : R.layout.layout_dialogx_poptip_ios_dark;
            }
            
            @Override
            public ALIGN align() {
                return ALIGN.TOP;
            }
            
            @Override
            public int enterAnimResId(boolean light) {
                return R.anim.anim_dialogx_ios_top_enter;
            }
            
            @Override
            public int exitAnimResId(boolean light) {
                return R.anim.anim_dialogx_ios_top_exit;
            }
            
            @Override
            public boolean tintIcon() {
                return false;
            }
        };
    }
    
    @Override
    public PopNotificationSettings popNotificationSettings() {
        return new PopNotificationSettings() {
            @Override
            public int layout(boolean light) {
                return light ? R.layout.layout_dialogx_popnotification_ios : R.layout.layout_dialogx_popnotification_ios_dark;
            }
            
            @Override
            public PopNotificationSettings.ALIGN align() {
                return ALIGN.TOP;
            }
            
            @Override
            public int enterAnimResId(boolean light) {
                return com.kongzue.dialogx.R.anim.anim_dialogx_notification_enter;
            }
            
            @Override
            public int exitAnimResId(boolean light) {
                return com.kongzue.dialogx.R.anim.anim_dialogx_notification_exit;
            }
            
            @Override
            public boolean tintIcon() {
                return false;
            }
            
            @Override
            public BlurBackgroundSetting blurBackgroundSettings() {
                return new BlurBackgroundSetting() {
                    @Override
                    public boolean blurBackground() {
                        return true;
                    }
                    
                    @Override
                    public int blurForwardColorRes(boolean light) {
                        return light ? R.color.dialogxIOSNotificationBkgLight : R.color.dialogxIOSNotificationBkgDark;
                    }
                    
                    @Override
                    public int blurBackgroundRoundRadiusPx() {
                        return dip2px(18);
                    }
                };
            }
        };
    }
    
    @Override
    public PopMenuSettings popMenuSettings() {
        return new PopMenuSettings() {
            @Override
            public int layout(boolean light) {
                return light ? R.layout.layout_dialogx_popmenu_ios : R.layout.layout_dialogx_popmenu_ios_dark;
            }
            
            @Override
            public BlurBackgroundSetting blurBackgroundSettings() {
                return new BlurBackgroundSetting() {
                    @Override
                    public boolean blurBackground() {
                        return true;
                    }
                    
                    @Override
                    public int blurForwardColorRes(boolean light) {
                        return light ? R.color.dialogxIOSBkgLight : R.color.dialogxIOSBkgDark;
                    }
                    
                    @Override
                    public int blurBackgroundRoundRadiusPx() {
                        return dip2px(15);
                    }
                };
            }
            
            @Override
            public int backgroundMaskColorRes() {
                return R.color.black20;
            }
            
            @Override
            public int overrideMenuDividerDrawableRes(boolean light) {
                return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;
            }
            
            @Override
            public int overrideMenuDividerHeight(boolean light) {
                return 1;
            }
            
            @Override
            public int overrideMenuTextColor(boolean light) {
                return 0;
            }
            
            @Override
            public int overrideMenuItemLayoutRes(boolean light) {
                return light ? R.layout.item_dialogx_ios_popmenu_light : R.layout.item_dialogx_ios_popmenu_dark;
            }
            
            @Override
            public int overrideMenuItemBackgroundRes(boolean light, int index, int count, boolean isContentVisibility) {
                if (light) {
                    if (index == 0) {
                        return R.drawable.button_dialogx_ios_top_light;
                    } else if (index == count - 1) {
                        return R.drawable.button_dialogx_ios_bottom_light;
                    } else {
                        return R.drawable.button_dialogx_ios_center_light;
                    }
                } else {
                    if (index == 0) {
                        return R.drawable.button_dialogx_ios_top_night;
                    } else if (index == count - 1) {
                        return R.drawable.button_dialogx_ios_bottom_night;
                    } else {
                        return R.drawable.button_dialogx_ios_center_night;
                    }
                }
            }
            
            @Override
            public int overrideSelectionMenuBackgroundColor(boolean light) {
                return 0;
            }
            
            @Override
            public boolean selectionImageTint(boolean light) {
                return false;
            }
            
            @Override
            public int paddingVertical() {
                return 0;
            }
        };
    }
}

Most interfaces can return null to use the default style without modification. The following will introduce each interface method in detail.

To set, use the following method:

DialogX.globalStyle = new IOSStyle();

Modify the Message Dialog Layout

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

Override the layout method to set the default message dialog layout file. The light parameter is used to determine whether it's in light mode. You can view the implementation of the light default theme layout file and dark default theme layout file here. Please refer to the Demo layout format for your layout design. It's not recommended to modify or remove components with id in the layout.

Modify Default Dialog Start and Close Animation Effects

@Override
public int enterAnimResId() {
    return R.anim.anim_dialogx_ios_enter;
}

@Override
public int exitAnimResId() {
    return 0;
}

You can customize the start and close animation effects of the message dialog. When you return 0, the default implementation is used. To customize animation files, refer to: Default Dialog Start Animation File and Default Dialog Close Animation File

Modify Default Button Order

In special cases, you can adjust the button layout order of the dialog. For example, in horizontal orientation, we usually use the "Other, Cancel, OK" logic, and in vertical orientation, we can adjust to the "OK, Other, Cancel" order. To modify the button order, override the following interface.

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

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

Additionally, if you need separators, you can insert “SPLIT” between buttons. For example, in the iOS theme style, separators have been added between button order:

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

To adjust the width and color of the separator, override the following interfaces to set them. These settings can also use return 0 to not set.

@Override
public int splitWidthPx() {
    return 1;
}

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

In the Material theme style, the space between buttons is not a separator but a gap. You can set the gap using “SPACE,” as shown below:

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

Customize Button Styles

Some themes require different styles when displaying buttons. You can customize through two interfaces: when the dialog buttons are horizontal/vertical or when the number of buttons displayed is one, two, or three, presenting different styles.

For example, in the iOS theme, when horizontal and only one button (OkButton) is displayed, a button style with rounded corners on both the bottom left and bottom right is needed. When multiple buttons are displayed, a style with only the bottom right corner rounded is required. If you need to adjust the button style based on different situations, you can override the following interface:

@Override
public HorizontalButtonRes overrideHorizontalButtonRes() {
    return new HorizontalButtonRes() {
        @Override
        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;
            }
        }
        
        @Override
        public int overrideHorizontalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {
            return light ? R.drawable.button_dialogx_ios_left_light : R.drawable.button_dialogx_ios_left_night;
        }
        
        @Override
        public int overrideHorizontalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {
            return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
        }
    };
}
@Override
public VerticalButtonRes overrideVerticalButtonRes() {
    return new VerticalButtonRes() {
        @Override
        public int overrideVerticalOkButtonBackgroundRes(int visibleButtonCount, boolean light) {
            return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
        }
        
        @Override
        public int overrideVerticalCancelButtonBackgroundRes(int visibleButtonCount, boolean light) {
            return light ? R.drawable.button_dialogx_ios_bottom_light : R.drawable.button_dialogx_ios_bottom_night;
        }
        
        @Override
        public int overrideVerticalOtherButtonBackgroundRes(int visibleButtonCount, boolean light) {
            return light ? R.drawable.button_dialogx_ios_center_light : R.drawable.button_dialogx_ios_center_night;
        }
    };
}

The visibleButtonCount parameter indicates the current number of displayed buttons, and the light parameter represents the current dialog's light/dark mode.

Drawable only accepts XML configuration and can define different states of button style effects. If needed, please refer to iOS drawable styles.

The return values of overrideHorizontalButtonRes and overrideVerticalButtonRes are return null by default, indicating no style modification settings.

Blurred Background

The default dialog supports a blurred background. If you need a blurred background effect, you can override the following interface:

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

If not needed, you can return null for default processing.

This interface requires overriding a BlurBackgroundSetting, which contains three sub-interfaces. They are blurBackground() for determining whether to enable blurring, blurForwardColorRes(boolean light) for handling the foreground color (it is recommended to set a certain level of transparency to ensure the blurred background effect is visible), and blurBackgroundRoundRadiusPx() for specifying the round radius of the blur effect in pixels (Px).

Modify the Style of Waiting/Tip Dialogs

If you need to modify the style of waiting/tip dialogs, you can override this interface:

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

This interface, when return null, uses the default style.

Within the WaitTipRes interface, the first sub-interface blurBackground() is used to determine if a blurred background effect is needed.

The second interface, overrideBackgroundColorRes(boolean light), is for resetting the foreground color.

The third interface, overrideTextColorRes(boolean light), is for setting the text color in light/dark modes. Note that this color will also modify the progress animation color.

The fourth interface, overrideWaitView(Context context, boolean light), is special. When return null, the default animation effect is used. If custom animation is required, a View component implementing the ProgressViewInterface interface is needed. For specific methods, please refer to the next section.

Custom Waiting Tip Animation Component ProgressViewInterface

The interface definition of ProgressViewInterface is as follows:

public interface ProgressViewInterface {
    
    // Stop loading animation
    void noLoading();
    
    // Switch to success state
    void success();
    
    // Switch to warning state
    void warning();
    
    // Switch to error state
    void error();
    
    // Switch to progress (value 0f-1f)
    void progress(float progress);
    
    // Switch to loading state
    void loading();
    
    // After the animation connecting different states is completed
    ProgressViewInterface whenShowTick(Runnable runnable);
    
    // Set color
    ProgressViewInterface setColor(int color);
}

These methods need to be implemented in a custom View to adjust the display style when the waiting tip dialog is in different states.

It is important to note that the default waiting tip dialog will have a transitional process from loading to completion/warning/error states. This process may cause a delay of a few dozen to a few hundred milliseconds before the target state is truly switched. Therefore, there is a whenShowTick(Runnable runnable) interface that needs to be implemented. Execute runnable.run() after the transitional process ends. If there is no transitional process, directly execute runnable.run() in the overridden method.

After switching between light and dark modes, the waiting tip dialog component should follow the color change. This color will be provided through the setColor(int color) interface. Note that the parameter color here is a color value, not a resource value. It is recommended to directly assign it to the Paint for drawing operations.

To refer to the Demo, you can view the iOS style ProgressView implementation: ProgressView.java

Customize Bottom Dialog/Menu Styles

The bottom dialog is the second component in DialogX with rich functionality. You can implement the overrideBottomDialogRes() method to customize the style details of the bottom dialog.

Similarly, when return null, the default style (Material theme) is used.

The following is an explanation based on the iOS style configuration:

@Override
public BottomDialogRes overrideBottomDialogRes() {
    return new BottomDialogRes() {
    
        @Override
        public boolean touchSlide() {
            return false;
        }
    
        @Override
        public int overrideDialogLayout(boolean light) {
            return light ? R.layout.layout_dialogx_bottom_ios : R.layout.layout_dialogx_bottom_ios_dark;
        }
    
        @Override
        public int overrideMenuDividerDrawableRes(boolean light) {
            return light ? R.drawable.rect_dialogx_ios_menu_split_divider : R.drawable.rect_dialogx_ios_menu_split_divider_night;
        }
    
        @Override
        public int overrideMenuDividerHeight(boolean light) {
            return 1;
        }
    
        @Override
        public int overrideMenuTextColor(boolean light) {
            return light ? R.color.dialogxIOSBlue : R.color.dialogxIOSBlueDark;
        }
    
        @Override
        public float overrideBottomDialogMaxHeight() {
            return 0f;
        }
    
        @Override
        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;
                }
            }
        }
        @Override
        public int overrideSelectionMenuBackgroundColor(boolean light) {
            return 0;
        }
        @Override
        public boolean selectionImageTint(boolean light) {
            return true;
        }
    
        @Override
        public int overrideSelectionImage(boolean light, boolean isSelected) {
            return 0;
        }
    };
}

The touchSlide() interface defines whether to support slide-to-close operations.

The overrideDialogLayout(boolean light) interface is used to set the layout for the bottom dialog. If you need to modify the layout style, please refer to the Bottom Dialog Default Light Layout and Bottom Dialog Default Dark Layout. Please follow the Demo layout format for your layout design. It is not recommended to modify or remove components with id in the layout. When return 0, the default implementation is used.

The overrideMenuDividerDrawableRes(boolean light) is used to modify the style of the bottom menu divider. You can customize drawable resources. The specific implementation can be referred to in the Bottom Menu Divider Default Drawable File. When return 0, the default implementation is used.

The overrideMenuDividerHeight(boolean light) is used to modify the thickness of the default divider, measured in pixels.

The overrideMenuTextColor(boolean light) is used to modify the default menu text color, with the value being the resource ID of the color.

The overrideBottomDialogMaxHeight() is used to set the default height ratio when the bottom dialog's content exceeds the displayable height of the screen. The value is a float, for example, setting it to 0.6f means that when the content exceeds the displayable height, the dialog will only pop up 0.6× the height of the screen upon launch. Further dragging up is needed to expand the entire dialog. This feature needs to be used in conjunction with touchSlide().

The overrideMenuItemLayout(boolean light, int index, int count, boolean isContentVisibility) interface is used to define the layout style of menu items. The layout parameter is for determining light/dark mode, index for the current menu item index, count for the number of menu items, and isContentVisibility for confirming whether other content is displayed (such as dialog title, body, or custom layout) when the menu is shown. For example, in iOS style, the first menu item by default uses a style with rounded corners on both the top left and right corners. When displaying menu titles, body, or custom layout, the first menu item uses a style without rounded corners. When index == count - 1, it's the last menu item, which in iOS style, should use a style with rounded corners on both the bottom left and right corners. Refer to the Bottom Menu Light Style Reference Layout and Bottom Menu Dark Style Reference Layout for the menu layout. When return 0 in this interface, the default implementation is used.

The overrideSelectionMenuBackgroundColor(boolean light) interface is used to define the default background color for selected menu items. For example, in the MIUI theme style with single-choice mode enabled, the menu will default to selecting the previously chosen item upon opening. This interface presets the background color for selected menu items.

The selectionImageTint(boolean light) interface determines whether using this theme will redefine the color of icons by default. If enabled, all menu icons will be overlaid with the light/dark text color of the theme. If disabled, the original color of the icons is used.

The overrideSelectionImage(boolean light, boolean isSelected) is used to set the default menu selected/unselected icon resources, which can be mipmap images or drawable resources.

Customize PopTip Styles

The following are the PopTip interface implementations for the iOS theme style:

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

Similarly, when return null, the default style is used.

This interface contains the following sub-interfaces:

layout(boolean light) for the default layout style of PopTip. Please refer to the specific layout implementations: PopTip Default Light Layout and PopTip Default Dark Layout.

align() interface determines the pop-up rule of PopTip, supporting the following values:

ALIGN {
    CENTER: Pop up in the center of the screen
    TOP: Pop up at the top of the screen (non-safe area)
    BOTTOM: Pop up at the bottom of the screen (non-safe area)
    TOP_INSIDE: Pop up inside the top safe area
    BOTTOM_INSIDE: Pop up inside the bottom safe area
}

enterAnimResId(boolean light) sets the start animation effect.

exitAnimResId(boolean light) sets the close animation effect.

Additional Tips

Customizing DialogX themes is one of the more challenging operations within the DialogX components. It is recommended to refer to the source code of existing themes:

Material Theme Source Code

iOS Theme Source Code

Kongzue Theme Source Code

MIUI Theme Source Code

You can also join the DialogX discussion group to get technical support in a timely manner.

Partial Settings for Custom Themes

Overriding Global Settings of the Theme

You can adjust some global settings defined by the theme by overriding certain settings. For example, the tinting of icons in PopTip is defined by the theme setting. Each adjustment can also be made through methods, but to globally set the icon tinting function of PopTip, you can directly override the interface setting to achieve this, as shown in the reference code:

// When setting the theme:
DialogX.globalStyle = new MaterialStyle(){
    @Override
    public PopTipSettings popTipSettings() {
        return new PopTipSettings() {
            @Override
            public boolean tintIcon() {
                return false;       // Disable icon tinting
            }
        };
    }
};

This will complete the adjustment.

Overriding Theme Layouts

To customize the layout of certain dialogs, you can directly create an xml layout with the same name as the theme resource in your app's res/layout and copy its content for modification.

For example, for the iOS theme's PopTip layout, after setting the theme, you can write a layout file named layout_dialogx_poptip_ios.xml and a dark theme file layout_dialogx_poptip_ios_dark.xml. For content, refer to layout_dialogx_poptip_ios.xml and layout_dialogx_poptip_ios_dark.xml to copy and modify their contents. After compilation, it will automatically override the theme's layout file, resulting in the desired outcome.

Clone this wiki locally