Skip to content

AdvancedExtendedUsage_tc

Kongzue edited this page Sep 23, 2024 · 3 revisions

🌐 View English Document | 简体中文文档

🆙高階擴展用法

DialogX 結構圖

DialogX 結構圖

單獨針對組件生效的屬性

儘管已經有全局的配置,但開發者依然可以單獨針對某一個組件進行一些設置。

例如如果全局配置為禁止使用點擊外部區域關閉對話框,而要 BottomDialog 例外,那麼可以使用如下配置:

BottomDialog.overrideCancelable = BaseDialog.BOOLEAN.TRUE;

還可以針對組件的動畫進行單獨設置,例如:

BottomDialog.overrideEnterDuration = 100;    //入場動畫時長為100毫秒
BottomDialog.overrideExitDuration = 100;     //出場動畫時長為100毫秒
BottomDialog.overrideEnterAnimRes = R.anim.anim_dialogx_top_enter;  //入場動畫資源
BottomDialog.overrideExitAnimRes = R.anim.anim_dialogx_top_exit;    //出場動畫資源

這樣的方法單獨對某一個組件進行進出場動畫的重寫設置。注意這些方法不會影響全局其他組件,只是單獨設置單一組件的動畫效果。

優先度為:實例使用方法設置 > 組件override設置 > 全局設置。

另外 FullScreenDialog 只提供動畫時長設置,不提供動畫資源設置(此組件只允許上下進出場動畫);

CustomDialog 額外擁有了 overrideMaskEnterAnimResoverrideMaskExitAnimRes,可以覆蓋背景遮罩的動畫效果,設置為 0 時取消動畫。

此時,在當前項目中其他的對話框組件將依然遵循全局配置,而單獨針對組件生效的屬性的對話框則會以單獨屬性設置為主。

注意此配置要區別於針對單一對話框實例的屬性設置,例如:

BottomDialog.build()
        ...
        .setCancelable(true)   
        .show();

重寫部分主題

例如想要將 PopTip 吐司提示不按照主題的設定(例如螢幕底部)顯示,而是以自己的要求顯示(例如螢幕中央),但對於 PopTip 的 align 屬性屬於主題控制的,此時可以透過重寫主題來調整對話框的部分行為,例如:

DialogX.globalStyle = new MaterialStyle(){
    @Override
    public PopTipSettings popTipSettings() {
        return new PopTipSettings() {
            @Override
            public ALIGN align() {
                return ALIGN.CENTER;
            }
        };
    }
};

完全自訂開啟、關閉動畫

請注意此方法針對的是需要對對話框內容組件和細節完全自行控制的情況,如果你只是想改變整體動畫而不需要控制細節那麼請參閱某種對話框的自訂進入和關閉動畫章節,例如 《基礎對話框 MessageDialog 和 輸入對話框 InputDialog - 自訂進入和關閉動畫》

使用 DialogXAnimInterface 介面可以完全自訂開啟、關閉動畫。

由於 DialogX 對話框組件的內部元素都是暴露的,你可以輕鬆獲取並訪問內部實例,利用這一點,再加上 DialogXAnimInterface 會負責對話框啟動和關閉的動畫行為,你可以充分利用它實現你想要的效果。

例如對於一個 CustomDialog,你可以這樣控制其啟動和關閉動畫:

CustomDialog.show(new OnBindView<CustomDialog>(R.layout.layout_custom_dialog) {
            @Override
            public void onBind(final CustomDialog dialog, View v) {
                //...
            }
        })
        //實現完全自訂動畫效果
        .setDialogXAnimImpl(new DialogXAnimInterface<CustomDialog>() {
            @Override
            public void doShowAnim(CustomDialog customDialog, ViewGroup dialogBodyView) {
                Animation enterAnim;
                int enterAnimResId = com.kongzue.dialogx.R.anim.anim_dialogx_top_enter;
                enterAnim = AnimationUtils.loadAnimation(me, enterAnimResId);
                enterAnim.setInterpolator(new DecelerateInterpolator(2f));
                long enterAnimDurationTemp = enterAnim.getDuration();
                enterAnim.setDuration(enterAnimDurationTemp);
                customDialog.getDialogImpl().boxCustom.startAnimation(enterAnim);
                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(0f, 1f);
                bkgAlpha.setDuration(enterAnimDurationTemp);
                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        if (customDialog.getDialogImpl() == null || customDialog.getDialogImpl().boxRoot == null) {
                            return;
                        }
                        customDialog.getDialogImpl().boxRoot.setBkgAlpha((Float) animation.getAnimatedValue());
                    }
                });
                bkgAlpha.start();
            }
            @Override
            public void doExitAnim(CustomDialog customDialog, ViewGroup dialogBodyView) {
                int exitAnimResIdTemp = com.kongzue.dialogx.R.anim.anim_dialogx_default_exit;
                Animation exitAnim = AnimationUtils.loadAnimation(me, exitAnimResIdTemp);
                customDialog.getDialogImpl().boxCustom.startAnimation(exitAnim);
                ValueAnimator bkgAlpha = ValueAnimator.ofFloat(1f, 0f);
                bkgAlpha.setDuration(exitAnim.getDuration());
                bkgAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        if (customDialog.getDialogImpl() == null || customDialog.getDialogImpl().boxRoot == null) {
                            return;
                        }
                        customDialog.getDialogImpl().boxRoot.setBkgAlpha((Float) animation.getAnimatedValue());
                    }
                });
                bkgAlpha.start();
            }
        });

