android源码分析-Dialog

article/2025/8/27 22:24:41

今天给大家介绍android -Dialog源码分析

 Dialog 是所有对话框的基类,例如AlertDialog,我们要深入了解指导Dialog的用法,逻辑,必须要把Dilaog弄清楚,下面首先我们来看下Google对Dialog的类描述:/**
  • Base class for Dialogs.
    Note: Activities provide a facility to manage the creation, saving and
  • restoring of dialogs. See {@link Activity#onCreateDialog(int)},
  • {@link Activity#onPrepareDialog(int, Dialog)},
  • {@link Activity#showDialog(int)}, and {@link Activity#dismissDialog(int)}. If
  • these methods are used, {@link #getOwnerActivity()} will return the Activity
  • that managed this dialog.
    Often you will want to have a Dialog display on top of the current
  • input method, because there is no reason for it to accept text. You can
  • do this by setting the {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
  • WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} window flag (assuming
  • your Dialog takes input focus, as it the default) with the following code:
  • getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  •     WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);</pre>
    

*/
通过上面的描述,我们知道有2点重要的信息
1、Acitivity给我们提供了创建,预创建,展示,取消对话框的框架,在这个框架中我们不必担心额外的异常,这写方法包括:
onCreateDialog(int) :创建Dialog过程中要回调
onPrepareDialog(int, Dialog):准备创建对话框过程中要回调
showDialog(int):展示对话框
dismissDialog(int):取消对话框
架构流程图
这里很简单,大家可以自行阅读源码
2、WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 这个是WindowManager里面的flag标志位,当setFlag(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM );之后WindowManager中的EditText自动获取控件,将会获得焦点,有个相反的Flag就是,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,设置之后WindowManager中的视图将得不到任务焦点。

 接下来我看看Dialog的构造方法:
public Dialog(Context context, int theme) {this(context, theme, true);}Dialog(Context context, int theme, boolean createContextThemeWrapper) {if (createContextThemeWrapper) {if (theme == 0) {TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,outValue, true);theme = outValue.resourceId;}mContext = new ContextThemeWrapper(context, theme);} else {mContext = context;}mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);Window w = PolicyManager.makeNewWindow(mContext);mWindow = w;w.setCallback(this);w.setOnWindowDismissedCallback(this);w.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}

Dialog的另外两个构造方法最终都要调用的这个方法,只不过在第三个参数为True,

 public Dialog(Context context) {this(context, 0, true);}
public Dialog(Context context, int theme) {this(context, theme, true);}

,所以我们先来分析下这个构造方法,首先看到了这个createContextThemeWrapper 参数,这个参数为true的话 dialog会查找系统的主题资源文件id,来初始化context。
如果theme==0,那么就查找theme,
if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; }
这里有个类TypedValue ,这个类主要是动态的表示和存储不同类型数据的容器,例如,系统资源resourceId,density,data,各种单位转化,例如,sp,dip,px,等等,这个类里面有个我们常用的方法:public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0; }
来实现不同单位下的转化。
然后下面有这么一句context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
outValue, true);
这句里面其实是在查找resourceId,最后赋值到TypedValue 里面,Android系统里面系统资源的查找主要有2个类来实现一个是,Resources,和AssetManager,他们的主要区别是,Resources主要是通过资源id来查找资源,例如各种getText(int id),getDrawable(int id),getXml…都是通过id来查找的,而AssetManager,则是通过文件名字来查找的,

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

获得窗口管理者,负责对窗口中添加视图和移除视图,

 Window w = PolicyManager.makeNewWindow(mContext);

在这里创建了一个窗口类,我们可以看到这里有个PolicyManager类,这其实是个创建窗口的工厂类,我们看看源码:

