Android 自定义View 之 Dialog弹窗

前言

  在日常开发中用到弹窗是比较多的,常用于提示作用,比如错误操作提示,余额不足提示,退出登录提示等,还有用于数据展示的弹窗,上拉弹窗等等,主要为了简化在日常开发中的使用。

在这里插入图片描述

正文

  Android中的Dialog弹窗是一种用于展示特定信息或者在用户需要进行某些操作时才显示的窗口。Dialog弹窗可以分为系统提供的常规Dialog弹窗和自定义Dialog弹窗。

  常规Dialog弹窗包括AlertDialog、ProgressDialog、DatePickerDialog、TimePickerDialog等,这些Dialog弹窗可以直接使用系统提供的API进行创建和使用。

一、弹窗视图帮助类

  下面我们来开始写代码,首先创建一个dialog包,然后在包下新建一个DialogViewHelper类,代码如下:

public class DialogViewHelper {

    //内容视图
    private final View mView;
    //弱应用视图
    private final SparseArray<WeakReference<View>> mSubViews;

    /**
     * 构造方法
     *
     * @param view 视图
     */
    public DialogViewHelper(View view) {
        //初始化
        mSubViews = new SparseArray<>();
        //获取弹窗视图
        mView = view;
    }

    /**
     * 构造方法
     *
     * @param context     上下文
     * @param layoutResId 布局资源id
     */
    public DialogViewHelper(Context context, int layoutResId) {
        //初始化
        mSubViews = new SparseArray<>();
        //获取弹窗视图
        mView = LayoutInflater.from(context).inflate(layoutResId, null);
    }

    /**
     * 获取弹窗内容视图
     *
     * @return mView
     */
    public View getContentView() {
        return mView;
    }

    /**
     * 获取子视图
     *
     * @param viewId 视图id
     * @return view
     */
    public View getSubView(int viewId) {
        //通过视图id得到弱引用视图
        WeakReference<View> viewWeakReference = mSubViews.get(viewId);
        View view = null;
        //如果弱引用视图不为空,说明有对应的xml文件,则对view进行赋值
        if (viewWeakReference != null) {
            view = viewWeakReference.get();
        }
        //如果view为空,则说明上面的弱引用列表数据不存在,通过findViewById方式重新赋值,不为空再放到数组里
        if (view == null) {
            view = mView.findViewById(viewId);
            if (view != null) {
                mSubViews.put(viewId, new WeakReference<>(view));
            }
        }
        return view;
    }

    /**
     * 设置文本
     *
     * @param viewId 视图id
     * @param text   字符
     */
    public void setText(int viewId, CharSequence text) {
        TextView tv = (TextView) getSubView(viewId);
        if (tv != null) {
            tv.setText(text);
        }
    }

    /**
     * 设置文本
     *
     * @param viewId 视图id
     * @param color  颜色
     */
    public void setTextColor(int viewId, int color) {
        TextView tv = (TextView) getSubView(viewId);
        if (tv != null) {
            tv.setTextColor(color);
        }
    }

    /**
     * 设置图标
     *
     * @param viewId 视图id
     * @param icon   图标
     */
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void setImageIcon(int viewId, Icon icon) {
        ImageView iv = (ImageView) getSubView(viewId);
        if (iv != null) {
            iv.setImageIcon(icon);
        }
    }

    /**
     * 设置图片
     *
     * @param viewId 视图id
     * @param resId  图标资源id
     */
    public void setImageResource(int viewId, int resId) {
        ImageView iv = (ImageView) getSubView(viewId);
        if (iv != null) {
            iv.setImageResource(resId);
        }
    }

    /**
     * 设置图片
     *
     * @param viewId   视图id
     * @param drawable 图
     */
    public void setImageDrawable(int viewId, Drawable drawable) {
        ImageView iv = (ImageView) getSubView(viewId);
        if (iv != null) {
            iv.setImageDrawable(drawable);
        }
    }

