Android 建造通用的Dialog和PopupWindow

一、Builder设计模式的定义
将一个复杂对象的构建和它的表现分离,使得同样的构建过程,可以创建不同的表示。
建造步骤:
1. 明确建造的对象,如QuickDialog、QuickPopup
2. 确定建造者,如QuickBuild(里面包含建造参数QuickParams)
3. 一些View的辅助类(QuickViewHelper)
二、Android使用系统的Dialog
//1.得到对话框的构造器,可以构造对话的模版
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("标题");
builder.setMessage("包含内容");
//2.添加一个确定按钮
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {

    }
});
//3.添加一个取消按钮
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {

    }
});
//4.使用构造器创建一个对话框的对象
AlertDialog dialog = builder.create();
//5.显示对话框
dialog.show();
三、安际开发情况
由于每个APP的风格不一样,UI设计的弹窗五花八门,系统的Dialog用得不多。系统的Dialog定制性不强等等。
由于系统的Dialog有诸多的不便,自己写一个通用的可定制的Dialog就是必要的了。
四、Dialog都有哪些通用特性?
1.Dialog的显示动画、位置,常用的是由下面出来和中间出来。
2.Dialog的背景,包括背景色、背景圆角。
3.其它的一些设置,用Dialog系统有的就行了,比如取消监听等等
五、Dialog、PopupWindow建造类(QuickBuild ),主要是设置构建的一些参数。
    /**
     * Dialog、PopupWindow 构建器
     */
    public class QuickBuild {
        private Activity mActivity;
        // 默认主题
        private int mTheme = R.style.QuickDialogTheme;
        // 构建Dialog的参数
        private QuickParams mParams;

        public QuickBuild(Activity activity) {
            this.mActivity = activity;
            mParams = new QuickParams(activity);
        }

        public static QuickBuild crate(Activity activity) {
            return new QuickBuild(activity);
        }

        /**
         * 设置主题
         */
        public QuickBuild setTheme(int theme) {
            this.mTheme = theme;
            return this;
        }

        /**
         * 设置宽度
         */
        public QuickBuild setWidth(int width) {
            mParams.mWidth = width;
            return this;
        }

        /**
         * 设置高度
         */
        public QuickBuild setHeight(int height) {
            mParams.mHeight = height;
            return this;
        }

        /**
         * 设置宽高
         */
        public QuickBuild setWidthHeight(int width, int height) {
            mParams.mWidth = width;
            mParams.mHeight = height;
            return this;
        }

        /**
         * 设置布局ID
         */
        public QuickBuild setContentView(int layoutId) {
            mParams.mLayoutId = layoutId;
            return this;
        }

        /**
         * 设置布局View
         */
        public QuickBuild setContentView(View view) {
            mParams.mContentView = view;
            return this;
        }


        /**
         * 设置ContentView背景的颜色:默认白色
         */
        public QuickBuild setBackgroundColor(int color) {
            mParams.mBgColor = color;
            return this;
        }

        /**
         * 设置Window背景
         *
         * @param isSetBg false 就不设置默认圆角
         */
        public QuickBuild isSetBackground(boolean isSetBg) {
            mParams.mIsSetBg = isSetBg;
            return this;
        }

        /**
         * 设置ContentView背景的圆角:默认10dp
         *
         * @param radius 内部已转成dp
         * @return
         */
        public QuickBuild setBackgroundRadius(int radius) {
            mParams.mBgRadius = radius;
            return this;
        }

        /**
         * 设置背景是否模糊:默认是模糊的
         *
         * @param isDimEnabled
         * @return
         */
        public QuickBuild isDimEnabled(boolean isDimEnabled) {
            mParams.mIsDimEnabled = isDimEnabled;
            return this;
        }

        /**
         * 设置宽度占满的比例
         */
        public QuickBuild setWidthScale(float scale) {
            mParams.mScale = scale;
            return this;
        }

        /**
         * 设置宽度占满
         */
        public QuickBuild setFullWidth() {
            setWidthScale(1.0f);
            return this;
        }

        /**
         * 设置高度占满
         */
        public QuickBuild setFullHeight() {
            mParams.mIsHeightFull = true;
            mParams.mHeight = ViewGroup.LayoutParams.MATCH_PARENT;
            return this;
        }

        /**
         * 从底部弹出
         */
        public QuickBuild fromBottom(boolean isAnim) {
            if (isAnim) {
                mParams.mAnimation = R.style.Anim_Dialog_Bottom;
            }
            mParams.mGravity = Gravity.BOTTOM;
            return this;
        }

        /**
         * 从顶部弹出
         */
        public QuickBuild fromTop(boolean isAnim) {
            if (isAnim) {
                mParams.mAnimation = R.style.Anim_Dialog_Top;
            }
            mParams.mGravity = Gravity.TOP;
            return this;
        }

        /**
         * 设置自定义的弹出动画
         */
        public QuickBuild animation(int resId) {
            mParams.mAnimation = resId;
            return this;
        }


        /**
         * 设置点击空白是否消失
         */
        public QuickBuild cancelable(boolean cancelable) {
            mParams.mCancelable = cancelable;
            return this;
        }

        /**
         * 设置取消的监听
         */
        public QuickBuild setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) {
            mParams.mOnCancelListener = onCancelListener;
            return this;
        }

        /**
         * 设置Dialog消息的监听
         */
        public QuickBuild setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
            mParams.mOnDismissListener = onDismissListener;
            return this;
        }

        /**
         * 设置按键的监听
         */
        public QuickBuild setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
            mParams.mOnKeyListener = onKeyListener;
            return this;
        }

        /**
         * 根据布局ID设置文本信息
         */
        public QuickBuild setText(int viewId, CharSequence text) {
            mParams.setText(viewId, text);
            return this;
        }

        /**
         * 根据布局ID设置点击事件
         */
        public QuickBuild setOnClickListener(int viewId, View.OnClickListener onClickListener) {
            mParams.setOnClickListener(viewId, onClickListener);
            return this;
        }

        /**
         * 构建Dialog
         */
        public QuickDialog build() {
            QuickDialog dialog = new QuickDialog(mActivity, mTheme);
            dialog.apply(mParams);
            return dialog;
        }

        /**
         * 构建并显示Dialog
         */
        public QuickDialog show() {
            QuickDialog dialog = build();
            dialog.show();
            return dialog;
        }

        /**
         * 构建QuickPopup
         */
        public QuickPopup buildPopup() {
            QuickPopup popup = new QuickPopup(mActivity);
            popup.apply(mParams);
            return popup;
        }


        /**
         * Dialog构建参数
         */
        static class QuickParams {
            // 存放文本的集合
            SparseArray<CharSequence> mTextArray = new SparseArray<>();
            // 存放点击事件的集合
            SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
            DialogInterface.OnCancelListener mOnCancelListener;
            DialogInterface.OnDismissListener mOnDismissListener;
            DialogInterface.OnKeyListener mOnKeyListener;
            View mContentView;
            int mLayoutId;
            int mWidth;
            int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
            int mGravity = Gravity.CENTER;
            int mAnimation;
            float mScale = 0.75f;
            int mBgRadius = 5;
            int mBgColor = Color.parseColor("#ffffff");
            boolean mIsSetBg = true;
            boolean mIsDimEnabled = true;
            boolean mCancelable = true;
            //
            boolean mIsHeightFull = false;

            QuickParams(Context context) {
                DisplayMetrics dm = context.getResources().getDisplayMetrics();
                mWidth = (int) Math.floor(dm.widthPixels);
            }

            /**
             * 根据布局ID设置文本信息
             */
            void setText(int viewId, CharSequence text) {
                mTextArray.put(viewId, text);
            }

            /**
             * 根据布局ID设置点击事件
             */
            void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
                mClickArray.put(viewId, onClickListener);
            }
        }

    }
