天天看點

Android通用Dialog的封裝

日常開發中或多或少都會使用到Dialog,每次都需要自定義繼承Dialog,寫多了不勝其煩,今天我們對Dialog做個封裝,可通過鍊式建構,以ViewDataBinding方式設定ContentView(之後會擴充支援通過View方式設定ContentView,類似的封裝可看我的另外一篇文章Android通用PopupWindow的封裝)。

Android通用Dialog的封裝

先看使用示例:

這裡的layout_sorting_filter是我們自定義View的xml檔案,LayoutSortingFilterBinding是對應的ViewDataBinding

GeneralDialog mGeneralDialog = new GeneralDialog.Builder<LayoutSortingFilterBinding>()
                    .layoutId(R.layout.layout_sorting_filter)
                    .intercept(new GeneralDialog.GeneralDialogEvent<LayoutSortingFilterBinding>() {
                        @Override
                        public void getView(LayoutSortingFilterBinding mBinding) {
                            /*在這裡可做初始化相關操作*/
                        }
                    })
                    .width(WindowManager.LayoutParams.MATCH_PARENT)
                    .height(WindowManager.LayoutParams.WRAP_CONTENT)
                    .setCanceledOnTouchOutside(true)
                    .gravity(Gravity.BOTTOM)
                    .addClickViews(R.id.tv_cancel, R.id.tv_sure)
                    .callbackClickOn(new GeneralDialog.OnClickCallback() {
                        @Override
                        public void onClick(GeneralDialog generalDialog, View view) {
                            int id = view.getId();
                            if (id == R.id.tv_cancel) {
                                 /*點選了取消*/
                            } else if (id == R.id.tv_sure) {
                                 /*點選了确定*/
                            }
                            generalDialog.dismiss();
                        }
                    }).Build(this);
            Window mWindow = mGeneralDialog.getWindow();
           /*設定視窗出現和消失的動畫*/
            if (mWindow != null) {
                mWindow.setWindowAnimations(R.style.anim_slide);
            }
            mGeneralDialog.show();
           

addClickViews方法是設定點選事件監聽的,支援設定多個View,如僅需設定一個也可通過方法addClickView(int id),如:

GeneralDialog mGeneralDialog = new GeneralDialog.Builder<LayoutSortingFilterBinding>()
                    .layoutId(R.layout.layout_sorting_filter)
                    .intercept(new GeneralDialog.GeneralDialogEvent<LayoutSortingFilterBinding>() {
                        @Override
                        public void getView(LayoutSortingFilterBinding mBinding) {
                            /*在這裡可做初始化相關操作*/
                        }
                    })
                    .width(WindowManager.LayoutParams.MATCH_PARENT)
                    .height(WindowManager.LayoutParams.WRAP_CONTENT)
                    .setCanceledOnTouchOutside(true)
                    .gravity(Gravity.BOTTOM)
                    .addClickView(R.id.tv_sure)
                    .callbackClickOn(new GeneralDialog.OnClickCallback() {
                        @Override
                        public void onClick(GeneralDialog generalDialog, View view) {
                            /*點選了确定*/
                            generalDialog.dismiss();
                        }
                    }).Build(this);
            Window mWindow = mGeneralDialog.getWindow();
           /*設定視窗出現和消失的動畫*/
            if (mWindow != null) {
                mWindow.setWindowAnimations(R.style.anim_slide);
            }
            mGeneralDialog.show();
           
Android通用Dialog的封裝

GeneralDialog完整的代碼如下:

import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import java.util.ArrayList;

/**
 * 作者:可口可樂的可樂 on 2019/9/02 14:45
 * Email : [email protected]
 * Describe:通用Dialog(用于各種提示或對話框形式的操作)
 * update on 2020/3/02 16:52
 */
public class GeneralDialog<T extends ViewDataBinding> extends Dialog implements View.OnClickListener {
    /**
     * Dialog layout視圖
     */
    private T mBinding;
    /**
     * 點選事件監聽(不帶layout視圖)
     */
    private OnClickCallback mClickEvent;
    /**
     * 點選事件監聽(帶layout視圖)
     */
    private OnBindingClickCallback<T> mBindingClickEvent;

    @SuppressWarnings("unchecked")
    private GeneralDialog(Context context, Builder builder) {
        this(context);
        mBinding = DataBindingUtil.inflate(getLayoutInflater(), builder.layoutId, null, false);
        setContentView(mBinding.getRoot());
        initSetting(builder.mDialogSet, builder.mCancelable, builder.canceledOnTouchOutside);
        intercept(builder.event);
        setClickEvent(builder.clickViews);
        mClickEvent = builder.mClickEvent;
        mBindingClickEvent = builder.mBindingClickEvent;
    }