    /**
     * 设置图片
     *
     * @param viewId 视图id
     * @param bitmap 图片
     */
    public void setImageBitmap(int viewId, Bitmap bitmap) {
        ImageView iv = (ImageView) getSubView(viewId);
        if (iv != null) {
            iv.setImageBitmap(bitmap);
        }
    }

    /**
     * 设置背景颜色
     *
     * @param viewId 视图id
     * @param color  颜色
     */
    public void setBackgroundColor(int viewId, int color) {
        View view = getSubView(viewId);
        if (view != null) {
            view.setBackgroundColor(color);
        }
    }

    /**
     * 设置背景
     *
     * @param viewId   视图id
     * @param drawable drawable
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public void setBackground(int viewId, Drawable drawable) {
        View view = getSubView(viewId);
        if (view != null) {
            view.setBackground(drawable);
        }
    }

    /**
     * 设置点击监听
     *
     * @param viewId          视图id
     * @param onClickListener 点击监听
     */
    public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        View view = getSubView(viewId);
        if (view != null) {
            view.setOnClickListener(onClickListener);
        }
    }
}

  这个弹窗视图帮助类,通过构造方法定义参数的方式,在使用的时候可以传递弹窗视图Id也可以直接传View进来,这是获取弹窗的视图,还有获取弹窗视图中的子控件的视图,通过获取子控件的视图就可以对子控件如TextView、ImageView、View等控件进行属性及点击事件的设置。

二、弹窗控制类

  上面写好了弹窗帮助类,下面写控制类,会用到上面的帮助类,我们来看看是怎么用的,在dialog下创建EasyDialog类,先写一个空的类就好了,代码如下:

public class EasyDialog extends Dialog {

}

这里我们继承自Dialog,下面我们创建DialogController,这是我们的调用的类,代码如下所示:

public class DialogController {
    //对话弹窗
    private final EasyDialog mEasyDialog;
    //窗口
    private final Window mWindow;
    //帮助类
    private DialogViewHelper mViewHelper;

    public DialogController(EasyDialog easyDialog, Window window) {
        mEasyDialog = easyDialog;
        mWindow = window;
    }

    public void setDialogViewHelper(DialogViewHelper dialogViewHelper) {
        mViewHelper = dialogViewHelper;
    }

    public void setText(int viewId, CharSequence text) {
        mViewHelper.setText(viewId, text);
    }