public final class PolicyManager {
30    private static final String POLICY_IMPL_CLASS_NAME =
31        "com.android.internal.policy.impl.Policy";
32
33    private static final IPolicy sPolicy;
34
35    static {
36        // Pull in the actual implementation of the policy at run-time
37        try {
38            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
39            sPolicy = (IPolicy)policyClass.newInstance();
40        } catch (ClassNotFoundException ex) {
41            throw new RuntimeException(
42                    POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
43        } catch (InstantiationException ex) {
44            throw new RuntimeException(
45                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
46        } catch (IllegalAccessException ex) {
47            throw new RuntimeException(
48                    POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
49        }
50    }
51
52    // Cannot instantiate this class
53    private PolicyManager() {}
54
55    // The static methods to spawn new policy-specific objects
56    public static Window makeNewWindow(Context context) {
57        return sPolicy.makeNewWindow(context);
58    }
59
60    public static LayoutInflater makeNewLayoutInflater(Context context) {
61        return sPolicy.makeNewLayoutInflater(context);
62    }
63
64    public static WindowManagerPolicy makeNewWindowManager() {
65        return sPolicy.makeNewWindowManager();
66    }
67
68    public static FallbackEventHandler More ...makeNewFallbackEventHandler(Context context) {
69        return sPolicy.makeNewFallbackEventHandler(context);
70    }
71}

在这个类里面,我们首先会看到IPolicy,IPolicy是一个代理接口,

public interface IPolicy {
32    public Window makeNewWindow(Context context);
33
34    public LayoutInflater makeNewLayoutInflater(Context context);
35
36    public WindowManagerPolicy makeNewWindowManager();
37
38    public FallbackEventHandler makeNewFallbackEventHandler(Context context);
39}

这个接口有三个方法,makeNewWindow()创建一个新窗口,makeNewLayoutInflater创建LayoutInflater ,makeNewWindowManager 窗口管理代理,makeNewFallbackEventHandler,创建FallbackEventHandler 手机回退处理接口,FallbackEventHandler 的实现类有PhoneFallbackEventHandler
IPolicy 在static语句块中用反射实例化了com.android.internal.policy.impl.Policy,然后通过这个实例来创建窗口操作。
com.android.internal.policy.impl.Policy,通过这个我们指导IPolicy 的实现类是Policy ,我们看看Policy

public class More ...Policy implements IPolicy {
35    private static final String TAG = "PhonePolicy";
36
37    private static final String[] preload_classes = {
38        "com.android.internal.policy.impl.PhoneLayoutInflater",
39        "com.android.internal.policy.impl.PhoneWindow",
40        "com.android.internal.policy.impl.PhoneWindow$1",
41        "com.android.internal.policy.impl.PhoneWindow$DialogMenuCallback",
42        "com.android.internal.policy.impl.PhoneWindow$DecorView",
43        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
44        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
45    };
46
47    static {
48        // For performance reasons, preload some policy specific classes when
49        // the policy gets loaded.
50        for (String s : preload_classes) {
51            try {
52                Class.forName(s);
53            } catch (ClassNotFoundException ex) {
54                Log.e(TAG, "Could not preload class for phone policy: " + s);
55            }
56        }
57    }
58
59    public Window More ...makeNewWindow(Context context) {
60        return new PhoneWindow(context);
61    }
62
63    public LayoutInflater More ...makeNewLayoutInflater(Context context) {
64        return new PhoneLayoutInflater(context);
65    }
66
67    public WindowManagerPolicy More ...makeNewWindowManager() {
68        return new PhoneWindowManager();
69    }
70
71    public FallbackEventHandler More ...makeNewFallbackEventHandler(Context context) {
72        return new PhoneFallbackEventHandler(context);
73    }
74}在static里面会提前加载会用到的类,到此我们知道了PolicyManager.makeNewWindow(mContext);最终是是Policy通过直接实例化了PhoneWindow来生成的,android中的window类是抽象类,定义了窗口的行为,和部分实现,他们的实现类为PhoneWindow,在这里面我可以猜到,android系统要适配平板的话应该是有PadWindow,如果是手表的话估计有WatchWindow,具体的PhoneWindow的逻辑会在以后的文章深入分析。继续接到前面讲,mWindow = w;设置dialog的mWindow ,w.setCallback(this);设置dialog中mWindow 的回调实现,这样dialog就具有类window的行为,比如我们按back dialog就会关闭等等。w.setOnWindowDismissedCallback(this);设置dialog关闭行为回调,mListenersHandler = new ListenersHandler(this);实例化ListenersHandler,ListenersHandler是继承Handler,负责dialog的show,cancel,dismiss的消息接受回调,具体Handler,机制可自行查阅资料,这里不在详述。下面我们来看看dialog是如何展示到Activity中的。首先我们看看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();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.copyFrom(l);nl.softInputMode |=WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;l = nl;}try {mWindowManager.addView(mDecor, l);mShowing = true;sendShowMessage();} finally {}}/*** Hide the dialog, but do not dismiss it.*/public void hide() {if (mDecor != null) {mDecor.setVisibility(View.GONE);}}

逻辑还是很清晰的,首先判断根据if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
判断是否dialog正在展示,如果展示的话就 判断dialog是否有actionbar,如果有的话就初始化,然后显示,你可能要问actionbar是什么东西,就是最上面有各icon和名字的标题界面,我们的layout文件初始化的时候就能看到那个东西,dialog也是可以设置的。
然后mCanceled = false;,因为正要展示dialog,当然不能被取消了啊,只有在展示完了之后才可以消失操作,
mDecor = mWindow.getDecorView();设置dialog的mDecor 属性,每个窗口都有一个根视图,所以mDecor 也是dialog的根视图,dialog上的View都是mDecor 的子视图,这个概念在Activity中也存在。
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);
}
如果dialog设置了FEATURE_ACTION_BAR,那么给actionbar设置默认的icon和logo,这里有ApplicationInfo类,这个类描述了Mainifest.xml的配置信息,这个类也可以通过context.getPackageManager();进一步获得,icon,logo就是我们在<application 。。。节点下面配置的信息啦,

