Android 自定义View 之 Dialog弹窗

简介: Android 自定义View 之 Dialog弹窗

前言


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

image.gif


正文


  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();
    }
}


然后我们运行一下:

223d486952364fec901df2595df65ecc.gif


七、简易使用


  上面的代码的作用是为了让你能更好的设置弹窗的属性,而如果你想要很简单的使用,例如一行代码解决问题,也可以,为此我单独写了一个工具类在库里面,因为是使用的关系,所以就不贴代码了,你可以去源码中去看,那么当一个新的项目要使用这个弹窗需要怎么做呢?通过引入依赖的方式,例如在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);


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

image.gif


八、源码


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

源码地址:EasyView

相关文章
|
12天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
14天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
27 5
|
21天前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
21天前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
30 2
|
22天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
26天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
23 2
|
3天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
5天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
7天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。