    public void setTextColor(int viewId, int color) {
        mViewHelper.setTextColor(viewId, color);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    public void setImageIcon(int viewId, Icon icon) {
        mViewHelper.setImageIcon(viewId, icon);
    }

    public void setImageBitmap(int viewId, Bitmap bitmap) {
        mViewHelper.setImageBitmap(viewId, bitmap);
    }

    public void setImageDrawable(int viewId, Drawable drawable) {
        mViewHelper.setImageDrawable(viewId, drawable);
    }

    public void setImageResource(int viewId, int resId) {
        mViewHelper.setImageResource(viewId, resId);
    }

    public View getView(int viewId) {
        return mViewHelper.getSubView(viewId);
    }

    public void setBackgroundColor(int viewId, int color) {
        mViewHelper.setBackgroundColor(viewId, color);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public void setBackground(int viewId, Drawable drawable) {
        mViewHelper.setBackground(viewId, drawable);
    }

    public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        mViewHelper.setOnClickListener(viewId, onClickListener);
    }

    public EasyDialog getDialog() {
        return mEasyDialog;
    }

    public Window getWindow() {
        return mWindow;
    }

    /**
     * 弹窗参数类
     */
    @SuppressLint("UseSparseArrays")
    public static class DialogParams {
        //上下文
        public Context mContext;
        //主题资源Id
        public int mThemeResId;
        //点击其他区域弹窗是否取消
        public boolean mCancelable;
        //弹窗取消监听
        public DialogInterface.OnCancelListener mOnCancelListener;
        //弹窗隐藏监听
        public DialogInterface.OnDismissListener mOnDismissListener;
        //键值监听
        public DialogInterface.OnKeyListener mOnKeyListener;
        //文本颜色
        public SparseArray<Integer> mTextColorArray = new SparseArray<>();
        //背景颜色
        public SparseArray<Integer> mBgColorArray = new SparseArray<>();
        //背景资源
        public SparseArray<Drawable> mBgResArray = new SparseArray<>();
        //存放文本
        public SparseArray<CharSequence> mTextArray = new SparseArray<>();
        //存放点击事件
        public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
        //存放长按点击事件
        public SparseArray<View.OnLongClickListener> mLongClickArray = new SparseArray<>();
        //存放对话框图标
        public SparseArray<Icon> mIconArray = new SparseArray<>();
        //存放对话框图片
        public SparseArray<Bitmap> mBitmapArray = new SparseArray<>();
        //存放对话框图片
        public SparseArray<Drawable> mDrawableArray = new SparseArray<>();
        //存放对话框图标资源文件
        public SparseArray<Integer> mImageResArray = new SparseArray<>();
        //对话框布局资源id
        public int mLayoutResId;
        //对话框的内容view
        public View mView;
        //对话框宽度
        public int mWidth;
        //对话框高度
        public int mHeight;
        //对话框垂直外边距
        public int mHeightMargin;
        //对话框横向外边距
        public int mWidthMargin;
        //对话框出现动画
        public int mAnimation;
        //对话框显示位置,默认居中显示
        public int mGravity = Gravity.CENTER;

        public DialogParams(Context context, int themeResId) {
            mContext = context;
            mThemeResId = themeResId;
        }

        /**
         * 应用
         */
        public void apply(DialogController controller) {
            DialogViewHelper helper = null;

            if (mView != null) {
                helper = new DialogViewHelper(mView);
            } else if (mLayoutResId != 0) {
                helper = new DialogViewHelper(mContext, mLayoutResId);
            }

            //如果helper为null,则mLayoutResId为0,没有设置弹窗xml
            if (helper == null) {
                throw new IllegalArgumentException("Please set layout!");
            }
            //为对话框设置内容视图
            controller.getDialog().setContentView(helper.getContentView());
            //设置DialogViewHelper辅助类
            controller.setDialogViewHelper(helper);
            //设置文本
            for (int i = 0; i < mTextArray.size(); i++) {
                controller.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
            }
            //设置文本颜色
            for (int i = 0; i < mTextColorArray.size(); i++) {
                controller.setTextColor(mTextColorArray.keyAt(i), mTextColorArray.valueAt(i));
            }
            //设置图标
            for (int i = 0; i < mIconArray.size(); i++) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    controller.setImageIcon(mIconArray.keyAt(i), mIconArray.valueAt(i));
                }
            }
            //设置图片
            for (int i = 0; i < mBitmapArray.size(); i++) {
                controller.setImageBitmap(mBitmapArray.keyAt(i), mBitmapArray.valueAt(i));
            }
            //设置图片
            for (int i = 0; i < mDrawableArray.size(); i++) {
                controller.setImageDrawable(mDrawableArray.keyAt(i), mDrawableArray.valueAt(i));
            }
            //设置图片
            for (int i = 0; i < mImageResArray.size(); i++) {
                controller.setImageResource(mImageResArray.keyAt(i), mImageResArray.valueAt(i));
            }
            //设置背景颜色
            for (int i = 0; i < mBgColorArray.size(); i++) {
                controller.setBackgroundColor(mBgColorArray.keyAt(i), mBgColorArray.valueAt(i));
            }
            //设置背景资源
            for (int i = 0; i < mBgResArray.size(); i++) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    controller.setBackground(mBgResArray.keyAt(i), mBgResArray.valueAt(i));
                }
            }
            //设置点击
            for (int i = 0; i < mClickArray.size(); i++) {
                controller.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
            }
            //配置自定义效果,底部弹出,宽高,动画,全屏
            Window window = controller.getWindow();
            //显示位置
            window.setGravity(mGravity);
            if (mAnimation != 0) {
                //设置动画
                window.setWindowAnimations(mAnimation);
            }
            //设置布局参数,主要是宽高和边距
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = mWidth;
            params.height = mHeight;
            params.verticalMargin = mHeightMargin;
            params.horizontalMargin = mWidthMargin;
            window.setAttributes(params);
        }
    }
}

  这个控制类分为两个部分,一部分是用来设置弹窗帮助类的属性,一部分是定义弹窗的参数,DialogController中的方法直接调用DialogViewHelper的方法。然后是DialogParams类,里面定义了弹窗的一些参数,有一个构造方法,传入上下文和主题,然后通过apply()方法去设置DialogController中的方法,最终设置弹窗的位置和动画效果以及宽高。