六、View的辅助类,主要是加载View布局和找到里面的View等
/**
 * View显示的辅助类
 */

public class QuickViewHelper {
    private View mContentView;
    private SparseArray<WeakReference<View>> mViews = new SparseArray<>();

    public QuickViewHelper(Context context, int layoutResId) {
        mContentView = LayoutInflater.from(context).inflate(layoutResId, null, false);
    }

    public QuickViewHelper(View mView) {
        mContentView = mView;
    }

    public View getContentView() {
        return mContentView;
    }


    /**
     * 设置文本
     */
    public void setText(int viewId, CharSequence charSequence) {
        TextView tv = getView(viewId);
        if (tv != null && charSequence != null) {
            if (TextUtils.isEmpty(charSequence)) {
                tv.setText("");
                return;
            }
            tv.setText(charSequence);
        }
    }

    public <T extends View> T getView(int viewId) {
        WeakReference<View> viewWeakReference = mViews.get(viewId);
        View view = null;
        if (viewWeakReference == null) {
            view = mContentView.findViewById(viewId);
            if (view != null) {
                mViews.put(viewId, new WeakReference<>(view));
            }
        } else {
            view = viewWeakReference.get();
        }
        return (T) view;
    }

    /**
     * 设置点击事件
     */
    public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        View view = getView(viewId);
        if (view != null && onClickListener != null) {
            view.setOnClickListener(onClickListener);
        }
    }
}
七、自定义通用的Dialog
/**
 * 自定义的Dialog
 */