WindowManager.LayoutParams l = mWindow.getAttributes();if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {WindowManager.LayoutParams nl = new WindowManager.LayoutParams();nl.copyFrom(l);nl.softInputMode |=WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;l = nl;}

从mWindow获得dialog的布局属性信息,然后配置SOFT_INPUT_IS_FORWARD_NAVIGATION,这个Flag就是让窗口显示在最前面,这个信息一般有系统完成设置,l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0
在android系统中有很多的flag都是通过这种方式来存储和判断的,一来节省内存提高速度,二来方便逻辑操作,语法优美,在上面这断话是通过按位与操作符,来判SOFT_INPUT_IS_FORWARD_NAVIGATION是否存在,==0则不存在,具体为什么,稍微想下很好理解
WindowManager.LayoutParams 是描述窗口的位置,水平、垂直权重、窗口类型Type、窗口UI交互等众多配置信息的类Flag、例如FLAG_DIM_BEHIND,这个flag控制Window背景是否变暗(dialog就有这个属性),FLAG_NOT_FOCUSABLE设置window不要获取焦点,这样里面的editText不可以输入等等,我以后会在其它文章详细介绍。

     try {mWindowManager.addView(mDecor, l);mShowing = true;sendShowMessage();} finally {}

终于到了dialog的展示语句了,mWindowManager将mDecor视图添加如了窗口中,那么dialog就展示在了我们面前,mWindowManager的实现类是WindowManagerImpl,所以addView()是在WindowManagerImpl中实现的,那么我们看看是怎么实现的。


82     @Override
83     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
84         applyDefaultToken(params);
85         mGlobal.addView(view, params, mDisplay, mParentWindow);
86     }

applyDefaultToken(params); 如果IBinder为null则设置默认的IBinder,IBinder是FrameWork和Wms的通信桥梁,具体怎么实现的我还在继续学习研究,mGlobal.addView(view, params, mDisplay, mParentWindow);真正的添加动作是
public final class WindowManagerImpl implements WindowManager { 49 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); 50 private final Display mDisplay; 51 private final Window mParentWindow; 52 53 private IBinder mDefaultToken;

WindowManagerGlobal完成的,下面这张图看以看出WindowManagerGlobal的位置
这里写图片描述
具体WindowManagerGlobal中addView是怎么添加的,以后的文章会详细讲解。
sendShowMessage(); 这个方法是通知事件的回调例如展示,取消等等,Message.obtain(mShowMessage).sendToTarget();发送各种消息。
下面我们来分析下dismiss(),dialog如何消失的,