三、监听接口

  一般的提示窗都有两个按钮,确定和取消,我们可以通过接口去进行使用,在dialog包下新建一个listener包,包下新建一个OnConfirmListener接口,代码如下:

public interface OnConfirmListener {
    void onConfirm();
}

再创建一个OnCancelListener接口,代码如下:

public interface OnCancelListener {
    void onCancel();
}

当然你还可以创建一个弹窗消失的监听接口。

四、样式

  为了增加用户体验,我们可以为弹窗增加出现和消失的动画效果,下面在themes.xml中增加如下代码:

    <!--自定义对话框-->
    <style name="EasyDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">true</item>
        <item name="android:windowNoTitle">true</item>
    </style>

这是弹窗的样式,下面我们定义弹窗出现和消失的动画,在res下新建一个anim包,以下的xml文件都在这个包下,创建dialog_scale_anim_in.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="135"
        android:fromXScale="0.8"
        android:fromYScale="0.8"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.05"
        android:toYScale="1.05" />

    <scale
        android:duration="105"
        android:fromXScale="1.05"
        android:fromYScale="1.05"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="135"
        android:toXScale="0.95"
        android:toYScale="0.95" />
    
    <scale
        android:duration="60"
        android:fromXScale="0.95"
        android:fromYScale="0.95"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="240"
        android:toXScale="1.0"
        android:toYScale="1.0" />

    <alpha
        android:duration="90"
        android:fromAlpha="0.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toAlpha="1.0" />

</set>

dialog_scale_anim_out.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="150"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.6"
        android:toYScale="0.6" />

    <alpha
        android:duration="150"
        android:fromAlpha="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toAlpha="0.0" />

</set>

dialog_from_top_anim_in.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromYDelta="-100%"
        android:toYDelta="0" />
</set>

dialog_from_top_anim_out.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromYDelta="0"
        android:toYDelta="-100%" />
</set>

dialog_from_right_anim_in.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="1000"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="0" />
</set>

dialog_from_right_anim_out.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="1000"
        android:toYDelta="0" />
</set>

dialog_from_bottom_anim_in.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:fromYDelta="1000"
        android:toXDelta="0"
        android:toYDelta="0" />
</set>

dialog_from_bottom_anim_out.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="100%" />
</set>

dialog_from_left_anim_in.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="-100%"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="0" />
</set>

dialog_from_left_anim_out.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="400"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="-100%"
        android:toYDelta="0" />
</set>

现在样式和动画都写好了,下面可以去写EasyDialog中的代码了。

五、简易弹窗

  在上面我们已经创建了EasyDialog,下面修改EasyDialog,代码如下所示:

public class EasyDialog extends Dialog {

    private final DialogController mController;

    public EasyDialog(@NonNull Context context, int themeResId) {
        super(context, themeResId);
        mController = new DialogController(this, getWindow());
    }

    public void setText(int viewId, CharSequence text) {
        mController.setText(viewId, text);
    }

    public View getView(int viewId) {
        return mController.getView(viewId);
    }

