自定義AlertDialog最佳實踐

MellisaSmal 7年前發布 | 11K 次閱讀 安卓開發 Android開發 移動開發

AlertDialog是每一個App所必須的控件,在Android4.0之前不可否認官方正版的Dialog很不美觀,但是Android4.0之后的holo風格的Dialog已經可以上的了廳堂了,再后來的Material Design設計風格的擬物扁平化Dialog已經是給人耳目一新的感覺。雖然官方UI標準愈加完善,可是部分UI設計師依然是按照蘋果iOS的規范設計App的UI,完全忽略不同平臺UI包括交互上的差異性,作為開發者也只好苦逼的遵循UI設計的規范,結果就是Android版本的Dialog多數情況下是以自定義UI風格的方式呈現。系統官方的AlertDialog使用了建造者模式,并且可以鏈式調用相當方便,所以這次從系統中剝離了AlertDialog源碼并進行部分改動以便適用于不同風格。

自定義AlertDialog支持如下方式:

  • 系統AlertDialog怎么使用該AlertDialog就怎么使用;
  • 支持自定義樣式和主題;
  • 支持自定義AlertDialog布局文件;
  • 支持自定義單選復選列表彈框;
  • 支持功能擴展。

AlertDialog簡介

一個標準AlertDialog分為三個區域:標題區、內容區和按鈕區,并且每一個區域都是可選部分。構建一個AlertDialog是使用的靜態內部類Builder,三個區域需要什么就build什么,這就是建造者模式的好處。

一些常用的方法如下:

  • Builder () 構造方法,默認一個參數的是Theme.Holo.Light主題,可以說設置兩個參數,第二個參數可以傳入主題
  • setTitle () 設置標題欄文本;
  • setIcon () 設置標題欄icon;
  • setIconAttribute () 設置特定主題下的icon;
  • setCustomTitle () 設置自定義標題欄;
  • setMessage () 設置彈框內容區域文本;
  • setItems () 設置彈框列表;
  • setSingleChoiceItems () 設置單選模式列表;
  • setMultiChoiceItems () 設置復選模式列表;
  • setNegativeButton() 設置左側按鈕;
  • setNeutralButton () 設置中間按鈕;
  • setPositiveButton () 設置右側按鈕;
  • setView () 設置自定義內容區域View;
  • create() 創建一個AlertDialog;
  • show () 創建并顯示一個AlertDialog。

AlertDialog的基本使用

創建一個標準AlertDialog

new Builder(this)
 .setTitle("title")
 .setMessage("hello world")
 .setPositiveButton("確定", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).setNeutralButton("中間", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).setNegativeButton("取消", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).show();

創建一個無標題欄AlertDialog

只需要將上述標準Dialog中setTitle方法剔除就可以了。

創建一個深色主題AlertDialog

AlertDialog在創建的時候默認是一個淺色Theme.Holo.Light主題,在構造方法中就已經設置了,如果想要設置深色主題,可以使用兩個參數的構造方法。

protected AlertDialog(Contextcontext) {
 this(context, 0);
}
 
protected AlertDialog(Contextcontext, int theme) {
 super(context, resolveDialogTheme(context, theme));
 mAlert = new AlertController(context, this, getWindow());
}
static int resolveDialogTheme(Contextcontext, int resId) {
 if (resId == 0) {
 return R.style.DialogHoloLight;
 } else {
 return resId;
 }
}

首先創建一個深色主題繼承一個默認系統已有的深色Dialog主題。

<stylename="DialogDark" parent="@android:style/Theme.DeviceDefault.Dialog">
 <itemname="android:alertDialogIcon">@drawable/ic_view_headline_white_24dp</item>
 <itemname="layout">@layout/alert_dialog_dark</item>
</style>

這里我們將dialog的布局文件也重新設置了一個,并且設置了該主題下標題欄icon,代碼如下:

new Builder(this,R.style.DialogDark)
 .setTitle("title")
 .setIconAttribute(android.R.attr.alertDialogIcon)
 .setMessage("hello world")
 .setPositiveButton("確定", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).setNeutralButton("中間", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).setNegativeButton("取消", new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int which) {
 dialog.dismiss();
 }
 }).show();

創建一個類似iOS風格的AlertDialog

想要創建iOS風格的Dialog主要是布局文件的排版,圓角布局或者從底部滑入動畫,該demo并沒有加入動畫。先創建一個iOS主題:

<stylename="DialogIOS" parent="@android:style/Theme.Holo.Light.Dialog">
 <itemname="android:alertDialogIcon">@drawable/ic_view_headline_black_24dp</item>
 <itemname="layout">@layout/alert_ios</item>
 <itemname="android:windowBackground">@drawable/bg_dialog</item>
</style>

AlertDialog的創建方式跟上面深色主題類似,只需要在構造方法傳入主題即可:

new Builder(this,R.style.DialogIOS)
...

如果創建類似QQ從底部滑入動畫,只需要在主題中設置一個轉場動畫:

<stylename="DialogIOS" parent="@android:style/Theme.Holo.Light.Dialog">
 <itemname="android:alertDialogIcon">@drawable/ic_view_headline_black_24dp</item>
 <itemname="layout">@layout/alert_ios</item>
 <itemname="android:windowBackground">@drawable/bg_dialog</item>
 <itemname="android:windowAnimationStyle">@style/DrawerAnim</item>
</style>

創建一個列表AlertDialog

創建列表對話框,包括單復選模式實際上都是單行文本模式列表,如果想要支持更復雜列表,可以更改源碼或者傳入一個自定義View。

new Builder(this)
 .setTitle("title")
 .setIconAttribute(android.R.attr.alertDialogIcon)
 .setItems(getData(), new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int position) {
 dialog.dismiss();
 }
 }).show();