public void dismiss() {if (Looper.myLooper() == mHandler.getLooper()) {dismissDialog();} else {mHandler.post(mDismissAction);}}void dismissDialog() {if (mDecor == null || !mShowing) {return;}if (mWindow.isDestroyed()) {Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");return;}try {mWindowManager.removeViewImmediate(mDecor);} finally {if (mActionMode != null) {mActionMode.finish();}mDecor = null;mWindow.closeAllPanels();onStop();mShowing = false;sendDismissMessage();}}

if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
在主线程和子线程都可以安全关闭对话框

void dismissDialog() {if (mDecor == null || !mShowing) {return;}

如果视图不存在或者根本就没有展示出来,就不用管了,

if (mWindow.isDestroyed()) {Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");return;}

如果窗口被销毁了 也就不管了

 try {mWindowManager.removeViewImmediate(mDecor);} finally {if (mActionMode != null) {mActionMode.finish();}mDecor = null;mWindow.closeAllPanels();onStop();mShowing = false;sendDismissMessage();}

mWindowManager立刻执行移除视图操作,finally中最后处理操作,回调onStop(),调用dialog消失回调接口,更改mShowing状态。
剩下的其它方法有一部分是Window.Callback,
KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback
接口中的方法,
还有一部分是PhoneWindow中的方法
接下来我来看看Dialog的一个实现类AlertDialog,警告对话框。

AlertDialog

AlertDialog是一个典型的建造者模式,通过Builder来构造AlertDialog,在构造过程中是通过AlertController来管理视图和属性的.下面是一个典型的展示代码:

new AlertDialog.Builder(self).setTitle("标题") .setMessage("简单消息框").setPositiveButton("确定", null).show();

AlertDialog 可以展示Text文本对话框,带有EditText对话框,单选对话框,多选对话框,列表对话。
AlertDialog 的构造方法如下

 protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {super(context, resolveDialogTheme(context, 0));mWindow.alwaysReadCloseOnTouchAttr();setCancelable(cancelable);setOnCancelListener(cancelListener);mAlert = new AlertController(context, this, getWindow());}

首先我们看到了resolveDialogTheme(context, 0) 方法,我们看看,

static int resolveDialogTheme(Context context, int resid) {if (resid == THEME_TRADITIONAL) {return com.android.internal.R.style.Theme_Dialog_Alert;} else if (resid == THEME_HOLO_DARK) {return com.android.internal.R.style.Theme_Holo_Dialog_Alert;} else if (resid == THEME_HOLO_LIGHT) {return com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert;} else if (resid == THEME_DEVICE_DEFAULT_DARK) {return com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;} else if (resid == THEME_DEVICE_DEFAULT_LIGHT) {return com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog_Alert;} else if (resid >= 0x01000000) {   // start of real resource IDs.return resid;} else {TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,outValue, true);return outValue.resourceId;}}

咋这个方法中主要是按照条件设置主题,在AlertDialog中系统给我们提供了,几种主题可以选择,
THEME_TRADITIONAL:透明主题
THEME_HOLO_DARK:全黑色主题
THEME_HOLO_LIGHT:全亮色主题
THEME_DEVICE_DEFAULT_DARK:设备默认黑色主题
THEME_DEVICE_DEFAULT_LIGHT:设备默认亮色主题
在这里面我们看到了HOLO字样,这个其实是Android 系统在4.0后推出的新主题风格,这种主题分为黑色和亮色系和混合系列,在很多的控件都有这种主题可以选择,dialog只是其中一种而已。

else if (resid >= 0x01000000) {   // start of real resource IDs.return resid;}这里写代码片

如果不是系统的选择,那么就使用用户自己定义的主题,

else {TypedValue outValue = new TypedValue();context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,outValue, true);return outValue.resourceId;}

如果都不是上面的选择,也不是自定义的,那么系统将给出默认的主题,com.android.internal.R.attr.alertDialogTheme,这种属性主题最终通过AssetManager来查找的mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);然后将查找的内容存到TypedValue中,这个在上面的内容中也详细的讲过了。
接下来我们看到了mWindow.alwaysReadCloseOnTouchAttr();这个我们看名字也大概猜到了什么作用呢,应该是在点击窗口外面关闭窗口,的确是这样,这个是在Window中的一个抽象方法,具体的实现是PhoneWindow中的,在PhoneWindow中的实现是很简单的

 public void More ...alwaysReadCloseOnTouchAttr() {
3548        mAlwaysReadCloseOnTouchAttr = true;
3549    }

你看就是赋值而已,那么最终这个属性是在哪里用呢 我们继续跟踪,

if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
3368                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
3369            if (a.getBoolean(
3370                    R.styleable.Window_windowCloseOnTouchOutside,
3371                    false)) {
3372                setCloseOnTouchOutsideIfNotSet(true);
3373            }
3374        }

我们看到了,如果mAlwaysReadCloseOnTouchAttr ==true或者版本大于等于VERSION_CODES.HONEYCOMB(蜂巢,就是3…0版本啦),然后里面继续判断如果窗口风格设置Window_windowCloseOnTouchOutside了就setCloseOnTouchOutsideIfNotSet(true);而setCloseOnTouchOutsideIfNotSet(true) 方法是在Window中实现的

public void More ...setCloseOnTouchOutsideIfNotSet(boolean close) {
889         if (!mSetCloseOnTouchOutside) {
890             mCloseOnTouchOutside = close;
891             mSetCloseOnTouchOutside = true;
892         }
893     }

主要就是设置mCloseOnTouchOutside 和mSetCloseOnTouchOutside 为true。
构造方法接下来就是设置回调,和实例化AlertController了。

然后我们纵观整个AlertDialog,2点重要发现。
第一、AlertDialog只是个壳,真正的实现和管理控制都是通过AlertController来实现的,AlertController中的还有个AlertParams 类,AlertController主要是管理了相关的View和属性变量,很多方法都是对视图的初始化和设置,AlertParams 负责初始化工作例如里面有apply(AlertController dialog) 这个是初始化AlertDialog的方法,在AlertController的create中主要是它来实现的,

public void More ...apply(AlertController dialog) {
949             if (mCustomTitleView != null) {
950                 dialog.setCustomTitle(mCustomTitleView);
951             } else {
952                 if (mTitle != null) {
953                     dialog.setTitle(mTitle);
954                 }
955                 if (mIcon != null) {
956                     dialog.setIcon(mIcon);
957                 }
958                 if (mIconId != 0) {
959                     dialog.setIcon(mIconId);
960                 }
961                 if (mIconAttrId != 0) {
962                     dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
963                 }
964             }
965             if (mMessage != null) {
966                 dialog.setMessage(mMessage);
967             }
968             if (mPositiveButtonText != null) {
969                 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
970                         mPositiveButtonListener, null);
971             }
972             if (mNegativeButtonText != null) {
973                 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
974                         mNegativeButtonListener, null);
975             }
976             if (mNeutralButtonText != null) {
977                 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
978                         mNeutralButtonListener, null);
979             }
980             if (mForceInverseBackground) {
981                 dialog.setInverseBackgroundForced(true);
982             }
983             // For a list, the client can either supply an array of items or an
984             // adapter or a cursor
985             if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
986                 createListView(dialog);
987             }
988             if (mView != null) {
989                 if (mViewSpacingSpecified) {
990                     dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
991                             mViewSpacingBottom);
992                 } else {
993                     dialog.setView(mView);
994                 }
995             } else if (mViewLayoutResId != 0) {
996                 dialog.setView(mViewLayoutResId);
997             }
998 
999             /*
1000            dialog.setCancelable(mCancelable);
1001            dialog.setOnCancelListener(mOnCancelListener);
1002            if (mOnKeyListener != null) {
1003                dialog.setOnKeyListener(mOnKeyListener);
1004            }
1005            */
1006        }

