Android入門——AlertDialog、ProgressDialog及自定義Dialog總結
引言
在我們程式開發中,使用者體驗始終是一項十分重要的指標,通常為了良好的使用者體驗,在確認一些敏感或者資料操作更新之前允許客戶反悔即讓使用者擁有更多的自主性,而Android開發中是藉助對話方塊Dialog系、Popupwindow和Dialog樣式的Activity來實現。
一、Dialog及其衍生類
Android裡Dialog作為對話方塊系的基類,我們一般不會直接去使用Dialog而是使用他的子類,比如說AlertDialog, DatePickerDialog, ProgressDialog, TimePickerDialog,CharacterPickerDialog,MediaRouteChooserDialog, MediaRouteControllerDialog, Presentation,其中前四者使用比較頻繁,使用方法也大同小異,接下來就以AlertDialog為例總結下。
1、AlertDialog
1.1、AlertDialog的部分原始碼結構
AlertDialog 其實也是採用了建造者模式,AlertDialog是我們最終想要生產得到的產品,但是我們不能直接通過它的構造方法來new,同時我們看到AlertDialog裡有一個內部類Builder,Builder內部類裡封裝了一系列的方法用於初始化,然後再通過Builder內部類裡有一個create方法返回Dialog
public class AlertDialog extends Dialog implements DialogInterface {
private AlertController mAlert;
protected AlertDialog(Context context) {
this(context, 0);
}//請注意AlertDialog的構造方法是protected
protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
this(context, 0);
setCancelable(cancelable);
setOnCancelListener(cancelListener);
}
...
protected AlertDialog(Context context, @StyleRes int themeResId) {
this(context, themeResId, true);
}
public static class Builder {
private final AlertController.AlertParams P;
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
public Builder setIcon(Drawable icon) {
P.mIcon = icon;
return this;
}
/**
* @param context the parent context
*
*/
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
...
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
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;
}
}
}
1.2、使用系統AlertDialog
我們都知道Android中的所有的View,首先是一個class,要使用一個class我們首先得先得到他的物件(初學的時候照著教材敲了彈出對話方塊的視窗,但是對於為什麼是用builder而不是直接new一無所知,曾經困惑了蠻久),然後進行一系列的初始化,最後再顯示。步驟簡記:一系列初始化Builder——>create得到Dialog物件——>顯示
1.2.1、setMessage一般對話方塊
private void showDialog(){
/**需要注意的是構造AlertDialog.Builder物件時傳入的引數可以理解成*說依附的父容器,比如說在Activity上顯示就傳入this,在Fragment上顯示就傳入*Fragment的父級即getActivity()至於是否都是父級的,我還得需要測試下,比如說巢狀Fragment的,測試完畢之後再行更新!!!!
*/
AlertDialog.Builder builder= new AlertDialog.Builder(this);//得到Builder物件
/*一系列初始化工作*/
builder.setIcon(R.mipmap.bcg)
.setTitle(R.string.dialog_title)
.setMessage(R.string.dialog_msg)
.setPositiveButton(R.string.dialog_btn_positive,new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this,"Yes",Toast.LENGTH_LONG).show();
}
})
.setNegativeButton(R.string.dialog_btn_negative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this,"No",Toast.LENGTH_LONG).show();
}
})
.create()/*得到dialog*/
.show();/*居中顯示於Activity*/
}
也可以把監聽器單獨封裝成一個類
class DialogBtnClicklistener implements DialogInterface.OnClickListener{
@Override
public void onClick(DialogInterface dialog, int which){
switch(which){
case Dialog.BUTTON_POSITIVE
case Dialog.BUTTON_NEGATIVE
...
}
}
}
1.2.2、setItems列表對話方塊
private void showDialog(){
final String items[]={"Dota2","Dota","War3"};
AlertDialog.Builder builder= new AlertDialog.Builder(this);//得到DIalog物件
builder.setIcon(R.mipmap.bcg)
.setTitle(R.string.dialog_title)
.setItems(items, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, items[which], Toast.LENGTH_LONG).show();
}
})
.create()
.show();
}
1.2.3 setSingleChoiceItems單選、setMultiChoiceItems多選對話方塊
.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, items[which], Toast.LENGTH_LONG).show();
}
})
private void showDialog(){
//final String items[]={"Dota2","Dota","War3"};
final String[] items = getResources().getStringArray(R.array.items);
final boolean selected[]={true,false,true};
AlertDialog.Builder builder= new AlertDialog.Builder(this);//得到DIalog物件
builder.setIcon(R.mipmap.bcg)
.setTitle(R.string.dialog_title)
/*selected是預設的對應的選中狀態。當你選擇時,系統會自動幫你把selected中的值做相應改變,所以在勾選框中可以得到所有的選擇狀態。*/
.setMultiChoiceItems(items, selected, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Toast.makeText(DialogActivity.this, items[which]+isChecked, Toast.LENGTH_LONG).show();
}
})
.create()
.show();
}
1.3、setView顯示自定義Dialog
先定義Dialog需要顯示的介面佈局檔案,再通過 View view = LayoutInflater.from(DialogActivity.this).inflate(R.layout.dialog_main, null)把佈局檔案載入進來,得到一個View物件,然後通過 AlertDialog.Builder 的setView方法來設定,最後還可以通過Window物件調整對話方塊的大小、透明度等等。
private void showCustomDialog(){
AlertDialog.Builder builder= new AlertDialog.Builder(this);//得到DIalog物件
// 通過LayoutInflater來載入一個xml的佈局檔案作為一個View物件
View view = LayoutInflater.from(DialogActivity.this).inflate(R.layout.dialog_main, null);
final Button btn= (Button) view.findViewById(R.id.btn_test);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(DialogActivity.this,"Costom Button clicked",Toast.LENGTH_LONG).show();
}
});
/**!!!!!!!!!
Window dialogWindow=builder.create().getWindow();
WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes();
layoutParams.width=800;
layoutParams.height=600;
layoutParams.alpha=0.8f;
dialogWindow.setAttributes(layoutParams);
Log.e("layoutParams",layoutParams.width+"--1---"+layoutParams.height);//此時輸出 800 --1--- 600
dialog.show();
Log.e("layoutParams",layoutParams.width+"--2---"+layoutParams.height);//此時值改變了-2 --1--- -2
//dialogWindow.setLayout(layoutParams.width,layoutParams.height);如果用這這段程式碼則無法調整對話方塊的大小
dialogWindow.setLayout(800,600);
*/
// 設定我們自己定義的佈局檔案作為彈出框的Content
builder.setView(view)
.show();
}
我們首先需要寫一個xml的佈局檔案,我們不需要在佈局檔案裡定義Button按鈕,可以通過 AlertDialog.Builder 來設定 action buttons。
通過 View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog, null); 我們可以將我們的佈局檔案載入進來,得到一個View物件,然後通過 AlertDialog.Builder 的setView方法來設定我們的自定義彈出框
2、ProgressDialog
2.1、ProgressDialog部分原始碼結構
ProgressDialog直接繼承自AlertDialog並實現了DialogInterface介面,支援旋轉的圓形進度條和水平進度條,從程式碼結構來看我們可以通過兩種方式來得到ProgressDialog物件:構造方法和靜態方法show
private Handler mViewUpdateHandler;
public class ProgressDialog extends AlertDialog {
/** Creates a ProgressDialog with a circular, spinning progress
* bar. This is the default.
*/
private Handler mViewUpdateHandler;
public ProgressDialog(Context context) {
...
}
public ProgressDialog(Context context, int theme) {
...
}
public static ProgressDialog show(Context context, CharSequence title,
CharSequence message, boolean indeterminate, boolean cancelable) {
...
}
public static ProgressDialog show(Context context, CharSequence title,
CharSequence message, boolean indeterminate,
boolean cancelable, OnCancelListener cancelListener) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setIndeterminate(indeterminate);
dialog.setCancelable(cancelable);
dialog.setOnCancelListener(cancelListener);
dialog.show();
return dialog;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
LayoutInflater inflater = LayoutInflater.from(mContext);
TypedArray a = mContext.obtainStyledAttributes(null,
com.android.internal.R.styleable.AlertDialog,
com.android.internal.R.attr.alertDialogStyle, 0);
if (mProgressStyle == STYLE_HORIZONTAL) {
/* Use a separate handler to update the text views as they
* must be updated on the same thread that created them.
*/
mViewUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
/* Update the number and percent */
int progress = mProgress.getProgress();
int max = mProgress.getMax();
if (mProgressNumberFormat != null) {
String format = mProgressNumberFormat;
mProgressNumber.setText(String.format(format, progress, max));
} else {
mProgressNumber.setText("");
}
if (mProgressPercentFormat != null) {
double percent = (double) progress / (double) max;
SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent));
tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgressPercent.setText(tmp);
} else {
mProgressPercent.setText("");
}
}
};
...
View view = inflater.inflate(a.getResourceId(
com.android.internal.R.styleable.AlertDialog_horizontalProgressLayout,
R.layout.alert_dialog_progress), null);
mProgress = (ProgressBar) view.findViewById(R.id.progress);
mProgressNumber = (TextView) view.findViewById(R.id.progress_number);
mProgressPercent = (TextView) view.findViewById(R.id.progress_percent);
setView(view);
} else {
View view = inflater.inflate(a.getResourceId(
com.android.internal.R.styleable.AlertDialog_progressLayout,
R.layout.progress_dialog), null);
mProgress = (ProgressBar) view.findViewById(R.id.progress);
mMessageView = (TextView) view.findViewById(R.id.message);
setView(view);
}
...
setIndeterminate(mIndeterminate);
onProgressChanged();
super.onCreate(savedInstanceState);
}
}
2.2、使用ProgressDialog
步驟簡記:得到ProgressDialog物件——>顯示
2.2.1、建立ProgressDialog物件
- 通過構造方法new
ProgressDialog dialog = new ProgressDialog(this);
- 通過ProgressDialog的靜態方法show
//建立進度對話方塊(只能是圓形條)並設定title和Message提示內容
ProgressDialog logDialog = ProgressDialog.show(this, "Notice", "Loging……");
//建立進度對話方塊(只能是圓形條)並設定title和Message提示內容,最後一個引數boolean indeterminate設定是否是不明確的狀態
ProgressDialog logDialog = ProgressDialog
.show(this,"Notice", "Loging……", false);
//建立進度對話方塊(只能是圓形條)並設定title和Message提示內容,最後一個引數boolean cancelable 設定進度條是否是可以取消的
ProgressDialog logDialog = ProgressDialog
.show(this,"Notice", "Loging……", false,true);
還可以監聽進度條取消的事件
private OnCancelListener cancelListener = new OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
Toast.makeText(DialogActivity.this, "ProgressBar canceled", Toast.LENGTH_LONG).show();
}
};
// cancelListener用於監聽進度條被取消
ProgressDialog logDialog = ProgressDialog.show(this, "Notice", "Loging……", true,
true, cancelListener);
2.2.2、圓形進度條對話方塊
private void showProgressDialog(){
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);// 設定進度條的形式為圓形轉動的進度條
dialog.setCancelable(true);// 設定是否可以通過點選Back鍵取消
dialog.setCanceledOnTouchOutside(false);// 設定在點選Dialog外是否取消Dialog進度條
dialog.setIcon(R.mipmap.ic_launcher);//
// 設定提示的title的圖示,預設是沒有的,需注意的是如果沒有設定title的話只設置Icon是不會顯示圖示的
dialog.setTitle("Notice");
// dismiss監聽
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
// 監聽Key事件被傳遞給dialog
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
return false;
}
});
// 監聽cancel事件
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
});
//設定可點選的按鈕,最多有三個(預設情況下)
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "Confirm",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, "Natrue",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setMessage("Loging in ...");
dialog.show();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6666);
/*cancel和dismiss方法本質都是一樣的,都是從螢幕中刪除Dialog,唯一的區別是
呼叫cancel方法會回撥DialogInterface.OnCancelListener如果註冊的話,dismiss方法不會回掉*/
dialog.cancel();
// dialog.dismiss();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
2.2.3、水平進度條對話方塊
private void showHorProgressDialog(){
final ProgressDialog dialog = new ProgressDialog(this);
///dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);// 設定進度條的形式為圓形轉動的進度條
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setProgress(R.mipmap.ic_launcher);
dialog.setSecondaryProgress(R.mipmap.image002);//設定二級進度條的背景
dialog.setCancelable(true);// 設定是否可以通過點選Back鍵取消
dialog.setCanceledOnTouchOutside(false);// 設定在點選Dialog外是否取消Dialog進度條
dialog.setIcon(R.mipmap.ic_launcher);//
// 設定提示的title的圖示,預設是沒有的,需注意的是如果沒有設定title的話只設置Icon是不會顯示圖示的
dialog.setTitle("Notice");
dialog.setMax(100);
// dismiss監聽
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
});
// 監聽Key事件被傳遞給dialog
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
return false;
}
});
// 監聽cancel事件
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
});
//設定可點選的按鈕,最多有三個(預設情況下)
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "Confirm",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, "Natrue",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.setMessage("Downloading ...");
dialog.show();
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (i < 100) {
try {
Thread.sleep(200);
// 更新進度條的進度,可以在子執行緒中更新進度條進度
dialog.incrementProgressBy(1);
dialog.incrementSecondaryProgressBy(15);//二級進度條更新方式
i++;
} catch (Exception e) { }
}
// 在進度條走完時刪除Dialog
dialog.dismiss();
}
}).start();
}