public class QuickDialog extends Dialog {
    private QuickViewHelper viewHelper;


    QuickDialog(@NonNull Context context, int themeResId) {
        super(context, themeResId);
    }

    /**
     * 根据布局ID设置文本信息
     */
    public QuickDialog setText(int viewId, CharSequence text) {
        if (viewHelper != null) {
            viewHelper.setText(viewId, text);
        }
        return this;
    }

    /**
     * 根据布局ID设置点击事件
     */
    public QuickDialog setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        if (viewHelper != null) {
            viewHelper.setOnClickListener(viewId, onClickListener);
        }
        return this;
    }


    @Override
    public void dismiss() {
        closeKeyboard();
        super.dismiss();
    }


    /**
     * 关闭软键盘
     */
    private void closeKeyboard() {
        View view = getCurrentFocus();
        if (view instanceof TextView) {
            InputMethodManager mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            mInputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
        }
    }

    public void apply(QuickBuild.QuickParams params) {
        // 1.设置布局
        if (params.mLayoutId != 0) {
            viewHelper = new QuickViewHelper(getContext(), params.mLayoutId);
        }
        if (params.mContentView != null) {
            viewHelper = new QuickViewHelper(params.mContentView);
        }
        if (viewHelper == null) {
            throw new IllegalArgumentException("请调用setContentView方法设置布局");
        }
        View contentView = viewHelper.getContentView();
        setContentView(contentView);

        // 2.设置文本
        int textSize = params.mTextArray.size();
        for (int i = 0; i < textSize; i++) {
            viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
        }
        // 3.设置点击
        int clickSize = params.mClickArray.size();
        for (int i = 0; i < clickSize; i++) {
            viewHelper.setOnClickListener(params.mClickArray.keyAt(i), params.mClickArray.valueAt(i));
        }

        // 4.设置Window
        Window window = getWindow();
        // 位置
        window.setGravity(params.mGravity);
        // 动画
        if (params.mAnimation != 0) {
            window.setWindowAnimations(params.mAnimation);
        }
        // 宽高
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = (int) (params.mWidth * params.mScale);
        lp.height = params.mHeight;
        window.setAttributes(lp);
        // 设置Window背景
        if (params.mIsSetBg) {
            final GradientDrawable bg = new GradientDrawable();
            int radius = dp2px(getContext(), params.mBgRadius);
            // 1 2 3 4(顺时针)
            bg.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
            bg.setColor(params.mBgColor);
            window.setBackgroundDrawable(bg);
        }

        // 设置背景是否模糊
        if (!params.mIsDimEnabled) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                window.setDimAmount(0f);
            }
        }

        // 设置外面能不能点击
        setCancelable(params.mCancelable);
        setCanceledOnTouchOutside(params.mCancelable);
        // 设置事件监听
        setOnCancelListener(params.mOnCancelListener);
        setOnDismissListener(params.mOnDismissListener);
        setOnKeyListener(params.mOnKeyListener);
    }

    public static int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }
}
八、 自定义PopupWindow
/**
 * 自定义的PopupWindow
 */