    public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        mController.setOnClickListener(viewId, onClickListener);
    }

    /**
     * EasyDialog 构建器
     */
    public static class Builder {
        //弹窗参数类
        private final DialogController.DialogParams dialogParams;

        public Builder(Context context) {
            this(context, R.style.EasyDialog);
        }

        public Builder(Context context, int themeResId) {
            dialogParams = new DialogController.DialogParams(context, themeResId);
        }

        /**
         * 设置对话框内容视图
         *
         * @param view 视图
         * @return Builder
         */
        public Builder setContentView(View view) {
            dialogParams.mView = view;
            dialogParams.mLayoutResId = 0;
            return this;
        }

        /**
         * 设置对话框内容视图
         *
         * @param layoutId 布局id
         * @return Builder
         */
        public Builder setContentView(int layoutId) {
            dialogParams.mView = null;
            dialogParams.mLayoutResId = layoutId;
            return this;
        }

        /**
         * 设置文本
         *
         * @param viewId 视图id
         * @param text   字符内容
         * @return Builder
         */
        public Builder setText(int viewId, CharSequence text) {
            dialogParams.mTextArray.put(viewId, text);
            return this;
        }

        /**
         * 设置文本颜色
         *
         * @param viewId 视图id
         * @param color  文字颜色
         * @return Builder
         */
        public Builder setTextColor(int viewId, int color) {
            dialogParams.mTextColorArray.put(viewId, color);
            return this;
        }

        /**
         * 设置背景
         *
         * @param viewId   视图id
         * @param drawable 资源
         * @return Builder
         */
        public Builder setBackground(int viewId, Drawable drawable) {
            dialogParams.mBgResArray.put(viewId, drawable);
            return this;
        }

        /**
         * 设置背景颜色
         *
         * @param viewId 视图id
         * @param color  颜色
         * @return Builder
         */
        public Builder setBackgroundColor(int viewId, int color) {
            dialogParams.mBgColorArray.put(viewId, color);
            return this;
        }

        /**
         * 设置图标
         *
         * @param viewId 视图id
         * @return Builder
         */
        public Builder setIcon(int viewId, Icon icon) {
            dialogParams.mIconArray.put(viewId, icon);
            return this;
        }

        /**
         * 设置图片
         *
         * @param viewId 视图id
         * @param bitmap 位图
         * @return Builder
         */
        public Builder setBitmap(int viewId, Bitmap bitmap) {
            dialogParams.mBitmapArray.put(viewId, bitmap);
            return this;
        }

        /**
         * 设置图片
         *
         * @param viewId   视图id
         * @param drawable 图片
         * @return Builder
         */
        public Builder setDrawable(int viewId, Drawable drawable) {
            dialogParams.mDrawableArray.put(viewId, drawable);
            return this;
        }

        /**
         * 设置图片
         *
         * @param viewId 视图id
         * @param resId  图片资源id
         * @return Builder
         */
        public Builder setImageRes(int viewId, int resId) {
            dialogParams.mImageResArray.put(viewId, resId);
            return this;
        }

        /**
         * 设置对话框宽度占满屏幕
         *
         * @return Builder
         */
        public Builder fullWidth() {
            dialogParams.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
            return this;
        }

        /**
         * 设置对话框高度占满屏幕
         *
         * @return Builder
         */
        public Builder fullHeight() {
            dialogParams.mHeight = ViewGroup.LayoutParams.MATCH_PARENT;
            return this;
        }

        /**
         * 设置对话框宽高
         *
         * @param width  宽
         * @param height 高
         * @return Builder
         */
        public Builder setWidthAndHeight(int width, int height) {
            dialogParams.mWidth = width;
            dialogParams.mHeight = height;
            return this;
        }

        /**
         * 设置对话框宽高和宽高边距
         *
         * @param width        宽
         * @param height       高
         * @param widthMargin  宽边距
         * @param heightMargin 高边距
         * @return Builder
         */
        public Builder setWidthAndHeightMargin(int width, int height, int widthMargin, int heightMargin) {
            dialogParams.mWidth = width;
            dialogParams.mHeight = height;
            dialogParams.mWidthMargin = widthMargin;
            dialogParams.mHeightMargin = heightMargin;
            return this;
        }

        /**
         * 设置动画
         *
         * @param styleAnimation 对话框动画
         * @return Builder
         */
        public Builder setAnimation(int styleAnimation) {
            dialogParams.mAnimation = styleAnimation;
            return this;
        }

        /**
         * 添加默认动画
         *
         * @return Builder
         */
        public Builder addDefaultAnimation() {
            dialogParams.mAnimation = R.style.dialog_scale_anim;
            return this;
        }

        /**
         * 添加自定义动画
         * @param gravity 弹窗位置  关系到使用什么动画
         * @param isAnimation 是否开启动画
         * @return Builder
         */
        public Builder addCustomAnimation(int gravity, boolean isAnimation) {
            switch (gravity) {
                case Gravity.TOP:
                    dialogParams.mGravity = Gravity.TOP;
                    dialogParams.mAnimation = R.style.dialog_from_top_anim;
                    break;
                case Gravity.RIGHT:
                    dialogParams.mGravity = Gravity.RIGHT;
                    dialogParams.mAnimation = R.style.dialog_from_right_anim;
                    break;
                case Gravity.BOTTOM:
                    dialogParams.mGravity = Gravity.BOTTOM;
                    dialogParams.mAnimation = R.style.dialog_from_bottom_anim;
                    break;
                case Gravity.LEFT:
                    dialogParams.mGravity = Gravity.LEFT;
                    dialogParams.mAnimation = R.style.dialog_from_left_anim;
                    break;
                default:
                    dialogParams.mGravity = Gravity.CENTER;
                    dialogParams.mAnimation = R.style.dialog_scale_anim;
                    break;
            }
            if (!isAnimation) {
                dialogParams.mAnimation = 0;
            }
            return this;
        }

        /**
         * 设置对话框是否可取消,默认为true。
         *
         * @return Builder
         */
        public Builder setCancelable(boolean cancelable) {
            dialogParams.mCancelable = cancelable;
            return this;
        }

        /**
         * 设置点击事件监听
         *
         * @param viewId          视图id
         * @param onClickListener 点击事件
         * @return Builder
         */
        public Builder setOnClickListener(int viewId, View.OnClickListener onClickListener) {
            dialogParams.mClickArray.put(viewId, onClickListener);
            return this;
        }

        /**
         * 设置长按事件监听
         *
         * @param viewId              视图id
         * @param onLongClickListener 长按事件
         * @return Builder
         */
        public Builder setOnLongClickListener(int viewId, View.OnLongClickListener onLongClickListener) {
            dialogParams.mLongClickArray.put(viewId, onLongClickListener);
            return this;
        }

        /**
         * 设置取消事件监听
         *
         * @param onCancelListener 取消事件监听
         * @return Builder
         */
        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
            dialogParams.mOnCancelListener = onCancelListener;
            return this;
        }

        /**
         * 设置隐藏事件监听
         *
         * @param onDismissListener 隐藏事件监听
         * @return Builder
         */
        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
            dialogParams.mOnDismissListener = onDismissListener;
            return this;
        }

        /**
         * 设置键监听
         *
         * @param onKeyListener 键监听事件
         * @return Builder
         */
        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
            dialogParams.mOnKeyListener = onKeyListener;
            return this;
        }

        /**
         * 创建弹窗
         *
         * @return EasyDialog
         */
        public EasyDialog create() {
            //通过上下文和主题样式设置弹窗
            final EasyDialog dialog = new EasyDialog(dialogParams.mContext, dialogParams.mThemeResId);
            //应用弹窗控制器
            dialogParams.apply(dialog.mController);
            //设置对话框是否可取消
            dialog.setCancelable(dialogParams.mCancelable);
            if (dialogParams.mCancelable) {
                //设置取消在触摸外面
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(dialogParams.mOnCancelListener);
            dialog.setOnDismissListener(dialogParams.mOnDismissListener);
            if (dialogParams.mOnKeyListener != null) {
                dialog.setOnKeyListener(dialogParams.mOnKeyListener);
            }
            return dialog;
        }

        /**
         * 显示弹窗
         *
         * @return dialog
         */
        public EasyDialog show() {
            final EasyDialog dialog = create();
            dialog.show();
            return dialog;
        }
    }
}

  这里面的核心是Builder类,通过链式调用我们可以自由的为EasyDialog设置需要的属性,然后就是对里面的各个方法进行处理,相信你看到代码就知道是什么意思了,何况还有注释,到这里为止其实我们的弹窗就已经写好了,下面我将说明一下怎么使用它。