創建單復選列表AlertDialog

不可否認系統級別的單復選icon很不好看,所以這里使用了自定義布局方式來更改系統內置默認的布局,內部文本列表使用的是CheckedTextView,這個控件一般很少用到,而這里需要更改icon就是需要該控件的checkMark屬性。為checkMark屬性設置一個選擇器,btn_radio_right選擇器如下:

<selectorxmlns:android="http://schemas.android.com/apk/res/android">
    <itemandroid:state_checked="false" android:drawable="@color/transparent"/>
    <itemandroid:state_checked="true" android:drawable="@drawable/ic_checked"/>
</selector>

單選模式的布局文件custom_dialog_singlechoice.xml如下:

<CheckedTextViewxmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="?android:attr/textColorAlertDialogListItem"
    android:gravity="center_vertical"
    android:paddingStart="16dip"
    android:paddingEnd="16dip"
    android:checkMark="@drawable/btn_radio_light"
    android:ellipsize="marquee"
/>

復選模式布局實現方式跟單選模式類似,這里就不貼出來了。

    //單選模式
    new Builder(this)
 .setTitle("title")
 .setIconAttribute(android.R.attr.alertDialogIcon)
 .setSingleChoiceItems(getData(), 0, new OnClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int position) {
 dialog.dismiss();
 }
 }).show();
 
//復選模式
new Builder(this)
 .setTitle("title")
 .setIconAttribute(android.R.attr.alertDialogIcon)
 .setMultiChoiceItems(getData(), null, new OnMultiChoiceClickListener() {
 @Override
 public void onClick(DialogInterfacedialog, int position, boolean isCheked) {

 }
 }).show();

設置自定義View的AlertDialog

自定義View很簡單就是使用setView方法,傳入一個自定義的View,在開發中自定義View使用頻率還是很高的。

new Builder(this)
 .setTitle("title")
 .setIconAttribute(android.R.attr.alertDialogIcon)
 .setView(View.inflate(this, R.layout.custom_layout, null)).show();

AlertDialog的銷毀以及事件的監聽

setCancelable(boolean cancelable)中cancelable默認值是true,這時候點擊彈框外部區域或者手機返回鍵Dialog默認會銷毀,反之,點擊后Dialog不會銷毀。

如果在Dialog銷毀時還想處理一些邏輯,就需要借助DialogInterface中如下兩個監聽器:

interface OnCancelListener {
 public void onCancel(DialogInterfacedialog);
}
 
interface OnDismissListener {
 public void onDismiss(DialogInterfacedialog);
}

Dialog銷毀可以調用兩個事件,分別對應這兩個方法cancel和dismiss,一般情況下開發者不必在乎它們倆的差異性,當想銷毀Dialog的時候,直接調用dismiss或者cancel方法,在cancel方法中實際上也調用了dismiss方法。

public void cancel() {
 if (!mCanceled && mCancelMessage != null) {
 mCanceled = true;
 // Obtain a new message so this dialog can be re-used
 Message.obtain(mCancelMessage).sendToTarget();
 }
 dismiss();
}

從這里可以看出如果我們設置了setOnDismissListener,調用cancel和dismiss方法都會出發該監聽器,但是如果設置setOnCancelListener則只會在使用cancel方法的時候出發,差別就在這里。如果Dialog被用戶顯式的銷毀掉,這將在用戶按下”back”鍵時,或者Dialog顯式的調用cancel()(按下Dialog的cancel按鈕)時發生,當一個Dialog被銷毀時, OnDismissListener將仍然被通知, 但如果你希望在對話框被顯示銷毀(而不是正常解除)時被通知,則你應該使用setOnCancelListener()注冊一個DialogInterface.OnCancelListener。

AlertDialog全屏

AlertDialog默認是不全屏的,如果想設置全屏可以使用如下方式:

Viewview=View.inflate(this, R.layout.layout_custom01, null);
//必須設置該屬性
view.setMinimumWidth(screenWidth);
AlertDialog.Builderbuilder = new Builder(this)
 .setTitle("title").setView(view);
AlertDialogdialog=builder.create();
WindowdialogWindow = dialog.getWindow();
//設置屏幕底部居左對齊
dialogWindow.setGravity(Gravity.LEFT | Gravity.BOTTOM);
WindowManager.LayoutParamslp = dialogWindow.getAttributes();
lp.x = 0;
lp.y = 0;
dialogWindow.setAttributes(lp);
dialog.show();

小結

AlertDialog簡單使用基本先到這里,有關AlertDialog代碼實現機制在以后的文章中再做解析。官方不建議開發者直接使用Dialog來創建彈出框,所以提供了AlertDialog,但是Android3.0以后官方又提供了一個新的類DialogFragment,說DialogFragment擁有比AlertDialog更優的用戶體驗,因為它可以跟Fragment生命周期綁定,有關DialogFragment使用后續文章也會簡單介紹一下,個人認為以簡單實用為上,既然AlertDialog可以滿足平常開發需要,非特殊需求我還是選擇AlertDialog。開發中有時候會遇到一些從底部上滑進入的彈框,如購物車或者支付寶支付信息界面,這種情況下可以在AlertDialog使用自定義動畫滿足需求,但是由于這種交互非常常見,所以Google官方又提供了一個吊炸天的控件BottomSheetDialog,最先引入大概是在support v23.2支持包中,也同樣有跟Fragment綁定的工具是BottomSheetDialogFragment,該控件算是彌補了SlidingDrawer、DrawerLayout、PopupWindow以及AlertDialog的在底部彈框交互上的不足,走到這里我只想說一個詞:”shit”,為什么這些控件不早一天出現。

 

 

來自:http://www.sunnyang.com/642.html

 

 本文由用戶 MellisaSmal 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!