Skip to content

BottomDialog&BottomMenu_tc

Kongzue edited this page Sep 14, 2024 · 7 revisions

🌐 View English Document | 简体中文文档

🧾底部對話框 BottomDialog 和底部菜單 BottomMenu

底部對話框 BottomDialog 和底部菜單 BottomMenu

底部對話框 BottomDialog 提供從底部彈出顯示的對話框樣式,可設置標題、提示文本和自定義佈局,使用 Material 主題時還會提供向下滑動關閉和向上滑動展開的功能。

底部菜單 BottomMenu 則是底部對話框 BottomDialog 的擴展組件,在底部對話框的基礎上額外提供了菜單功能,菜單可設置菜單內容/菜單圖標/單選功能,在不同的主題下還可以提供“取消”關閉按鈕(註:因 Material 直接可以下滑關閉因此 Material 主題不提供額外的“取消”關閉按鈕)

顯示一個簡單底部對話框

使用以下代碼顯示一個對話框:

BottomDialog.show("標題", "這裏是對話框內容。");

請注意,Material 主題的底部對話框默認是通過下滑操作來進行關閉的,不同主題處理邏輯可能不同,例如在iOS主題中,則是提供“取消”按鈕來進行關閉。

在其他主題中你可以通過.setCancelButton(...)方法來設置取消按鈕的文字和事件

BottomDialog.show("標題", "這裏是對話框內容。")
    .setCancelButton("取消", new OnDialogButtonClickListener<BottomDialog>() {
        @Override
        public boolean onClick(BottomDialog baseDialog, View v) {
            //...
            return false;
        }
    });

顯示一個底部菜單

底部菜單是基於底部對話框擴展實現的一個快速選擇菜單對話框,你可以通過傳入List<String>String[] menuListList<CharSequence> menuList或者CharSequence[] menuList來快速顯示一個菜單:

BottomMenu.show("新標籤頁中打開", "稍後閱讀", "複製鏈接網址")
        .setMessage("這裏是標題")
        .setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
            @Override
            public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
                toast(text);
                return false;
            }
        });

上述代碼中,OnMenuItemClickListener回調是菜單的點擊事件回調,其中 text 為菜單文本,index 為菜單的索引。

單選菜單也可以輕鬆設置菜單圖標,我們盡可能的省去編寫 Adapter 適配器的麻煩,要設置菜單圖標可以直接指定,對於某一項菜單不需要圖標的可以直接傳入 0:

.setIconResIds(R.mipmap.img_dialogx_demo_add, R.mipmap.img_dialogx_demo_edit...)

圖標是否根據亮暗色主題進行染色處理可通過以下方法調整:

.setAutoTintIconInLightOrDarkMode(boolean)

(可選)使用回調方式設置圖標:

或者使用實現一個回調setOnIconChangeCallBack(...)的方式:

BottomMenu.show("添加", "查看", "編輯", "刪除")
        .setOnIconChangeCallBack(new OnIconChangeCallBack(true) {
            @Override
            public int getIcon(BottomMenu bottomMenu, int index, String menuText) {
                switch (menuText) {
                    case "添加":
                        return R.mipmap.img_dialogx_demo_add;
                    case "查看":
                        return R.mipmap.img_dialogx_demo_view;
                    case "編輯":
                        return R.mipmap.img_dialogx_demo_edit;
                    case "刪除":
                        return R.mipmap.img_dialogx_demo_delete;
                }
                return 0;
            }
        })
        .setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
            @Override
            public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
                toast(text);
                return false;
            }
        });

上述代碼中,OnIconChangeCallBack 存在一個參數(boolean)autoTintIconInLightOrDarkMode 用於識別是否需要將圖標進行相應的染色,以適應亮色和暗色模式的變化,設置為 true 時代表,處於亮暗色模式下,菜單圖標會自動根據文字顏色進行染色,當使用非多彩圖標的線性/面性圖標時,建議開啟此功能。

