文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android 自定义View 之 Dialog弹窗

2023-08-18 08:53

关注

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

在这里插入图片描述

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

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

一、弹窗视图帮助类

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

public class DialogViewHelper {    //内容视图    private final View mView;    //弱应用视图    private final SparseArray> mSubViews;        public DialogViewHelper(View view) {        //初始化        mSubViews = new SparseArray<>();        //获取弹窗视图        mView = view;    }        public DialogViewHelper(Context context, int layoutResId) {        //初始化        mSubViews = new SparseArray<>();        //获取弹窗视图        mView = LayoutInflater.from(context).inflate(layoutResId, null);    }        public View getContentView() {        return mView;    }        public View getSubView(int viewId) {        //通过视图id得到弱引用视图        WeakReference 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;    }        public void setText(int viewId, CharSequence text) {        TextView tv = (TextView) getSubView(viewId);        if (tv != null) {            tv.setText(text);        }    }        public void setTextColor(int viewId, int color) {        TextView tv = (TextView) getSubView(viewId);        if (tv != null) {            tv.setTextColor(color);        }    }        @RequiresApi(api = Build.VERSION_CODES.M)    public void setImageIcon(int viewId, Icon icon) {        ImageView iv = (ImageView) getSubView(viewId);        if (iv != null) {            iv.setImageIcon(icon);        }    }        public void setImageResource(int viewId, int resId) {        ImageView iv = (ImageView) getSubView(viewId);        if (iv != null) {            iv.setImageResource(resId);        }    }        public void setImageDrawable(int viewId, Drawable drawable) {        ImageView iv = (ImageView) getSubView(viewId);        if (iv != null) {            iv.setImageDrawable(drawable);        }    }        public void setImageBitmap(int viewId, Bitmap bitmap) {        ImageView iv = (ImageView) getSubView(viewId);        if (iv != null) {            iv.setImageBitmap(bitmap);        }    }        public void setBackgroundColor(int viewId, int color) {        View view = getSubView(viewId);        if (view != null) {            view.setBackgroundColor(color);        }    }        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)    public void setBackground(int viewId, Drawable drawable) {        View view = getSubView(viewId);        if (view != null) {            view.setBackground(drawable);        }    }        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 mTextColorArray = new SparseArray<>();        //背景颜色        public SparseArray mBgColorArray = new SparseArray<>();        //背景资源        public SparseArray mBgResArray = new SparseArray<>();        //存放文本        public SparseArray mTextArray = new SparseArray<>();        //存放点击事件        public SparseArray mClickArray = new SparseArray<>();        //存放长按点击事件        public SparseArray mLongClickArray = new SparseArray<>();        //存放对话框图标        public SparseArray mIconArray = new SparseArray<>();        //存放对话框图片        public SparseArray mBitmapArray = new SparseArray<>();        //存放对话框图片        public SparseArray mDrawableArray = new SparseArray<>();        //存放对话框图标资源文件        public SparseArray 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中增加如下代码:

        

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

                    

dialog_scale_anim_out.xml,代码如下:

        

dialog_from_top_anim_in.xml,代码如下所示:

    

dialog_from_top_anim_out.xml,代码如下所示:

    

dialog_from_right_anim_in.xml,代码如下所示:

    

dialog_from_right_anim_out.xml,代码如下所示:

    

dialog_from_bottom_anim_in.xml,代码如下所示:

    

dialog_from_bottom_anim_out.xml,代码如下所示:

    

dialog_from_left_anim_in.xml,代码如下所示:

    

dialog_from_left_anim_out.xml,代码如下所示:

    

现在样式和动画都写好了,下面可以去写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);    }        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);        }                public Builder setContentView(View view) {            dialogParams.mView = view;            dialogParams.mLayoutResId = 0;            return this;        }                public Builder setContentView(int layoutId) {            dialogParams.mView = null;            dialogParams.mLayoutResId = layoutId;            return this;        }                public Builder setText(int viewId, CharSequence text) {            dialogParams.mTextArray.put(viewId, text);            return this;        }                public Builder setTextColor(int viewId, int color) {            dialogParams.mTextColorArray.put(viewId, color);            return this;        }                public Builder setBackground(int viewId, Drawable drawable) {            dialogParams.mBgResArray.put(viewId, drawable);            return this;        }                public Builder setBackgroundColor(int viewId, int color) {            dialogParams.mBgColorArray.put(viewId, color);            return this;        }                public Builder setIcon(int viewId, Icon icon) {            dialogParams.mIconArray.put(viewId, icon);            return this;        }                public Builder setBitmap(int viewId, Bitmap bitmap) {            dialogParams.mBitmapArray.put(viewId, bitmap);            return this;        }                public Builder setDrawable(int viewId, Drawable drawable) {            dialogParams.mDrawableArray.put(viewId, drawable);            return this;        }                public Builder setImageRes(int viewId, int resId) {            dialogParams.mImageResArray.put(viewId, resId);            return this;        }                public Builder fullWidth() {            dialogParams.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;            return this;        }                public Builder fullHeight() {            dialogParams.mHeight = ViewGroup.LayoutParams.MATCH_PARENT;            return this;        }                public Builder setWidthAndHeight(int width, int height) {            dialogParams.mWidth = width;            dialogParams.mHeight = height;            return this;        }                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;        }                public Builder setAnimation(int styleAnimation) {            dialogParams.mAnimation = styleAnimation;            return this;        }                public Builder addDefaultAnimation() {            dialogParams.mAnimation = R.style.dialog_scale_anim;            return this;        }                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;        }                public Builder setCancelable(boolean cancelable) {            dialogParams.mCancelable = cancelable;            return this;        }                public Builder setOnClickListener(int viewId, View.OnClickListener onClickListener) {            dialogParams.mClickArray.put(viewId, onClickListener);            return this;        }                public Builder setOnLongClickListener(int viewId, View.OnLongClickListener onLongClickListener) {            dialogParams.mLongClickArray.put(viewId, onLongClickListener);            return this;        }                public Builder setOnCancelListener(OnCancelListener onCancelListener) {            dialogParams.mOnCancelListener = onCancelListener;            return this;        }                public Builder setOnDismissListener(OnDismissListener onDismissListener) {            dialogParams.mOnDismissListener = onDismissListener;            return this;        }                public Builder setOnKeyListener(OnKeyListener onKeyListener) {            dialogParams.mOnKeyListener = onKeyListener;            return this;        }                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;        }                public EasyDialog show() {            final EasyDialog dialog = create();            dialog.show();            return dialog;        }    }}

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

六、常规使用

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

                                                                                                            

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

                            

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

public class EasyDialogActivity extends EasyActivity 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 stringList = new ArrayList<>(Arrays.asList(stringArr));EasyDialogUtils.showSelectDialog(EasyDialogActivity.this, "社会主义核心价值观",                        stringList, this::showMsg);

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

在这里插入图片描述

八、源码

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

源码地址:EasyView

来源地址:https://blog.csdn.net/qq_38436214/article/details/131167550

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