Android通用Dialog的封装

点击查看优化版通用Dialog的封装
日常开发中或多或少都会使用到Dialog,每次都需要自定义继承Dialog,写多了不胜其烦,今天我们对Dialog做个封装,可通过链式构建,以ViewDataBinding方式设置ContentView(之后会扩展支持通过View方式设置ContentView,类似的封装可看我的另外一篇文章Android通用PopupWindow的封装)。
Android通用Dialog的封装_第1张图片
先看使用示例:
这里的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的封装_第2张图片
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的封装_第3张图片
搞定,收工。

希望本文可以帮助到您,也希望各位不吝赐教,提出您在使用中的宝贵意见,谢谢。

如果可以的话,也可以扫一扫下方的二维码请作者喝一杯奶茶哈
Android通用Dialog的封装_第4张图片
谢谢您的观看。
有问题可发送至:[email protected]
欢迎交流,共同进步。

你可能感兴趣的:(分享,android,移动开发,app,安卓)