另外,底部菜單也提供單選功能,你可以設置一個已選擇的 index 索引,那麼在 BottomMenu 顯示時,相應位置的菜單會被選中,不同主題的選擇效果略有不同。

private int selectMenuIndex;

BottomMenu.show("拒絕", "詢問", "始終允許", "僅在使用中允許")
        .setMessage("這裏是權限確認的文本說明,這是一個單選的演示菜單")
        .setTitle("獲得權限標題")
        .setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
            @Override
            public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
            	//記錄已選擇值
                selectMenuIndex = index;
                toast(text);
                return false;
            }
        })
        .setSelection(selectMenuIndex);		//指定已選擇的位置

同樣的,當您在OnMenuItemClickListener中返回值設置return true時,點擊菜單後不會自動關閉。

單選菜單

如果你需要選中菜單但不立即關閉對話框,而是保持選中狀態,等待用戶確認或取消操作再進行處理,那麼此時可以通過單選狀態回調 OnMenuItemSelectListener#onOneItemSelect 來實現對菜單實時選中狀態的監聽:

.setOnMenuItemClickListener(new OnMenuItemSelectListener<BottomMenu>() {
    @Override
    public void onOneItemSelect(BottomMenu dialog, CharSequence text, int index, boolean select) {
        //index 即用戶點擊的菜單項索引
    }
})
//開啟單選模式,以下方式二選一:
.setSelection(selectMenuIndex)	//開啟單選模式,並指定一個已選項
.setSingleSelection()		//開啟單選模式,但不指定已選項

此時,用戶點擊菜單後對話框不會立即消失,而是等待用戶可能再次修改選擇。

要在對話框關閉時,或之後再獲取用戶已選擇的菜單項,可以通過 .getSelectionIndex() 方法獲取用戶當前選中的菜單索引,另可通過 .getMenuList() 獲取所有的菜單文本。

多選菜單

要開啟多選菜單,你可以通過以下代碼實現,使用多選菜單狀態回調實時獲取選中項:

.setOnMenuItemClickListener(new OnMenuItemSelectListener<BottomMenu>() {
    @Override
    public void onMultiItemSelect(BottomMenu dialog, CharSequence[] text, int[] index) {
        //index[] 數組是所有已選中菜單項的索引集合,text[] 是所有已選中菜單項文本的集合
    }
})
//開啟多選模式,以下方式二選一:
.setSelection(selectMenuIndexArray);	//開啟多選模式,並指定一個已選中的索引數組
.setMultiSelection()	//開啟多選模式,但不指定已選項

此時,用戶點擊菜單後對話框不會立即消失,而是等待用戶可能再次修改或繼續其他選擇。

要在對話框關閉時,或之後再獲取用戶已選擇的菜單項,可以通過 .getSelectionIndexArray() 方法獲取用戶當前選中的菜單索引集合,通過 .getSelectTextArray() 獲取已選中的菜單文本集合,另可通過 .getMenuList() 獲取所有的菜單文本。

按鈕點擊回調

BottomDialog 和 BottomMenu 和 MessageDialog 一樣,也支持設置 OK、Cancel、Other 按鈕的顯示,但請注意,部分主題可能不支持額外的 Cancel 和 Other 兩個按鈕。

要設置按鈕和回調,與 MessageDialog 類似,使用以下代碼進行設置:

.setOkButton("OK", new OnDialogButtonClickListener<BottomDialog>() {
    @Override
    public boolean onClick(BottomDialog dialog, View v) {
        //...
        return false;
    }
})
.setCancelButton("Cancel", new OnDialogButtonClickListener<BottomDialog>() {
    @Override
    public boolean onClick(BottomDialog dialog, View v) {
        //...
        return false;
    }
})
.setOtherButton("Other", new OnDialogButtonClickListener<BottomDialog>() {
    @Override
    public boolean onClick(BottomDialog dialog, View v) {
        //...
        return false;
    }
})