例如还有,createListView(final AlertController dialog) 创建ListView,就是AlertDialog中的多选,单选,列表视图的初始化,所以AlertController结构还是很简单的。
第二、点就是AlertDialog一般都是通过内部类Builder来实例化的,通过点语法风格方便的写,AlertDialog的建造着模式是Android中很经典的。Builder 有create和show 和一大堆配置方法,写法结构很值得我们学习,建造者模式在Android其他地方也可以发现,例如BitmapFactory 图片工厂,ViewPropertyAnimator View的属性动画,

AlertDialog的介绍就是这么多了,AlertDialog很方面的实现警告对话框,也就是说仅仅是警告对话框,很多时候他的布局是死的,远远不能满足我们需求,我们都会继承Dialog来自定义自己的dialog,还可以对设置的View设置动画,提高用户体验,那就发挥大家的想象吧!


http://chatgpt.dhexx.cn/article/1LHdx7ft.shtml

相关文章

Dialog源码分析

1, 概述 Dialog(对话框)不仅可以显示信息,还可以和Activity界面进行交互,这种交互是阻塞式的. 继承Dialog的类有好几种,主要以AlertDialog为例来分析一下具体的原理。 2 实现 Dialog依附于Activity来实现,一般在acitivty中显示,因为Dialog的交互是阻塞式的,所以最好另开一线…

