Dialog對(duì)話框是android常用的基礎(chǔ)視圖組件之一啊奄,本文總結(jié)了對(duì)話框常用的幾種樣式竹椒,以及自定義視圖和帶動(dòng)畫(huà)效果的對(duì)話框
Dialog雖然可以顯示到屏幕上褐墅,但是Dialog并非繼承自View烫罩,而是繼承自O(shè)bject樱调。Dialog的生命周期由Activity來(lái)控制约素,所以當(dāng)Activity被銷(xiāo)毀后,如果再有對(duì)Dialog的操作會(huì)導(dǎo)致異常:java.lang.IllegalArgumentException: View not attached to window manager笆凌。
當(dāng)Dialog顯示的時(shí)候圣猎,下面的Activity會(huì)失去焦點(diǎn)送悔,用戶(hù)的注意力將全部集中在Dialog上跪妥,進(jìn)行操作纽疟;當(dāng)Dialog消失后,Activity將重新獲得焦點(diǎn)。
1 常用樣式對(duì)話框
1.1 雙按鈕對(duì)話框
先上圖
再上代碼蛇损,最后解釋
final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this) {
@Override
public AlertDialog create() {
d("對(duì)話框create床玻,創(chuàng)建時(shí)調(diào)用");
return super.create();
}
@Override
public AlertDialog show() {
d("對(duì)話框show其屏,顯示時(shí)調(diào)用");
return super.show();
}
};
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
d("對(duì)話框取消");
}
});
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
d("對(duì)話框銷(xiāo)毀");
}
});
dialog.setIcon(R.mipmap.ic_launcher)
.setTitle("我是標(biāo)題")
.setMessage("我是要顯示的消息")
.setCancelable(true)
.setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("點(diǎn)擊確定");
}
})
.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("點(diǎn)擊取消");
}
});
dialog.show();
創(chuàng)建對(duì)話框采用了建造者模式膨更,通過(guò)構(gòu)建AlertDialog.Builder來(lái)進(jìn)行參數(shù)設(shè)置矗漾,最后通過(guò)show函數(shù)顯示對(duì)話框。
初始化AlertDialog.Builder的時(shí)候蛛倦,可以重寫(xiě)create和show函數(shù)板驳,在創(chuàng)建和顯示的時(shí)候做一些額外工作慨蓝。
setOnCancelListener:設(shè)置對(duì)話框取消時(shí)的回調(diào)函數(shù)
setOnDismissListener:設(shè)置對(duì)話框消失時(shí)的回調(diào)函數(shù)
setIcon:設(shè)置對(duì)話框的圖標(biāo)
setTitle:設(shè)置對(duì)話框標(biāo)題
setMessage:設(shè)置對(duì)話框消息
setCancelable:設(shè)置對(duì)話框是否可以被取消,如果是true端幼,則點(diǎn)擊非對(duì)話框區(qū)域礼烈,對(duì)話框消失;false則剛好相反
setPositiveButton:設(shè)置確定按鈕的文字和回調(diào)函數(shù)
setNegativeButton:設(shè)置取消按鈕的文字和回調(diào)函數(shù)
show:顯示對(duì)話框
這里要注意一點(diǎn)婆跑,對(duì)話框的cancel和dismiss兩個(gè)函數(shù)的區(qū)別
查看源碼可以發(fā)現(xiàn)
public void cancel() {
if (mCancelMessage != null) {
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
public void setOnCancelListener(final OnCancelListener listener) {
if (listener != null) {
mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
} else {
mCancelMessage = null;
}
}
如果沒(méi)有調(diào)用setOnCancelListener此熬,那么cancel和dismiss兩個(gè)函數(shù)功能是一樣的;當(dāng)調(diào)用了setOnCancelListener時(shí)滑进,會(huì)在執(zhí)行cancel函數(shù)時(shí)向觀察者發(fā)出一個(gè)消息犀忱,僅此而已。
1.2 三按鈕對(duì)話框
final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setIcon(R.mipmap.ic_launcher)
.setTitle("我是標(biāo)題")
.setMessage("我是要顯示的消息")
.setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("確定");
}
})
.setNeutralButton("說(shuō)明",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("說(shuō)明");
}
})
.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("取消");
}
});
dialog.show();
與雙按鈕對(duì)話框相比扶关,三按鈕對(duì)話框多了作為說(shuō)明操作的按鈕阴汇,當(dāng)對(duì)話框的message無(wú)法顯示全部?jī)?nèi)容是,通過(guò)第三個(gè)按鈕將用戶(hù)引導(dǎo)到新的界面驮审,顯示長(zhǎng)篇幅說(shuō)明文字鲫寄。
1.3 列表對(duì)話框
final String[] items = {"項(xiàng)目1", "項(xiàng)目2", "項(xiàng)目3", "項(xiàng)目4"};
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是標(biāo)題")
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
d("選擇: " + items[which]);
}
});
dialog.show();
列表對(duì)話框,用于顯示一個(gè)列表并進(jìn)行單選疯淫。
這里主要通過(guò)setItems函數(shù)為對(duì)話框配置要顯示的列表和選擇時(shí)的回調(diào)函數(shù)地来,選擇后對(duì)話框自動(dòng)消失。
請(qǐng)注意回調(diào)函數(shù)的參數(shù)which從0開(kāi)始熙掺。
1.4 單選對(duì)話框
int select = 1; //表示單選對(duì)話框初始時(shí)選中哪一項(xiàng)
final String[] items = {"項(xiàng)目1", "項(xiàng)目2", "項(xiàng)目3", "項(xiàng)目4"};
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是標(biāo)題")
.setSingleChoiceItems(items, select,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
select = which;
d("選擇: " + items[select]);
}
})
.setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (select != -1) {
d("確定: " + items[select]);
}
}
});
dialog.show();
單選對(duì)話框未斑,與列表對(duì)話框功能相似,主要區(qū)別是選擇后對(duì)話框是否消失币绩。單選對(duì)話框可以反復(fù)選擇蜡秽,直到用戶(hù)點(diǎn)擊確定按鈕府阀。
setSingleChoiceItems函數(shù)的第一個(gè)參數(shù)是要顯示的列表文本,第二個(gè)參數(shù)表示默認(rèn)選中哪一項(xiàng)芽突,下標(biāo)從0開(kāi)始试浙。
1.5 多選對(duì)話框
private ArrayList<Integer> list = new ArrayList<>();
final String[] items = {"項(xiàng)目1", "項(xiàng)目2", "項(xiàng)目3", "項(xiàng)目4"};
final boolean selected[] = {false, true, false, false};
list.clear();
for (int i = 0, size = selected.length; i < size; ++i) {
if (selected[i]) {
list.add(i);
}
}
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是標(biāo)題")
.setMultiChoiceItems(items, selected,
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (isChecked) {
list.add(which);
} else {
list.remove(Integer.valueOf(which));
}
}
})
.setPositiveButton("確定",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (list.size() == 0) {
d("你什么都沒(méi)選啊,小伙");
} else {
StringBuilder str = new StringBuilder();
for (int i = 0, size = list.size(); i < size; i++) {
str.append(items[list.get(i)]);
if (i < size - 1) {
str.append(", ");
}
}
d("你選中了: " + str.toString());
}
}
});
dialog.show();
setMultiChoiceItems函數(shù)
參數(shù)1:設(shè)置要顯示的文本列表items
參數(shù)2:和文本數(shù)據(jù)個(gè)數(shù)一致的boolean數(shù)組selected寞蚌,selected[i]表示items[i]是否是選中狀態(tài)
參數(shù)3:選項(xiàng)被點(diǎn)擊時(shí)的回調(diào)函數(shù)田巴,isChecked為true表示選中;false表示取消
1.6 等待對(duì)話框
ProgressDialog dialog = new ProgressDialog(MainActivity.this);
dialog.setTitle("我是標(biāo)題");
dialog.setMessage("等待中... 想關(guān)閉請(qǐng)殺掉app");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
這里使用了ProgressDialog挟秤,由于setCancelable設(shè)置為false壹哺,對(duì)話框無(wú)法取消,所以此時(shí)只能殺死app艘刚!
setIndeterminate參數(shù)為true表示該進(jìn)度條不能明確等待進(jìn)度管宵,僅僅告知用戶(hù)需要等待,沒(méi)有預(yù)期攀甚。
1.7 進(jìn)度對(duì)話框
int progress = 0;
final int MAX_PROGRESS = 100;
final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
dialog.setTitle("我是標(biāo)題");
dialog.setProgress(0);
dialog.setMax(MAX_PROGRESS);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
mHandler.removeCallbacksAndMessages(null);
d("進(jìn)度被打斷");
}
});
dialog.show();
progress = 0;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
d("" + progress);
progress++;
dialog.setProgress(progress);
if (progress == 100) {
dialog.cancel();
} else {
mHandler.postDelayed(this, 100);
}
}
}, 100);
同樣使用ProgressDialog表示一個(gè)有進(jìn)度概念的等待對(duì)話框箩朴,給用戶(hù)一個(gè)心理預(yù)期。
setProgress函數(shù):設(shè)置當(dāng)前進(jìn)度值
setMax函數(shù):設(shè)置最大進(jìn)度值
setProgressStyle函數(shù):設(shè)置進(jìn)度條類(lèi)型
1.8 日期選擇對(duì)話框
Calendar c = Calendar.getInstance();
DatePickerDialog dialog = new DatePickerDialog(MainActivity.this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
d("選擇日期:" + year + "年" + (monthOfYear+1) + "月" + dayOfMonth + "日");
}
}, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
dialog.show();
注意云稚,當(dāng)選擇成功回調(diào)的時(shí)候隧饼,月份是從0開(kāi)始的,所以要注意加一静陈。
1.9 時(shí)間選擇對(duì)話框
Calendar c = Calendar.getInstance();
TimePickerDialog dialog = new TimePickerDialog(MainActivity.this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
d("選擇時(shí)間:" + hourOfDay + "時(shí)" + minute + "分");
}
}, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true);
dialog.show();
構(gòu)造參數(shù)最后一個(gè)參數(shù)true表示采用24小時(shí)制
2 自定義對(duì)話框
2.1 自定義UI對(duì)話框
public class CustomDialog1 extends Dialog {
private Context context;
public CustomDialog1(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.context = context;
build();
}
private void build() {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.custom_dialog1_layout, null);
view.findViewById(R.id.like).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "喜歡", Toast.LENGTH_SHORT).show();
dismiss();
}
});
view.findViewById(R.id.dislike).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "一般", Toast.LENGTH_SHORT).show();
dismiss();
}
});
setContentView(view);
}
}
先通過(guò)inflate函數(shù)解析布局文件燕雁,然后通過(guò)setContentView為對(duì)話框設(shè)置視圖
2.2 自定義尺寸位置對(duì)話框
public class CustomDialog2 extends Dialog {
private Context context;
public CustomDialog2(Context context) {
super(context);
// super(context, R.style.custom_dialog2_style);
init(context);
}
public void init(Context context) {
this.context = context;
setContentView(R.layout.custom_dialog2_layout);
Window window = getWindow();
window.setBackgroundDrawableResource(R.drawable.custom_dialog1_bg);//設(shè)置window的背景色
WindowManager.LayoutParams lp = window.getAttributes();
window.setGravity(Gravity.BOTTOM);
lp.x = 250;
lp.y = 250;
lp.width = 400;
lp.height = 400;
lp.alpha = 0.8f;
window.setAttributes(lp);
setCanceledOnTouchOutside(true);//設(shè)置點(diǎn)擊Dialog外部任意區(qū)域關(guān)閉Dialog
}
}
custom_dialog2_layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@android:color/holo_red_dark"
>
</FrameLayout>
對(duì)話框的gravity是通過(guò)window的setGravity來(lái)實(shí)現(xiàn)貼邊功能的。
對(duì)話框窗口大小是通過(guò)WindowManager.LayoutParams來(lái)進(jìn)行調(diào)整的:
x:負(fù)數(shù)表示向左移動(dòng)鲸拥,正數(shù)表示向右移動(dòng)
y:負(fù)數(shù)表示向下移動(dòng)拐格,正數(shù)表示向上移動(dòng)
width:表示對(duì)話框的寬
height:表示對(duì)話框的高
alpha:表示對(duì)話框的透明度
上面顯示了這樣一個(gè)對(duì)話框:
1.對(duì)話框尺寸400400
2.對(duì)話框居中貼底邊
3.右移250,上移250
4.內(nèi)部有一個(gè)80dp80dp的紅色視圖
2.3 動(dòng)畫(huà)對(duì)話框
public class AnimDialog1 extends Dialog {
private Context context;
public AnimDialog1(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.context = context;
build();
}
private void build() {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.custom_dialog1_layout, null);
view.findViewById(R.id.like).setVisibility(View.GONE);
view.findViewById(R.id.dislike).setVisibility(View.GONE);
setContentView(view);
Window window = getWindow();
window.setWindowAnimations(R.style.dialog1_window_anim);
}
}
對(duì)話框的出現(xiàn)消失動(dòng)畫(huà)是通過(guò)window的setWindowAnimations函數(shù)來(lái)設(shè)置的刑赶,這里并不是設(shè)置一個(gè)anim捏浊,而是設(shè)置了一個(gè)
<style name="dialog1_window_anim" parent="android:Animation" >
<item name="android:windowEnterAnimation">@anim/dialog1_enter_anim</item>
<item name="android:windowExitAnimation">@anim/dialog1_exit_anim</item>
</style>
這里可以清楚的看到,可以為window設(shè)置進(jìn)入和退出動(dòng)畫(huà)
部分手機(jī)如果沒(méi)有動(dòng)畫(huà)效果撞叨,請(qǐng)?jiān)贛anifest.xml的application下的android:theme的style定義當(dāng)中加入 <item name="android:windowNoTitle">true</item>
3 其他
3.1 對(duì)話框常用樣式
<item name="android:windowFrame">@null</item><!--是否有邊框-->
<item name="android:windowIsFloating">true</item><!--是否浮現(xiàn)在activity之上-->
<item name="android:windowIsTranslucent">false</item><!--是佛半透明-->
<item name="android:windowNoTitle">true</item><!--有無(wú)標(biāo)題-->
<item name="android:windowBackground">@android:color/holo_green_light</item><!--背景顏色-->
<item name="android:backgroundDimEnabled">false</item><!--是否充許對(duì)話框的背景變暗-->