回調中有一個返回值,若return true 則可以點擊後不自動關閉對話框。

另外,DialogX 還提供了多種設置回調和按鈕文本的方法:

//只設置按鈕文本
.setOkButton("確定")

//只設置按鈕點擊回調
.setOkButton(new OnDialogButtonClickListener<BottomDialog>() {
    @Override
    public boolean onClick(BottomDialog dialog, View v) {
        toast("點擊確定按鈕");
        return false;
    }
});

//設置按鈕文本並設置回調
.setOkButton("確定", new OnDialogButtonClickListener<BottomDialog>() {
    @Override
    public boolean onClick(BottomDialog dialog, View v) {
        toast("點擊確定按鈕");
        return false;
    }
});

//隱藏按鈕
.setOkButton(null)

請依據個人喜好隨意使用。

獲取用戶選擇狀態

如果你的業務流程中,並不需要對用戶操作立即進行處理,但需要知道用戶選擇了哪個按鈕,你也可以通過持有對話框句柄,在後續獲取選擇的狀態,例如:

private BottomDialog dialog;

//業務流程模擬,創建並顯示了一個對話框,但不立即處理點擊事務
dialog = BottomDialog.show(...);

//需要時可根據 getButtonSelectResult() 方法獲取用戶點擊了哪個按鈕選項
BUTTON_SELECT_RESULT result = dialog.getButtonSelectResult();

BUTTON_SELECT_RESULT 是一個枚舉,它包含以下類型:

NONE,           //未做出選擇
BUTTON_OK,      //選擇了確定按鈕
BUTTON_CANCEL,  //選擇了取消按鈕
BUTTON_OTHER    //選擇了其他按鈕

你可以根據它的狀態判斷用戶點擊了哪個按鈕。

基於這個特性,如果業務流程中針對用戶的選擇,無論選擇哪個選項都有相同部分的代碼需要執行,那麼開發者也可以在用戶選擇後,例如在 DialogLifecycle#onDismiss 對話框的關閉事件中對用戶的選擇進行統一處理,減少重複代碼量;

生命週期回調

想要監控對話框的生命週期,可以實現其 .setDialogLifecycleCallback(...) 接口,建議使用build()方法構建對話框:

BottomMenu.build()
        .setDialogLifecycleCallback(new DialogLifecycleCallback<BottomMenu>() {
            @Override
            public void onShow(BottomMenu dialog) {
                //對話框啟動時回調
            }
            @Override
            public void onDismiss(BottomMenu dialog) {
                //對話框關閉時回調
            }
        })
        .show();

BottomDialog/BottomMenu 也支持 Lifecycle,你可以使用 .getLifecycle() 獲取 Lifecycle 對象。

對於可滑動的 BottomDialog/BottomMenu(受限於主題),可以使用增強型的 DialogLifecycleCallback 實現 BottomDialogSlideEventLifecycleCallback,它可以提供關於滑動過程以及滑動關閉的擴展事件處理和回調:

.setDialogLifecycleCallback(new BottomDialogSlideEventLifecycleCallback<BottomDialog>() {
    @Override
    public boolean onSlideClose(BottomDialog dialog) {
        log("滑動關閉對話框觸發");
        return false;	//默認返回 false,當返回 true 時表示攔截操作,對話框不會繼續關閉;
    }
    @Override
    public boolean onSlideTouchEvent(BottomDialog dialog, View v, MotionEvent event) {
        log("#滑動過程觸發: action="+ event.getAction() + " y="+event.getY());
        return false;	//默認返回 false,當返回 true 時表示攔截操作,對話框不會處理滑動流程,可實現觸摸事件接管;
    }
})

你也可以通過使用 new 構建實例時,override 的生命週期事件的方式來處理生命週期事務,例如:

//複寫事件演示
new BottomDialog() {
    @Override
    public void onShow(BottomDialog dialog) {
        //...
        tip("onShow");
    }
    @Override
    public void onDismiss(BottomDialog dialog) {
        //...
        tip("onDismiss");
    }
}

你也可以使用方法 .onShow(DialogXRunnable).onDismiss(DialogXRunnable),來處理生命週期事務,例如:

BottomDialog.show(...) 
        .onShow(new DialogXRunnable<BottomDialog>() {
            @Override
            public void run(BottomDialog dialog) {
                //BottomDialog show!
            }
        })
        .onDismiss(new DialogXRunnable<BottomDialog>() {
            @Override
            public void run(BottomDialog dialog) {
                //BottomDialog dismiss!
            }
        });

自定義佈局

BottomDialog 原生參數支持自定義佈局,你可以通過如下代碼自定義底部對話框佈局:

BottomDialog.show("標題", "這裏是對話框內容。\n底部對話框也支持自定義佈局擴展使用方式。",
        new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
            @Override
            public void onBind(BottomDialog dialog, View v) {
                //v.findViewById...
            }
        });

BottomMenu 菜單需要使用如下代碼加入自定義佈局:

BottomMenu.show("新標籤頁中打開", "稍後閱讀", "複製鏈接網址")
        .setMessage("菜單標題")
        .setOnMenuItemClickListener(new OnMenuItemClickListener<BottomMenu>() {
            @Override
            public boolean onClick(BottomMenu dialog, CharSequence text, int index) {
                toast(text);
                return true;
            }
        })
        .setCustomView(new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
            @Override
            public void onBind(BottomDialog dialog, View v) {
                //v.findViewById...
            }
        });

回調參數中,v 為您給定的佈局文件的實例化組件,您可以通過 v.findViewById(resId)來實例化其他子佈局組件,並在 onBind 方法中設置其功能和事件回調。

使用 ViewBinding 的話也可以更換為 OnBindingView 來實現直接通過 binding 獲取佈局實例:

BottomDialog.show("標題", "這裏是對話框內容。\n底部對話框也支持自定義佈局擴展使用方式。",
        new OnBindingView<BottomDialog, LayoutCustomViewBinding>() {
            @Override
            public void onBind(BottomDialog dialog, View view, LayoutCustomViewBinding binding) {
                //View childView = binding.childView
            }
        });

其他額外方法

//設置菜單項目(無級)
.setMenus("添加", "編輯", "刪除", "分享"...);

//設置圖標(無級)
.setIconResIds(R.mipmap.img_dialogx_demo_add, R.mipmap.img_dialogx_demo_edit...)
  
//設置菜單項目(列表或集合)
.setMenuList(list);

//強制重新刷新界面
.refreshUI();

//關閉對話框
.dismiss();

//是否允許點擊外部區域或返回鍵關閉
.setCancelable(boolean);

//設置標題文字樣式
.setTitleTextInfo(TextInfo);

//設置消息文字樣式
.setMessageTextInfo(TextInfo);

//設置按鈕文字樣式
.setCancelTextInfo(TextInfo);
    
//設置返回按鍵回調
.setOnBackPressedListener(OnBackPressedListener);

//獲取對話框實例化對象,您可以通過此方法更深度的訂製Dialog的功能
.getDialogImpl()

//獲取自定義佈局實例
.getCustomView()
    
//設置背景顏色,強行對對話框背景進行染色,請注意參數為int類型的顏色值而非R.color的索引
.setBackgroundColor(ColorInt);

//設置菜單文本樣式
.setMenuTextInfo(TextInfo)

//設置對話框圓角(會裁切內容顯示,此設置在 BottomDialog 和 BottomMenu 中只影響左上、右上兩個角的圓角)
.setRadius(float px)

//隱藏對話框(無動畫),恢復顯示請執行非靜態方法的 .show()
.hide();