六、常规使用

  下面我们在Activity中使用它,修改xml的代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".used.EasyDialogActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="----------------- 常规使用 -----------------"
        android:textColor="@color/black" />


    <LinearLayout
        android:gravity="center_vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:text="出现位置:"
            android:textColor="@color/black"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <RadioGroup
            android:id="@+id/rg_gravity"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/rb_top"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="" />

            <RadioButton
                android:id="@+id/rb_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="" />

            <RadioButton
                android:id="@+id/rb_bottom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="" />

            <RadioButton
                android:id="@+id/rb_left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="" />

            <RadioButton
                android:id="@+id/rb_center"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="" />
        </RadioGroup>
    </LinearLayout>

    <CheckBox
        android:id="@+id/cb_animation"
        android:text="是否需要动画"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <CheckBox
        android:id="@+id/cb_cancelable"
        android:text="是否点击外部消失"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示弹窗" />
</LinearLayout>

然后我们在layout下创建一个dialog_warm_tip.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="300dp"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_dialog_bg">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:gravity="center"
        android:text="弹窗标题"
        android:textColor="@color/black"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/line"
        app:layout_constraintTop_toBottomOf="@+id/tv_title" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:gravity="center"
        android:text="你想要写什么内容呢?"
        android:textColor="@color/black"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view" />

    <View
        android:id="@+id/view_1"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/line"
        app:layout_constraintTop_toBottomOf="@+id/tv_content" />

    <TextView
        android:id="@+id/tv_cancel"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:gravity="center"
        android:text="取消"
        android:textColor="@color/black"
        android:textSize="14sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/view_2"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view_1" />

    <View
        android:id="@+id/view_2"
        android:layout_width="1dp"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/line"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tv_confirm"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/tv_cancel"
        app:layout_constraintTop_toBottomOf="@+id/view_1" />

    <TextView
        android:id="@+id/tv_confirm"
        android:layout_width="0dp"
        android:layout_height="?attr/actionBarSize"
        android:gravity="center"
        android:text="确定"
        android:textColor="@color/black"
        android:textSize="14sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/view_2"
        app:layout_constraintTop_toBottomOf="@+id/view_1" />