    private GeneralDialog(@NonNull Context context) {
        super(context, R.style.DarkBackgroundDialog);
    }

    /**
     * 窗體的一些設定
     *
     * @param mDialogSet             窗體的設定類
     * @param mCancelable            是否可取消(按實體傳回鍵)
     * @param canceledOnTouchOutside 是否可點選外部取消
     **/
    private void initSetting(DialogSet mDialogSet, boolean mCancelable, boolean canceledOnTouchOutside) {
        Window window = getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        if (mDialogSet != null) {
            lp.width = mDialogSet.width;
            lp.height = mDialogSet.height;
            lp.gravity = mDialogSet.gravity;
            setCancelable(mDialogSet.mCancelable);
            setCanceledOnTouchOutside(mDialogSet.canceledOnTouchOutside);
        } else {
            lp.width = getContext().getResources().getDimensionPixelOffset(R.dimen.x812);
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            lp.gravity = Gravity.CENTER;
            setCancelable(mCancelable);
            setCanceledOnTouchOutside(canceledOnTouchOutside);
        }
        window.setAttributes(lp);
    }

    /**
     * 設定點選事件
     *
     * @param clickViews 需要設定點選事件監聽的View的id集合
     */
    private void setClickEvent(ArrayList<Integer> clickViews) {
        if (clickViews != null && mBinding != null) {
            for (int id : clickViews) {
                if (mBinding.getRoot().findViewById(id) != null) {
                    mBinding.getRoot().findViewById(id).setOnClickListener(this);
                }
            }
        }
    }

    /**
     * 攔截,可根據傳回的ViewDataBinding實作需要的邏輯
     *
     * @param event 回調事件,可傳回目前Dialog視圖
     */
    private void intercept(GeneralDialogEvent<T> event) {
        if (event != null && mBinding != null) {
            event.getView(mBinding);
        }
    }

    /**
     * 設定點選事件回調
     *
     * @param event 回調事件
     */
    private void clickCallback(OnClickCallback event) {
        if (event != null && mBinding != null) {
            mClickEvent = event;
        }
    }

    /**
     * 設定點選事件回調
     *
     * @param event 回調事件
     */
    private void bindingClickCallback(OnBindingClickCallback<T> event) {
        if (event != null && mBinding != null) {
            mBindingClickEvent = event;
        }
    }

    @Override
    public void onClick(View v) {
        if (mClickEvent != null) {
            mClickEvent.onClick(this, v);
        }
        if (mBindingClickEvent != null) {
            mBindingClickEvent.onClick(this, mBinding, v);
        }
    }

    public interface GeneralDialogEvent<T> {
        /**
         * 傳回Dialog的layout視圖(DataBinding類型)
         */
        void getView(T mBinding);
    }

    public interface OnClickCallback {
        /**
         * 點選事件回調
         *
         * @param dialog dialog本身
         * @param v      産生點選事件的View
         */
        void onClick(GeneralDialog dialog, View v);
    }

    public interface OnBindingClickCallback<T> {
        /**
         * 點選事件回調
         *
         * @param dialog   dialog本身
         * @param mBinding dialog的layout視圖(DataBinding類型)
         * @param v        産生點選事件的View
         */
        void onClick(GeneralDialog dialog, T mBinding, View v);
    }

    public static class Builder<T extends ViewDataBinding> {
        /**
         * Dialog的布局檔案id
         */
        private int layoutId;
        /**
         * 用于傳回Dialog的layout視圖(DataBinding類型)
         */
        private GeneralDialogEvent event;
        /**
         * 點選事件監聽(不帶layout視圖)
         */
        private OnClickCallback mClickEvent;
        /**
         * 點選事件監聽(帶layout視圖)
         */
        private OnBindingClickCallback mBindingClickEvent;
        /**
         * 設定此對話框在觸摸視窗外時是否被取消,優先級比cancel高
         */
        private DialogSet mDialogSet;
        /**
         * 設定此對話框在觸摸視窗外時是否被取消,優先級比mDialogSet低
         */
        private boolean canceledOnTouchOutside = true;
        /**
         * 設定此對話框在能否被按傳回鍵銷毀,優先級比mDialogSet低
         */
        private boolean mCancelable;
        /**
         * 存儲所有的帶點選事件的View id集合
         */
        private ArrayList<Integer> clickViews;

        /**
         * 設定Dialog的布局檔案id
         *
         * @param layoutId 布局檔案id
         */
        public Builder layoutId(int layoutId) {
            this.layoutId = layoutId;
            return this;
        }