爬虫为什么会使用到HTTP代理?

在进行网页爬虫的时候使用HTTP代理&#xff0c;可以进行匿名抓取网页信息&#xff0c;爬取大数据等使用方向。HTTP代理我们很了解&#xff0c;但是你有了解过HTTP协议是什么吗&#xff1f; HTTP协议即超文本传输协议&#xff0c;是Internet上信息传输时使用最为广泛的一种简单…

爬虫笔记——全局代理下的requests写法

当PC挂了全局代理的时候&#xff0c;如果你的requests还像下面写的话&#xff0c;大概率会报错&#xff1a; 正确的写法是&#xff1a;

抓包:Android不走代理的请求

测试用例 测试应用有两个按钮&#xff0c;分别用 HttpURLConnection 和 Okhttp3 请求 https://www.baidu.com/。注意&#xff1a;两个请求都加入了 Proxy.NO_PROXY。 //HttpURLConnection请求https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel13812371237 public …

python爬虫——selenium+chrome使用代理

先看下本文中的知识点&#xff1a; python selenium库安装chrome webdirver的下载安装seleniumchrome使用代理进阶学习 搭建开发环境&#xff1a; selenium库chrome webdirver谷歌浏览器 >7.9 PS&#xff1a;安装了的同学可以跳过了接着下一步&#xff0c;没安装的同学跟…

python 爬虫 客户端_python爬虫

爬虫简介 什么是爬虫&#xff1f; 爬虫&#xff1a;就是抓取网页数据的程序。 HTTP和HTTPS HTTP协议(HyperText Transfer Protocol&#xff0c;超文本传输协议)&#xff1a;是一种发布和接收 HTML页面的方法。 HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)简单…

爬虫从入门到精通(7) | 常见反爬-代理IP的使用

目录 一、 为什么要使用代理IP&#xff1f; 二、代理IP的原理 三、代理IP的作用 四、代理IP的分类 ​ 1.根据代理的协议区分 2.根据匿名程度区分 五、在requests模块中如何设置代理 一、 为什么要使用代理IP&#xff1f; 使用自己本地的IP 利用爬虫技术获取某个网站信息的时…

爬虫手册06 代理池和代理服务器搭建

免费代理池的搭建 参考资料&#xff1a;Python3 网络爬虫开发实战&#xff08;第二版&#xff09; 一. 学习目标 &#xff1a; 参考《第9章 代理的使用》9.2节搭建自己私有的代理IP池。 二. 实验环境&#xff1a; 阿里云或腾讯云的服务器&#xff0c;我是在打折和新用户优…

我裂开了,教给他如何搭建和使用代理服务器,他居然用来做这么不正经的事(爬虫,代理ip)

代码是正经代码&#xff0c;但是程序员正不正经就不知道了。 ​ 前言 在使用爬虫对某些网站进行爬取时&#xff0c;为了不让网站发现我们的ip&#xff0c;模拟其他用户ip地址去访问网站。也就相当于间接的去访问网站&#xff0c;流程如图&#xff1a; 我们使用到代理服务器&…

Python爬虫进阶 - win和linux下selenium使用代理

