在對AlertDialog進(jìn)行封裝之前痒玩,我們有必要先了解一下AlertDialog的源碼實(shí)現(xiàn)昆庇。一般情況下一行代碼我們就可以創(chuàng)建一個彈框固歪,如下所示:
new AlertDialog.Builder(this).create().show();
下面我們就一段段來分析汹胃,首先點(diǎn)擊進(jìn)去看下AlertDialog
public class AlertDialog extends AppCompatDialog implements DialogInterface {
...
}
public class AppCompatDialog extends Dialog implements AppCompatCallback {
...
}
也就是說我們的AlertDialog是繼承自AppCompatDialog上沐,而AppCompatDialog是繼承自我們的Dialog皮服,這個關(guān)系知道是怎么一回事就可以了,接下來進(jìn)入到Builder里面参咙。
public static class Builder {
private final AlertController.AlertParams P;
private final int mTheme;
/**
* Creates a builder for an alert dialog that uses the default alert
* dialog theme.
* <p>
* The default alert dialog theme is defined by
* {@link android.R.attr#alertDialogTheme} within the parent
* {@code context}'s theme.
*
* @param context the parent context
*/
public Builder(@NonNull Context context) {
this(context, resolveDialogTheme(context, 0));
}
/**
* Creates a builder for an alert dialog that uses an explicit theme
* resource.
* <p>
* The specified theme resource ({@code themeResId}) is applied on top
* of the parent {@code context}'s theme. It may be specified as a
* style resource containing a fully-populated theme, such as
* {@link R.style#Theme_AppCompat_Dialog}, to replace all
* attributes in the parent {@code context}'s theme including primary
* and accent colors.
* <p>
* To preserve attributes such as primary and accent colors, the
* {@code themeResId} may instead be specified as an overlay theme such
* as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
* override only the window attributes necessary to style the alert
* window as a dialog.
* <p>
* Alternatively, the {@code themeResId} may be specified as {@code 0}
* to use the parent {@code context}'s resolved value for
* {@link android.R.attr#alertDialogTheme}.
*
* @param context the parent context
* @param themeResId the resource ID of the theme against which to inflate
* this dialog, or {@code 0} to use the parent
* {@code context}'s default alert dialog theme
*/
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
mTheme = themeResId;
}
...
}
可以看到龄广,我們的Builder并不是直接在AlertDialog里面去創(chuàng)建方法,而是創(chuàng)建了一個靜態(tài)內(nèi)部類Builder蕴侧,為什么會這樣設(shè)計择同,了解設(shè)計模式的朋友可能會明白,這是用了建造者設(shè)計模式净宵,便于為最后生成的AlertDialog進(jìn)行個性化定制敲才。在Builder里面有兩個構(gòu)造方法:
public Builder(@NonNull Context context) {
this(context, resolveDialogTheme(context, 0));
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
mTheme = themeResId;
}
注意看裹纳,第一個構(gòu)造方法最終會執(zhí)行到第二個構(gòu)造方法里面,那么兩個構(gòu)造方法的區(qū)別是什么呢紧武?注意看第二個構(gòu)造方法的第二個參數(shù)themeResId剃氧,意思很明顯AlertDialog的設(shè)置的主題,所以這里我們就很好去理解了阻星,即:
在創(chuàng)建AlertDialog的Builder的時候朋鞍,如果我們不給AlertDialog指定主題,則系統(tǒng)會默認(rèn)使用系統(tǒng)自帶的主題樣式妥箕。
繼續(xù)研究構(gòu)造函數(shù)滥酥,里面有一個很重要的參數(shù):P。這個P是在Builder里面聲明的一個靜態(tài)類:
public static class Builder {
private final AlertController.AlertParams P;
private final int mTheme;
在調(diào)用構(gòu)造函數(shù)的時候會實(shí)例化P矾踱。那么這個P又是什么呢恨狈?點(diǎn)進(jìn)去看看:
class AlertController {
...
public static class AlertParams {
public final Context mContext;
public final LayoutInflater mInflater;
}
}
可以很明顯的看到疏哗,P是AlertController類里面的一個靜態(tài)內(nèi)部類呛讲,我們先知道這個概念,后面再詳細(xì)介紹返奉。
接下來我們進(jìn)入到create方法
public AlertDialog create() {
// We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
// so we always have to re-set the theme
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
我們一行一行來看:
首先創(chuàng)建了一個AlertDialog的實(shí)例
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, resolveDialogTheme(context, themeResId));
mAlert = new AlertController(getContext(), this, getWindow());
}
在這里創(chuàng)建了AlertController這個類贝搁,在AlertController這個構(gòu)造方法里面主要是去設(shè)置一些屬性
public AlertController(Context context, AppCompatDialog di, Window window) {
mContext = context;
mDialog = di;
mWindow = window;
mHandler = new ButtonHandler(di);
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
R.attr.alertDialogStyle, 0);
mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);
mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
mSingleChoiceItemLayout = a
.getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
mButtonIconDimen = a.getDimensionPixelSize(R.styleable.AlertDialog_buttonIconDimen, 0);
a.recycle();
/* We use a custom title so never request a window title */
di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
繼續(xù)向下看:
P.apply(dialog.mAlert);
public void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
if (mMessage != null) {
dialog.setMessage(mMessage);
}
if (mPositiveButtonText != null || mPositiveButtonIcon != null) {
dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
mPositiveButtonListener, null, mPositiveButtonIcon);
}
if (mNegativeButtonText != null || mNegativeButtonIcon != null) {
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
mNegativeButtonListener, null, mNegativeButtonIcon);
}
if (mNeutralButtonText != null || mNeutralButtonIcon != null) {
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
mNeutralButtonListener, null, mNeutralButtonIcon);
}
// For a list, the client can either supply an array of items or an
// adapter or a cursor
if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
createListView(dialog);
}
if (mView != null) {
if (mViewSpacingSpecified) {
dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
mViewSpacingBottom);
} else {
dialog.setView(mView);
}
} else if (mViewLayoutResId != 0) {
dialog.setView(mViewLayoutResId);
}
/*
dialog.setCancelable(mCancelable);
dialog.setOnCancelListener(mOnCancelListener);
if (mOnKeyListener != null) {
dialog.setOnKeyListener(mOnKeyListener);
}
*/
}
首先在AlertController類里面的靜態(tài)內(nèi)部類AlertParams里調(diào)用了apply方法,而在這個方法里面其實(shí)不難看出是給我們的AlertController里面的屬性賦值芽偏,而值是來自于AlertParams聲明的屬性雷逆。其他的幾行代碼很好理解,就是給我們的AlertDialog設(shè)置監(jiān)聽污尉,最后返回AlertDialog實(shí)例膀哲,最后調(diào)用show方法進(jìn)行展示即可。
到這里被碗,AlertDialog的源碼我們大致走了一遍某宪,我們可以大致梳理一下其中幾個關(guān)鍵類的關(guān)系。
AlertController:顧名思義AlertDialog的控制器類锐朴,主要是用于處理AlertDialog的各種系統(tǒng)內(nèi)置屬性兴喂,持有AlertDialog的實(shí)例。
AlertParams:AlertController的內(nèi)部類焚志,是一個static的類衣迷,聲明各種通用的屬性,比如文本酱酬,圖片壶谒,視圖等等。在執(zhí)行apply方法的時候膳沽,將獲取的值傳遞給AlertController汗菜。
Builder:AlertDialog的內(nèi)部類泼差,是一個static的類,用于構(gòu)建AlertDialog呵俏。同時持有AlertController.AlertParams的實(shí)例堆缘。
了解了這幾個類的主要作用,接下來我們就通過一個具體的實(shí)例來看看AlertDialog到底是怎么運(yùn)行的普碎,簡單例子:
val dialog = AlertDialog.Builder(this)
dialog.setIcon(R.mipmap.ic_launcher)
dialog.setTitle("對話框")
dialog.setMessage("這是一個普通的對話框")
dialog.show()
一步一步分析:
val dialog = AlertDialog.Builder(this)
調(diào)用AlertDialog內(nèi)部類Builder類的構(gòu)造方法吼肥,實(shí)例化AlertController.AlertParams類
dialog.setIcon(R.mipmap.ic_launcher)
dialog.setTitle("對話框")
dialog.setMessage("這是一個普通的對話框")
給AlertController.AlertParams類里面的圖標(biāo)、標(biāo)題麻车、信息設(shè)置具體的值缀皱。
dialog.show()
這個會是分析的重點(diǎn):
(1)首先會去執(zhí)行AlertDialog.Builder類里面的show()方法
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
(2)進(jìn)入create方法
@NonNull
public AlertDialog create() {
// We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
// so we always have to re-set the theme
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
首先創(chuàng)建AlertDialog的實(shí)例,然后最終將這個實(shí)例返回动猬,P是在Builder的時候就會被實(shí)例化的AlertController.AlertParams實(shí)例啤斗。接著執(zhí)行AlertController.AlertParams的apply方法,在這里將我們賦值的title赁咙、message和icon賦值給AlertController钮莲,也可以理解為對AlertController進(jìn)行封裝,接下來繼續(xù)對AlertDialog的各種回調(diào)進(jìn)行監(jiān)聽彼水。
(2)繼續(xù)執(zhí)行到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);
} else {
// Fill the DecorView in on any configuration changes that
// may have occured while it was removed from the WindowManager.
final Configuration config = mContext.getResources().getConfiguration();
mWindow.getDecorView().dispatchConfigurationChanged(config);
}
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();
boolean restoreSoftInputMode = false;
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
l.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
restoreSoftInputMode = true;
}
mWindowManager.addView(mDecor, l);
if (restoreSoftInputMode) {
l.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
}
mShowing = true;
sendShowMessage();
}
首先判斷有沒有被創(chuàng)建過崔拥,也可以理解為AlertDialog所對應(yīng)的視圖部分,如果沒有凤覆,執(zhí)行
dispatchOnCreate(null)方法
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
在這個方法里面會去執(zhí)行onCreate(savedInstanceState)方法链瓦,所以我們找到AlertDialog的onCreate(savedInstanceState)方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
在這個方法里面最終會執(zhí)行到AlertController的installContent方法
public void installContent() {
final int contentView = selectContentView();
mDialog.setContentView(contentView);
setupView();
}
這個方法比較重要,首先是通過selectContentView()獲取到我們的View盯桦,然后將View添加到mDialog里面去慈俯,最終執(zhí)行setupView()方法對這個視圖賦值,最后調(diào)用
mWindowManager.addView(mDecor, l);
將頁面進(jìn)行展示出來拥峦。大概的時序圖為:
了解了原理之后贴膘,我們就開始進(jìn)入實(shí)戰(zhàn)部分了
首先類的結(jié)構(gòu)如下所示:
首先創(chuàng)建AlertController類,主要的功能有:
(1)持有AlertDialog的實(shí)例
(2)創(chuàng)建內(nèi)部類AlertParams
class AlertController {
private AlertDialog mDialog;
private Window mWindow;
private DialogViewHelper mViewHelper;
public AlertController(AlertDialog dialog, Window window) {
this.mDialog = dialog;
this.mWindow = window;
}
public AlertDialog getDialog() {
return mDialog;
}
public Window getWindow() {
return mWindow;
}
// 設(shè)置TextView
public void setText(int viewId, CharSequence text) {
mViewHelper.setText(viewId, text);
}
public void setViewHelper(DialogViewHelper viewHelper) {
this.mViewHelper = viewHelper;
}
// 設(shè)置點(diǎn)擊事件
public void setOnClickListener(int viewId, View.OnClickListener listener) {
mViewHelper.setOnClickListener(viewId, listener);
}
public <T extends View> T getView(int viewId) {
return mViewHelper.getView(viewId);
}
public static class AlertParams {
public Context mContext;
public int mThemeResId;
// 點(diǎn)擊空白區(qū)是否可以取消
public boolean mCancelable = true;
// dialog cancel監(jiān)聽
public DialogInterface.OnCancelListener mOnCancelListener;
// dialog dismiss 監(jiān)聽
public DialogInterface.OnDismissListener mOnDismissListener;
// dialog 按鍵監(jiān)聽
public DialogInterface.OnKeyListener mOnKeyListener;
// 布局View
public View mView;
// 布局LayoutId
public int mViewLayoutResId;
// 存放所有的文本
public SparseArray<CharSequence> mTextArray = new SparseArray<>();
// 存放所有的點(diǎn)擊事件
public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
// 設(shè)置寬度
public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
// 設(shè)置動畫
public int mAnimations = 0;
// 設(shè)置從底部彈出
public int mGravity = Gravity.CENTER;
// 設(shè)置高度
public int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
public AlertParams(Context context, int themeResId) {
this.mContext = context;
this.mThemeResId = themeResId;
}
// 綁定和設(shè)置參數(shù)
public void apply(AlertController mAlert) {
DialogViewHelper viewHelper = null;
// 設(shè)置布局
if (mViewLayoutResId != 0) {
viewHelper = new DialogViewHelper(mContext, mViewLayoutResId);
}
if (mView != null) {
viewHelper = new DialogViewHelper();
viewHelper.setContentView(mView);
}
mAlert.setViewHelper(viewHelper);
if (viewHelper == null) {
throw new IllegalArgumentException("請設(shè)置布局setContentView()");
}
// 給Dialog設(shè)置布局
mAlert.getDialog().setContentView(viewHelper.getContentView());
// 設(shè)置文本
int textArraySize = mTextArray.size();
for (int i = 0; i < textArraySize; i++) {
viewHelper.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
}
// 設(shè)置點(diǎn)擊事件
int textClickSize = mClickArray.size();
for (int i = 0; i < textClickSize; i++) {
viewHelper.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
}
Window window = mAlert.getWindow();
window.setGravity(mGravity);
if (mAnimations != 0)
window.setWindowAnimations(mAnimations);
WindowManager.LayoutParams params = window.getAttributes();
params.width = mWidth;
params.height = mHeight;
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.setAttributes(params);
}
}
}
創(chuàng)建AlertDialog繼承自Dialog事镣,其包含的主要內(nèi)容有:
(1)持有AlertController的實(shí)例
(2)創(chuàng)建內(nèi)部類Builder
public class AlertDialog extends Dialog {
final AlertController mAlert;
public AlertDialog(@NonNull Context context) {
this(context, 0);
}
public AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, themeResId);
mAlert = new AlertController(this, getWindow());
}
// 設(shè)置TextView
public void setText(int viewId, CharSequence text) {
mAlert.setText(viewId, text);
}
// 設(shè)置點(diǎn)擊事件
public void setOnClickListener(int viewId, View.OnClickListener listener) {
mAlert.setOnClickListener(viewId, listener);
}
public <T extends View> T getView(int viewId) {
return mAlert.getView(viewId);
}
public static class Builder {
private final AlertController.AlertParams P;
public Builder(@NonNull Context context) {
this(context, R.style.MyDialog);
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
P = new AlertController.AlertParams(context, themeResId);
P.mThemeResId = themeResId;
}
// 設(shè)置View
public Builder setContentView(int layoutResId) {
P.mView = null;
P.mViewLayoutResId = layoutResId;
return this;
}
// 設(shè)置View
public Builder setContentView(View view) {
P.mView = view;
P.mViewLayoutResId = 0;
return this;
}
// 設(shè)置文本
public Builder setText(int viewId, CharSequence text) {
P.mTextArray.put(viewId, text);
return this;
}
// 設(shè)置全屏
public Builder fullWith() {
P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
return this;
}
// 設(shè)置動畫
public Builder fromBottom(boolean isAnimation) {
if (isAnimation) {
P.mAnimations = R.style.dialog_from_bottom_anim;
}
P.mGravity = Gravity.BOTTOM;
return this;
}
// 設(shè)置Dialog的寬高
public Builder setWidthAndHeight(int width, int height) {
P.mWidth = width;
P.mHeight = height;
return this;
}
// 設(shè)置默認(rèn)動畫
public Builder addDefaultAnimation() {
P.mAnimations = R.style.dialog_scale_anim;
return this;
}
// 添加動畫
public Builder setAnimations(int styleAnimation) {
P.mAnimations = styleAnimation;
return this;
}
// 設(shè)置點(diǎn)擊事件
public Builder setOnClickListener(int viewId, View.OnClickListener listener) {
P.mClickArray.put(viewId, listener);
return this;
}
public Builder setCancelable(boolean cancelable) {
P.mCancelable = cancelable;
return this;
}
public Builder setOnCancelListener(OnCancelListener onCancelListener) {
P.mOnCancelListener = onCancelListener;
return this;
}
public Builder setOnDismissListener(OnDismissListener onDismissListener) {
P.mOnDismissListener = onDismissListener;
return this;
}
public Builder setOnKeyListener(OnKeyListener onKeyListener) {
P.mOnKeyListener = onKeyListener;
return this;
}
public AlertDialog create() {
final AlertDialog dialog = new AlertDialog(P.mContext, P.mThemeResId);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
public AlertDialog show() {
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}
最后附上我們的DialogViewHelper處理類
public class DialogViewHelper {
private View mContentView = null;
private SparseArray<WeakReference<View>> mViews;
public DialogViewHelper(Context mContext, int mViewLayoutResId) {
this();
mContentView = LayoutInflater.from(mContext).inflate(mViewLayoutResId, null);
}
public DialogViewHelper() {
mViews = new SparseArray<>();
}
// 設(shè)置布局
public void setContentView(View mView) {
this.mContentView = mView;
}
// 設(shè)置TextView
public void setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
if (tv != null) {
tv.setText(text);
}
}
// 設(shè)置點(diǎn)擊事件
public void setOnClickListener(int viewId, View.OnClickListener listener) {
View tv = getView(viewId);
if (tv != null) {
tv.setOnClickListener(listener);
}
}
public <T extends View> T getView(int viewId) {
WeakReference<View> viewReference = mViews.get(viewId);
View view = null;
if (viewReference != null) {
view = viewReference.get();
}
if (view == null) {
view = mContentView.findViewById(viewId);
if (view != null)
mViews.put(viewId, new WeakReference<>(view));
}
return (T) view;
}
// 獲取Content內(nèi)容的View
public View getContentView() {
return mContentView;
}
}
到這里步鉴,一個根據(jù)AlertDialog源碼搭建的通用型AlertDialog就搭建完成啦。
最后附上一個使用例子:
new AlertDialog.Builder(this)
.setContentView(R.layout.item_permission)
.setText(R.id.item_delete_content, getString(R.string.sure_refruse_permission))
.setOnClickListener(R.id.item_delete_cancel, new NoDoubleClickListener() {
@Override
public void onNoDoubleClick(View v) {
if (mXXPermissions != null && mXXPermissions.isShowing()) {
mXXPermissions.dismiss();
}
Util.checkPermission(MainActivity.this);
}
})
.setOnClickListener(R.id.item_delete_sure, new NoDoubleClickListener() {
@Override
public void onNoDoubleClick(View v) {
if (mXXPermissions != null && mXXPermissions.isShowing()) {
mXXPermissions.dismiss();
}
requestPermission();
}
})
.setCancelable(false)
.fullWith()
.create();