對於 animProgress 它本質上是個反向回調執行器,因為動畫時長不定,你需要通知 DialogX 當前你的動畫到達哪個階段了,對話框需要根據這個階段進行操作處理,例如關閉動畫執行過程應當是 1f 至 0f 的過程,完畢後應當銷毀對話框,那麼當 animProgress.run(0f) 時就會執行銷毀流程,而啟動動畫應當是 0f 至 1f 的過程,當 animProgress.run(1f) 時啟動對話框的動畫完全執行完畢。

隊列對話框

某些場景下需要有“模態”對話框的需要,即,一次性創建多個對話框,組成隊列,逐一顯示,當上一個對話框關閉時自動啟動下一個對話框,此時可以使用隊列對話框來完成。

範例代碼如下,在 DialogX.showDialogList(...) 中構建多個對話框,請注意這些對話框必須是沒有啟動的狀態,使用 .build() 方法完成構建,以 “,” 分隔組成隊列,即可自動啟動。

DialogX.showDialogList(
        MessageDialog.build().setTitle("提示").setMessage("這是一組消息對話框隊列").setOkButton("開始").setCancelButton("取消")
                .setCancelButton(new OnDialogButtonClickListener<MessageDialog>() {
                    @Override
                    public boolean onClick(MessageDialog dialog, View v) {
                        dialog.cleanDialogList();
                        return false;
                    }
                }),
        PopTip.build().setMessage("每個對話框會依次顯示"),
        PopNotification.build().setTitle("通知提示").setMessage("直到上一個對話框消失"),
        InputDialog.build().setTitle("請注意").setMessage("你必須使用 .build() 方法構建,並保證不要自己執行 .show() 方法").setInputText("輸入文字").setOkButton("知道了"),
        TipDialog.build().setMessageContent("準備結束...").setTipType(WaitDialog.TYPE.SUCCESS),
        BottomDialog.build().setTitle("結束").setMessage("下滑以結束旅程,祝你編碼愉快!").setCustomView(new OnBindView<BottomDialog>(R.layout.layout_custom_dialog) {
            @Override
            public void onBind(BottomDialog dialog, View v) {
                ImageView btnOk;
                btnOk = v.findViewById(R.id.btn_ok);
                btnOk.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                                        dialog.dismiss();
                                    }
                });
            }
        })
);

使用過程中,隨時可以使用 .cleanDialogList() 來停止接下來的隊列對話框的顯示。

實現模式(實驗性)

調整 DialogX 的實現模式有兩種方法,其一是全局調整,即直接修改 DialogX 的全局配置,例如:

DialogX.implIMPLMode= DialogX.IMPL_MODE.WINDOW;

另一個方式為單次修改,僅在一個跳出視窗中生效,例如:

MessageDialog.build()    //必須使用 build() 方法構建時指定 DialogImplMode 才會生效。
        .setDialogImplMode(DialogX.IMPL_MODE.WINDOW)
        .setTitle("Title text")
        .setMessage("Message content")
        .setOkButton("OK")
        .show();

Window 實現模式

DialogX 預設使用 View 實現模式,自適應生命週期,你也可以選擇 Window 的實現方案(與 AlertDialog 類似),請注意,使用 Window 實現模式是實驗性質的,且存在一定的風險和已知 bug,你可以使用以下方法啟用 Window 實現模式:

DialogX.implIMPLMode= DialogX.IMPL_MODE.WINDOW;

WindowUtil 提供了額外自訂WindowManager.LayoutParams的介面:

WindowUtil.windowSettings = new WindowUtil.WindowSettings() {
    @Override
    public WindowManager.LayoutParams overrideWindowLayoutParamsInterface(Context context, View dialogView, WindowManager.LayoutParams originWindowLayoutParams) {
        //處理 originWindowLayoutParams...
        return originWindowLayoutParams;
    }

    @Override
    public ViewGroup overrideRootView(Context context) {
        //自訂根布局,對話框布局會被添加到根布局中,返回 null 代表使用默認的 FrameLayout
        return null;
    }
};

此外,如果 app 具有懸浮窗權限,在 Window 模式下還可以實現全局跳出視窗,請在初始化時使用以下代碼開啟:

DialogX.globalHoverWindow = true;

DialogFragment 實現方式

在最新的版本 0.0.41.beta4 中,提供了 DialogFragment 實現方式,使用以下方法啟用 DialogFragment 實現:

DialogX.implIMPLMode= DialogX.IMPL_MODE.DIALOG_FRAGMENT;

此模式下目前尚未發現待處理的問題,但依然不建議在生產環境使用。

FloatingActivity 實現方式

類似於 Window,使用一個透明底的 Activity 作為對話框的底層承擔顯示,這樣的做法可以再一定程度上從後台啟動(PendingIntent)。

DialogX.implIMPLMode= DialogX.IMPL_MODE.FLOATING_ACTIVITY;
Clone this wiki locally