目录 Windows selenium配置 下载地址 Chrome Chromedriver 版本对应关系 实践测试 操作元素 浏览器操作 获取元素信息 鼠标操作 实战demo selenium添加代理 Linux selenium配置 检查服务器环境 下载安装第三方库&#xff08;最简单版&#xff09; 实践测试 代码…

Python自助爬虫代理ip模块

短小无比的前言&#xff1a; 代理对于爬虫来说可是很重要的一环&#xff0c;尤其在对于大量数据的时候&#xff0c;一不小心自己ip挂了&#xff0c;要么你换网&#xff0c;要么你等个几小时恢复 之后你上网查阅了种种办法&#xff0c;跨越种种艰难险阻&#xff0c;数以堆计的bu…

python爬虫之requests库使用代理方式

在看这篇文章之前&#xff0c;需要大家掌握的知识技能&#xff1a; python基础html基础http状态码 让我们看看这篇文章中有哪些知识点&#xff1a; get方法post方法header参数&#xff0c;模拟用户data参数&#xff0c;提交数据proxies参数&#xff0c;使用代理进阶学习 安装…

爬虫代理IP哪家好?

前言 随着大数据时代的到来,爬虫已经成了获取数据的必不可少的方式,做过爬虫的想必都深有体会,爬取的时候莫名其妙 IP 就被网站封掉了,毕竟各大网站也不想自己的数据被轻易地爬走。 对于爬虫来说,为了解决封禁 IP 的问题,一个有效的方式就是使用代理,使用代理之后可以让…

Python爬虫基础-使用代理

为什么需要代理&#xff1f; 我们爬取数据的时候&#xff0c;开始可以正常爬取&#xff0c;但是过了一段时间&#xff0c;网站可能就会提示“您的IP访问频率过高”&#xff0c;然后就无法正常访问网站。 这是因为网站采取了反爬策略&#xff0c;某个ip访问频率超过一个阈值后&…

Python爬取代理IP

在一些网页的内容爬取过程中&#xff0c;有时候在单位时间内如果我们发送的请求次数过多&#xff0c;网站就可能会封掉我们的IP地址&#xff0c;这时候为了保证我们的爬虫的正常运行&#xff0c;我们就要使用代理IP。 下面来介绍如何构建自己的IP池。 我们用快代理来获取代理i…

流量数据分析的方法学习

1、看数字和趋势&#xff08;以电商网站为例&#xff09; 2、维度分解 3、用户分群&#xff08;又叫用户画像&#xff09; 4、转化漏斗 5、行为轨迹 关注行为轨迹&#xff0c;是为了真实了解用户行为。通过大数据手段&#xff0c;还原用户的行为轨迹&#xff0c;有助于增长团队…

恶意流量分析(一)

在分析恶意样本时&#xff0c;样本可能包含网络行为&#xff0c;比如样本从C2服务器上请求下载后续病毒文件。所以对于在病毒分析的角度&#xff0c;对恶意流量的分析也是不可避免的。在这里通过恶意流量习题&#xff08;malware-traffic-analysis&#xff09;对恶意流量进行入…

APP流量分析

分析参数 发送流量、接收流量 流量统计方法 抓包&#xff1a;干扰因素多&#xff0c;其他APP、安卓自带TCP收发长度统计功能 #找到包名 adb shell pm list packsges -3 #找到UID adb shell ps | grep <包名> #找到目录 adb shell cat /proc/9045<uid>/status #…

如何进行网站流量分析(一)

如何进行网站流量分析&#xff08;一&#xff09; 流量分析整体来说是一个内涵非常丰富的体系&#xff0c;整体过程是一个金字塔结构&#xff1a; 金字塔的顶部是网站的目标&#xff1a;投资回报率&#xff08;ROI&#xff09;。 网站流量分析模型举例 1 网站流量质量分析&a…

流量与日志分析

文章目录 1.流量与日志分析1.1系统日志分析1.1.1window系统日志与分析方法1.1.2linux 系统日志与分析方法 1.2 web日志分析iis 日志分析方法apache日志分析**access_log****error_log** nginx日志分析tomcat 日志分析主流日志分析工具使用 1.流量与日志分析 日志&#xff0c;是…