</androidx.constraintlayout.widget.ConstraintLayout>

下面修改一下Activity中的代码,如下所示:

public class EasyDialogActivity extends EasyActivity<ActivityEasyDialogBinding> implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, RadioGroup.OnCheckedChangeListener {

    private EasyDialog easyDialog;
    private EasyDialog.Builder builder;
    private int mGravity;
    private boolean mIsAnimation;
    private boolean mIsCancelable;

    @Override
    protected void onCreate() {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        builder = new EasyDialog.Builder(EasyDialogActivity.this);
        initView();
    }

    private void initView() {
        binding.btnShow.setOnClickListener(this);
        binding.rgGravity.setOnCheckedChangeListener(this);
        binding.cbAnimation.setOnCheckedChangeListener(this);
        binding.cbCancelable.setOnCheckedChangeListener(this);
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        if (builder != null) {
            switch (checkedId) {
                case R.id.rb_top:
                    mGravity = Gravity.TOP;
                    break;
                case R.id.rb_right:
                    mGravity = Gravity.RIGHT;
                    break;
                case R.id.rb_bottom:
                    mGravity = Gravity.BOTTOM;
                    break;
                case R.id.rb_left:
                    mGravity = Gravity.LEFT;
                    break;
                case R.id.rb_center:
                    mGravity = Gravity.CENTER;
                    break;
            }
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (builder != null) {
            switch (buttonView.getId()) {
                case R.id.cb_animation:
                    mIsAnimation = isChecked;
                    break;
                case R.id.cb_cancelable:
                    mIsCancelable = isChecked;
                    break;
            }
        }
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_show:
                showDialog();
                break;
        }
    }