//隱藏對話框(模擬關閉對話框的動畫),恢復顯示請執行非靜態方法的 .show()
.hideWithExitAnim();

//是否允許滑動關閉(僅對 Material 主題有效)
.setAllowInterceptTouch(boolean)

//是否處於顯示狀態
.isShow()

//置頂對話框
.bringToFront()

//指定對話框顯示層級
.setThisOrderIndex(int)

背景遮罩

BottomDialog 支持修改背景遮罩,這是為了豐富擴展性。如果需要背景遮罩,您可以自行使用如下代碼設置:

bottomDialog.setMaskColor(colorInt);

請注意,傳入參數為 ColorInt 值,您可以使用 Color.parseColor("#4D000000") 設置一個 HEX 色值,或使用 getResources().getColor(R.color.black30) 設置一個顏色的資源值。

額外組件

TextInfo

TextInfo 用於存儲基礎文本樣式設置,其包含一系列屬性和響應的 get/set 方法,例如方法解釋如下:

屬性 解釋 默認值
fontSize 字號大小,值為-1時使用默認樣式,單位:dp -1
gravity 對齊方式,值為-1時使用默認樣式,取值可使用Gravity.CENTER等對齊方式 -1
fontColor 文字顏色,值為1時使用默認樣式,取值可以用Color.rgb(r,g,b)等方式獲取 1
bold 是否粗體 false

請注意,fontColor 為 ColorInt 值,您可以使用 Color.parseColor("#4D000000") 設置一個 HEX 色值,或使用資源 getResources().getColor(R.color.black30) 設置一個顏色的資源值,請勿直接傳入資源 ID,它可能會無效。

關於“滑動提示條”

在使用 Material 主題風格且底部對話框,默認是可滑動關閉的,且當 cancelable 值為 true 時,會在對話框頂部顯示一個 “滑動提示條”,若依據您的需求需要隱藏其顯示,可通過以下方式關閉:

方法1,禁用向下滑動關閉

禁用向下滑動對話框以關閉 BottomDialog 可以隱藏滑動提示條。

.setAllowInterceptTouch(false)

方式2,針對單次有效的方案

借助 DialogX 暴露內部元素的特性,刪除 tab 佈局。

BottomDialog.show("標題", "這裏是對話框內容。\n底部對話框也支持自定義佈局擴展使用方式。",
        new OnBindView<BottomDialog>(R.layout.layout_custom_view) {
            @Override
            public void onBind(BottomDialog dialog, View v) {
                if (dialog.getDialogImpl().imgTab != null) {
                    ((ViewGroup) dialog.getDialogImpl().imgTab.getParent()).removeView(dialog.getDialogImpl().imgTab);
                }
                //...
            }
        });

方式3,重寫佈局

手動創建一個 layout_dialogx_bottom_material 的佈局(暗色對應 layout_dialogx_bottom_material_dark),內容請參考 layout_dialogx_bottom_material.xml(暗色對應 layout_dialogx_bottom_material_dark.xml

修改刪除其中 <ImageView id="@+id/img_tab"> 的佈局:

<ImageView
    android:id="@+id/img_tab"
    android:layout_width="30dp"
    android:layout_height="4dp"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    android:src="@drawable/rect_dialogx_material_dialogtap" />

此方案利用的是對資源的重寫方式完成修改,請無需擔心空指針,DialogX 內部有做對應處理。

單獨指定樣式

若你的 App 引入了多種主題,在特定場景下需要使對話框顯示為某種非全局的主體樣式,可使用 .build() 構建對話框,然後使用 .setStyle(style) 來指定主題樣式,在最後執行 .show() 命令顯示對話框,例如:

BottomDialog.build()
        //或直接使用 .build(IOSStyle.style())
        .setStyle(IOSStyle.style())
        .setTitle("Title")
        .setMessage("Message content.")
        .setOkButton("OK")
        .show();
Clone this wiki locally