        /**
         * 設定Dialog的寬
         *
         * @param width 寬
         */
        public Builder width(int width) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.width = width;
            return this;
        }

        /**
         * 設定Dialog的高
         *
         * @param height 高
         */
        public Builder height(int height) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.height = height;
            return this;
        }

        /**
         * 設定Dialog的對齊方式
         *
         * @param gravity 對齊方式
         */
        public Builder gravity(int gravity) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.gravity = gravity;
            return this;
        }

        /**
         * 設定Dialog點選外部是否可以取消
         *
         * @param cancel 是否可取消
         */
        public Builder setCanceledOnTouchOutside(boolean cancel) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.canceledOnTouchOutside = cancel;
            this.canceledOnTouchOutside = cancel;
            return this;
        }

        /**
         * 設定Dialog是否可以取消(按實體傳回鍵)
         *
         * @param cancel 是否可取消
         */
        public Builder setCancelable(boolean cancel) {
            if (mDialogSet == null) {
                mDialogSet = new DialogSet();
            }
            mDialogSet.mCancelable = cancel;
            this.mCancelable = cancel;
            if (!cancel) {
                /*this.canceledOnTouchOutside預設為true,當設定mCancelable為false時,設定canceledOnTouchOutside會影響
                 * mCancelable的值,是以如果設定了mCancelable為false,則canceledOnTouchOutside先要設定為false
                 * */
                mDialogSet.canceledOnTouchOutside = false;
                this.canceledOnTouchOutside = false;
            }
            return this;
        }

        /**
         * 攔截事件
         */
        public Builder intercept(GeneralDialogEvent event) {
            this.event = event;
            return this;
        }

        /**
         * 點選事件回調
         */
        public Builder callbackClickOn(OnClickCallback event) {
            this.mClickEvent = event;
            return this;
        }

        /**
         * 點選事件回調
         */
        public Builder bindingCallbackClickOn(OnBindingClickCallback event) {
            this.mBindingClickEvent = event;
            return this;
        }

        /**
         * 增加點選事件
         *
         * @param id 點選監聽的View的id
         */
        public Builder addClickView(int id) {
            if (clickViews == null) {
                clickViews = new ArrayList<>();
            }
            clickViews.add(id);
            return this;
        }

        /**
         * 增加點選事件
         *
         * @param ids 點選監聽的Views的id
         */
        public Builder addClickViews(int... ids) {
            if (ids != null && ids.length > 0) {
                if (clickViews == null) {
                    clickViews = new ArrayList<>();
                }
                for (int id : ids) {
                    clickViews.add(id);
                }
            }

            return this;
        }

        /**
         * 傳回帶DialogSet的GeneralDialog
         *
         * @param context 上下文
         */
        public GeneralDialog Build(Context context) {
            if (mDialogSet == null) {
                throw new RuntimeException("未設定窗體大小和位置資訊");
            }
            return new GeneralDialog<T>(context, this);
        }

        /**
         * 傳回不帶DialogSet的GeneralDialog
         *
         * @param context 上下文
         */
        public GeneralDialog getDefault(Context context) {
            if (mDialogSet != null) {
                mDialogSet = null;
            }
            return new GeneralDialog<T>(context, this);
        }
    }

    /**
     * 窗體設定類
     */
    public static class DialogSet {
        /**
         * Dialog的寬
         */
        private int width;
        /**
         * Dialog的高
         */
        private int height;
        /**
         * Dialog對齊模式
         */
        private int gravity;
        /**
         * 點選外部是否能取消
         */
        boolean canceledOnTouchOutside;
        /**
         * 是否能取消
         */
        boolean mCancelable;
    }

}
           

用到的R.style.DarkBackgroundDialog對應如下:

<resources>
    <style name="DarkBackgroundDialog" parent="@android:style/Theme.Dialog">
        <!-- 是否漂現在activity上 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 是否隐藏标題 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 是否允許背景變暗 -->
        <item name="android:backgroundDimEnabled">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
</resources>
           

代碼的注釋比較清晰,就不做過多說明了,有疑問的朋友歡迎在評論區留言。

Android通用Dialog的封裝

搞定,收工。

希望本文可以幫助到您,也希望各位不吝賜教,提出您在使用中的寶貴意見,謝謝。

如果可以的話,也可以掃一掃下方的二維碼請作者喝一杯奶茶哈

Android通用Dialog的封裝

謝謝您的觀看。

有問題可發送至:[email protected]

歡迎交流,共同進步。