public class QuickPopup extends PopupWindow {
    private QuickViewHelper viewHelper;
    private Activity mActivity;

    public QuickPopup(Activity activity) {
        super(activity);
        this.mActivity = activity;
    }

    /**
     * 根据布局ID设置文本信息
     */
    public QuickPopup setText(int viewId, CharSequence text) {
        if (viewHelper != null) {
            viewHelper.setText(viewId, text);
        }
        return this;
    }

    /**
     * 根据布局ID设置点击事件
     */
    public QuickPopup setOnClickListener(int viewId, View.OnClickListener onClickListener) {
        if (viewHelper != null) {
            viewHelper.setOnClickListener(viewId, onClickListener);
        }
        return this;
    }


    @Override
    public void dismiss() {
        // 关闭键盘
        closeKeyboard();
        // 背景还原
        setWindowDim(mActivity, false);
        super.dismiss();
    }


    /**
     * 关闭软键盘
     */
    private void closeKeyboard() {
        if (mActivity == null) {
            return;
        }
        View view = mActivity.getCurrentFocus();
        if (view instanceof TextView) {
            InputMethodManager mInputMethodManager = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
            mInputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
        }
    }

    public void apply(QuickBuild.QuickParams params) {
        // 1.设置布局
        if (params.mLayoutId != 0) {
            viewHelper = new QuickViewHelper(mActivity, params.mLayoutId);
        }
        if (params.mContentView != null) {
            viewHelper = new QuickViewHelper(params.mContentView);
        }
        if (viewHelper == null) {
            throw new IllegalArgumentException("请调用setContentView方法设置布局");
        }
        View contentView = viewHelper.getContentView();
        measureView(contentView);
        setContentView(contentView);

        // 设置宽高
        if (params.mWidth > 0) {
            setWidth(params.mWidth);
        } else {
            setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        if (params.mHeight > 0) {
            setHeight(params.mHeight);
        } else {
            setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        if (params.mIsHeightFull) {
            setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        }

        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setOutsideTouchable(params.mCancelable);
        setFocusable(params.mCancelable);
        // 动画
        if (params.mAnimation != 0) {
            setAnimationStyle(params.mAnimation);
        }


        // 2.设置文本
        int textSize = params.mTextArray.size();
        for (int i = 0; i < textSize; i++) {
            viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
        }
        // 3.设置点击
        int clickSize = params.mClickArray.size();
        for (int i = 0; i < clickSize; i++) {
            viewHelper.setOnClickListener(params.mClickArray.keyAt(i), params.mClickArray.valueAt(i));
        }
        // 背景模糊
        if (params.mIsDimEnabled) {
            setWindowDim(mActivity, true);
        }
    }

    /**
     * 测量View的宽高
     *
     * @param view View
     */
    private void measureView(View view) {
        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 给整个屏幕添加阴影背景
     *
     * @param activity
     * @param isDim    TRUE  添加  false 不添加
     */
    public void setWindowDim(Activity activity, boolean isDim) {
        if (null != activity) {
            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
            lp.alpha = isDim ? .7f : 1.0f;
            if (isDim) {
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            } else {
                activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            }
            activity.getWindow().setAttributes(lp);
        }
    }
    
}
七、Dialog默认显示的主题
 <!--从下弹出来的基本动画-->
    <style name="Anim_Dialog_Bottom" parent="@style/Theme.AppCompat.Dialog">
        <item name="android:windowEnterAnimation">@anim/slide_in</item>
        <item name="android:windowExitAnimation">@anim/slide_out</item>
    </style>

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

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!--slide_out-->
    <translate
        android:duration="250"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="100%" />
</set>
八、Dialog的使用
QuickDialog dialog = QuickBuild.create(this)
        .setContentView(R.layout.dialog_loading)
        .fromBottom(true)
        .setCancelable(false)
        .setIsDimEnabled(false)
        .build();
        // 设置Dialog布局里面View的点击事件,文本设置一样
        dialog.setOnClickListener(R.id.dialog_message, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
            }
        });
        dialog.show();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容