欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 到底什么是Dialog?

程序员文章站 2022-05-19 13:16:37
...
package zhaoyong.mylibrary;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

/**
 * Created by ZhaoYong on 2017/10/17.
 * 什么是Dialog
 */

public class 什么是Dialog extends Activity {

    void MyDialog() {
        //test code
        Dialog dialog = new Dialog(this);
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Title")
                .setMessage("Dialog content.")
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .show();

        /*
         * Dialog:
         * implements DialogInterface, Callback, android.view.KeyEvent.Callback, OnCreateContextMenuListener
         *
         * Activity Dialog Popupwindow Toast Snackbar 都属于显示控件
         * Fragment 不属于显示控件,而是显示组件
         *
         * Activity Dialog PopupWindow Toast Snackbar 类的内部都管理维护着一个Window对象
         * 这个Window对象不但是一个View组件的集合管理对象,它也实现了组件的加载与绘制流程
         *
         * Dialog源码中实现了getWindow()方法获取其中的Window对象
         * Fragment中没有Window对象
         *
         * 设计模式:Dialog是建造者模式,可看AlertDialog.Builder
         */

        // Dialog构造方法
        /*
        Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
            if (createContextThemeWrapper) {
                if (themeResId == 0) {
                    final TypedValue outValue = new TypedValue();
                    context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                    themeResId = outValue.resourceId;
                }
                mContext = new ContextThemeWrapper(context, themeResId);
            } else {
                mContext = context;
            }

            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

            final Window w = new PhoneWindow(mContext);
            mWindow = w;
            w.setCallback(this);
            w.setOnWindowDismissedCallback(this);
            w.setWindowManager(mWindowManager, null, null);
            w.setGravity(Gravity.CENTER);

            mListenersHandler = new ListenersHandler(this);
        }
        */

        /*
         * 直接构造了一个PhoneWindow,并赋值给Dialog的成员变量mWindow
         * Dialog和Activity的显示逻辑都是类似的,都是通过对应的Window变量来实现窗口的加载与显示的
         * mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         */

        // window类的setWindowManager方法:
        /*
        public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
            mAppToken = appToken;
            mAppName = appName;
            mHardwareAccelerated = hardwareAccelerated
                    || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
            if (wm == null) {
                wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
            }
            mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }
        */

        /*
        总结:

        Dialog和Activity的显示逻辑是相似的都是内部管理这一个Window对象
        用WIndow对象实现界面的加载与显示逻辑

        Dialog中的Window对象与Activity中的Window对象是相似的,都对应着一个WindowManager对象

        Dialog相关的几个类:Dialog,AlertDialog,AlertDialog.Builder,AlertController,AlertController.AlertParams
        其中Dialog是窗口的父类,主要实现Window对象的初始化和一些共有逻辑
        而AlertDialog是具体的Dialog的操作实现类,AlertDialog.Builder类是AlertDialog的内部类
        主要用于构造AlertDialog,AlertController是AlertDialog的控制类
        AlertController.AlertParams类是控制参数类

        构造显示Dialog的一般流程:
        构造AlertDialog.Builder,然后设置各种属性,最后调用AlertDialog.Builder.create方法获取AlertDialog对象
        并且create方法中会执行,构造AlertDialog,设置dialog各种属性的操作
        最后我们调用Dialog.show方法展示窗口,初始化Dialog的布局文件,Window对象等
        然后执行mWindowManager.addView方法,开始执行绘制View的操作,并最终将Dialog显示出来
        */

    }

    /**
     * 要理解什么是dialog就要理解什么是window
     */
    Window window;

    // Window表示的是一个窗口的概念,它是一个抽象类,它的具体实现是PhoneWindow
    // 权限:android.permission.SYSTEM_ALERT_WINDOW
    @SuppressLint("RtlHardcoded")
    public void show(String content, int layoutId) {
        WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        // 加载布局
        View view = View.inflate(this, layoutId, null);
        // 设置浮窗params属性
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.type = WindowManager.LayoutParams.TYPE_PHONE;
        params.gravity = Gravity.TOP + Gravity.LEFT;// 将重心设置为左上方
        params.format = PixelFormat.TRANSLUCENT;// 半透明
        params.x = 100;// 设置显示位置
        params.y = 100;
        TextView tvLocation = new TextView(this);
        tvLocation.setText("");
        tvLocation.setText(content);
        // 将View添加到窗体管理器
        wm.addView(view, params);
    }

    /*
     * 所以,什么是Dialog?
     * Dialog Popupwindow Toast Snackbar
     * 这些都是内置Window对象,通过Window显示封装好的显示控件
     * 以上都是个人理解
     * 这些显示控件都是基于window的各个属性创建出来用于在界面上显示的
     */

    /*
     * Window属性:
     * LayoutParams.Flags参数表示Window的属性,通过设置它的选项可以控制Window的显示特性。如下几种常见选项:
     * FLAG_NOT_FOCUSABLE 不许获得焦点
     * FLAG_NOT_TOUCHABLE 不接受触摸屏事件
     * FLAG_NOT_TOUCH_MODAL 当窗口可以获得焦点(没有设置 FLAG_NOT_FOCUSALBE 选项)时,仍然将窗口范围之外的点设备事件(鼠标、触摸屏)发送给后面的窗口处理。否则它将独占所有的点设备事件,而不管它们是不是发生在窗口范围内。
     * FLAG_SHOW_WHEN_LOCKED 当屏幕锁定时,窗口可以被看到。这使得应用程序窗口优先于锁屏界面。可配合FLAG_KEEP_SCREEN_ON选项点亮屏幕并直接显示在锁屏界面之前。可使用FLAG_DISMISS_KEYGUARD选项直接解除非加锁的锁屏状态。此选项只用于最顶层的全屏幕窗口。
     * FLAG_DIM_BEHIND 窗口之后的内容变暗
     * FLAG_BLUR_BEHIND 窗口之后的内容变模糊
     */

}