Dialog详解及自定义对话框

    上一篇文章介绍了AlertDialog对话框的一般运用并留下了几个问题。这篇文将更详细的介绍对话框的的基类Dialog。我们都知道AlertDialog的界面划分成了三个区域,那Dialog的区域呢?其实Dialog的界面要比AlertDialog简单得多,它只划分为两个区域:标题区域内容区域(其实一般的窗口都是这样划分的,包括Acivity),并且标题局域不能设置自定义布局而内容区域只能添加自定义布局,Dialog的setContentView()方法可用于向内容区域添加自定义布局,但本文不会用它来做自定义对话框。本文的重点是搞明白Dialog实质的显示原理和用更好的方法自定义对话框。

1.源码详解

    首先先介绍一下跟对话框相关的几个重要的知识点
Window:window是一个抽象类,很多方法都是由他的子类PhoneWindow实现。他代表一个窗口,对话框就是一个窗口,activity也是一个窗口(dialog和activity都持有各自的Widnow对象),用dialog的getWindow方法可获取该对话框的Window对象。
DecorView:该类继承自FrameLayout,window内部有一个DecorView对象,该DecorView即是window的根视图,用Window对象的getDecorView方法可得到该view。
WindowManager:这个类特别重要,我们所能看到的view就是由它添加到手机屏幕上的,该类中有三个重要的方法:

//向屏幕中添加view
public void addView(View view, ViewGroup.LayoutParams params);
//更新屏幕中的view
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//删除屏幕中的view
public void removeView(View view);

WindowManager.LayoutParams:WindowManager的内部类,继承自ViewGroup.LayoutParams,是专门用于Window的,用Window的setAttributes()方法即可设置Window的LayoutParams(布局参数)。这个类中用很多常量标记(如:FLAG_FULLSCREEN窗口全屏)用于设置Window的类型。用Window类的addFlags()可以添加这些标记

看下下面的两句代码

Dialog d = new Dialog(this);//this为activity实例
d.show();

它可以显示下图的对话框
这里写图片描述
可以看到阴暗背景和一条白色区域,其实白色区域就是Window的DecorView的所在区域,而且这块白色view就是DecorView的子view。这个子view就包含了上文中提到标题区域和内容标题,由于我们没有用setContentView设置内容,所以看不到内容区域。接下来看下dialog的构造函数和show()函数源码中分别做了什么

public Dialog(@NonNull Context context) {
    this(context, 0, true);
}
public Dialog(@NonNull Context context, @StyleRes int themeResId) {
    this(context, themeResId, true);
}

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

    可以发现构造函数最终是在第三个构造函数实现的,9到18行对传进来的就是对context用新的theme(theme就是属性的的批量设置)进行包装,让对话框窗口有“对话框”的样子(如非全屏,窗口外部会有阴暗效果),theme中的属性设置有些是和WindowManager.LayoutParams中的常量标记相对应的。关于theme和Window属性的详细内容可看我后续文章。20行到29行是对话框Window和WindowManager的初始化并设置一些回调,构造函数没有看到显示DecorView的相关代码。看下show()的源码

public void show() {
  if (mShowing) {
       if (mDecor != null) {
           if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
               mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
           }
           mDecor.setVisibility(View.VISIBLE);
       }
       return;
   }

   mCanceled = false;

   if (!mCreated) {
       dispatchOnCreate(null);
   }

   onStart();
   mDecor = mWindow.getDecorView();
    //ActionBar相关设置
   if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
       final ApplicationInfo info = mContext.getApplicationInfo();
       mWindow.setDefaultIcon(info.icon);
       mWindow.setDefaultLogo(info.logo);
       mActionBar = new WindowDecorActionBar(this);
   }

   WindowManager.LayoutParams l = mWindow.getAttributes();
   //输入法相关设置
   if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
       WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
       nl
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值