    /**
     * 显示弹窗
     */
    private void showDialog() {
        builder.setContentView(R.layout.dialog_warm_tip)
                //添加自定义动画
                .addCustomAnimation(mGravity, mIsAnimation)
                //设置对话框可取消
                .setCancelable(mIsCancelable)
                //设置标题
                .setText(R.id.tv_title, "温馨提示")
                //设置内容
                .setText(R.id.tv_content, "您今天还没有搞钱,请记得搞钱!")
                //设置文字颜色
                .setTextColor(R.id.tv_confirm, ContextCompat.getColor(EasyDialogActivity.this, R.color.white))
                //设置背景资源
                .setBackground(R.id.tv_confirm, ContextCompat.getDrawable(EasyDialogActivity.this, R.drawable.shape_confirm_bg))
                //设置弹窗宽高
                .setWidthAndHeight(EasyUtils.dp2px(EasyDialogActivity.this, 320), LinearLayout.LayoutParams.WRAP_CONTENT)
                //添加点击事件  取消
                .setOnClickListener(R.id.tv_cancel, v1 -> {
                    easyDialog.dismiss();
                })
                //添加点击事件  确定
                .setOnClickListener(R.id.tv_confirm, v2 -> {
                    showMsg("我知道了!");
                    easyDialog.dismiss();
                })
                //添加取消监听
                .setOnCancelListener(dialog -> {
                    showMsg("弹窗取消了");
                })
                //弹窗消失监听
                .setOnDismissListener(dialog -> {
                    showMsg("弹窗消失了");
                });
        //创建弹窗
        easyDialog = builder.create();
        //显示弹窗
        easyDialog.show();
    }
}

然后我们运行一下:

在这里插入图片描述

七、简易使用

  上面的代码的作用是为了让你能更好的设置弹窗的属性,而如果你想要很简单的使用,例如一行代码解决问题,也可以,为此我单独写了一个工具类在库里面,因为是使用的关系,所以就不贴代码了,你可以去源码中去看,那么当一个新的项目要使用这个弹窗需要怎么做呢?通过引入依赖的方式,例如在app模块中使用,则打开app模块下的build.gradle,在dependencies{}闭包下添加即可,之后记得要Sync Now

dependencies {
    implementation 'io.github.lilongweidev:easyview:1.0.5'
}

  然后使用,例如显示提示弹窗:

EasyDialogUtils.showTipDialog(EasyDialogActivity.this, "温馨提示", "端午又要调休!",
                        () -> showMsg("取消"), () -> showMsg("确定"));

显示String列表选择弹窗:

final String[] stringArr = {"富强", "民主", "文明", "和谐", "自由", "平等", "公正", "法治", "爱国", "敬业", "诚信", "友善"};
List<String> stringList = new ArrayList<>(Arrays.asList(stringArr));
EasyDialogUtils.showSelectDialog(EasyDialogActivity.this, "社会主义核心价值观",
                        stringList, this::showMsg);

这样使用是不是很简单呢?

在这里插入图片描述

八、源码

如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~

源码地址:EasyView