Android仿QQ消息提示實(shí)現(xiàn)彈出式對(duì)話框
本文在《7種形式的Android Dialog使用實(shí)例》在這篇文章的基礎(chǔ)進(jìn)行學(xué)習(xí),具體內(nèi)容如下
1.概述
android原生控件向來以丑著稱(新推出的Material Design當(dāng)另說),因此幾乎所有的應(yīng)用都會(huì)特殊定制自己的UI樣式。而其中彈出式提示框的定制尤為常見,本篇我們將從模仿QQ退出提示框來看一下常見的幾種自定義提示框的實(shí)現(xiàn)方式。
這里使用的幾種彈出框?qū)崿F(xiàn)方法概括為以下幾種:
自定義Dialog
自定義PopupWindow
自定義Layout View
Activity的Dialog樣式
FragmentDialog
先看下最終的效果圖:

2.實(shí)踐
前面提到幾種實(shí)現(xiàn)方式均可以達(dá)到同樣的演示效果,但其中又是各有不同。這里先逐一列舉各種具體實(shí)現(xiàn),最后加以綜述總結(jié)和歸納吧。
在此之前呢,先看一下這里實(shí)現(xiàn)的對(duì)話框共用布局layout/confirm_dialog.xml 。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/confirm_dialog_bg"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical" >
<TextView
android:id="@+id/title_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingBottom="10dp"
android:paddingTop="15dp"
android:text="Message Title"
android:textColor="@android:color/black"
android:textSize="20sp"
android:visibility="visible" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/transparent"
android:orientation="vertical" >
<TextView
android:id="@+id/text_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="10dp"
android:textColor="@android:color/black"
android:text="this is message content"
android:textSize="16dip"/>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginTop="15dip"
android:background="#c5c5c5" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dip"
android:background="@android:color/transparent"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<!-- 取消按鈕 -->
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Cancel"
android:textStyle="bold"
android:textColor="#0072c6"
android:background="@drawable/confirm_dialog_cancel_selector"
android:textSize="15sp" />
<!-- 確認(rèn)按鈕 -->
<View
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:background="#c5c5c5"/>
<Button
android:id="@+id/btn_ok"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="OK"
android:textStyle="bold"
android:textColor="#0072c6"
android:background="@drawable/confirm_dialog_ok_selector"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
僅僅通過布局預(yù)覽就可以看到效果了:

下邊我們分別通過上述幾種方式來使用這個(gè)布局展示消息提示框。
2.1 Dialog
這個(gè)是最基本也最常見的非阻塞式對(duì)話框。具體形式可分為七種,詳細(xì)參見網(wǎng)上各種文章,隨便引用一篇7種形式的Android Dialog使用舉例。
(注:官方在fragmentDialog推出后就不在推薦直接使用Dialog來創(chuàng)建對(duì)話框,這是后話)
我們這里自定義的提示框ConfirmDialog繼承自Dialog,使用confirm_dialog.xml 初始化布局,綁定相應(yīng)事件。
public class ConfirmDialog extends Dialog {
private Context context;
private TextView titleTv,contentTv;
private View okBtn,cancelBtn;
private OnDialogClickListener dialogClickListener;
public ConfirmDialog(Context context) {
super(context);
this.context = context;
initalize();
}
//初始化View
private void initalize() {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.confirm_dialog, null);
setContentView(view);
initWindow();
titleTv = (TextView) findViewById(R.id.title_name);
contentTv = (TextView) findViewById(R.id.text_view);
okBtn = findViewById(R.id.btn_ok);
cancelBtn = findViewById(R.id.btn_cancel);
okBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
if(dialogClickListener != null){
dialogClickListener.onOKClick();
}
}
});
cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
if(dialogClickListener != null){
dialogClickListener.onCancelClick();
}
}
});
}
/**
*添加黑色半透明背景
*/
private void initWindow() {
Window dialogWindow = getWindow();
dialogWindow.setBackgroundDrawable(new ColorDrawable(0));//設(shè)置window背景
dialogWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);//設(shè)置輸入法顯示模式
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
DisplayMetrics d = context.getResources().getDisplayMetrics();//獲取屏幕尺寸
lp.width = (int) (d.widthPixels * 0.8); //寬度為屏幕80%
lp.gravity = Gravity.CENTER; //中央居中
dialogWindow.setAttributes(lp);
}
public void setOnDialogClickListener(OnDialogClickListener clickListener){
dialogClickListener = clickListener;
}
/**
*添加按鈕點(diǎn)擊事件
*/
public interface OnDialogClickListener{
void onOKClick();
void onCancelClick();
}
}
2.2 PopupWindow
PopupWindow是阻塞式對(duì)話框,只有在退出操作時(shí)候程序才會(huì)繼續(xù)運(yùn)行。另外PopupWindow可以根據(jù)自由確定自身位置。按照位置有無偏移分,可以分為偏移和無偏移兩種;按照參照物的不同,可以分為相對(duì)于某個(gè)控件(Anchor錨)和相對(duì)于父控件。具體如下
showAsDropDown(View anchor):相對(duì)某個(gè)控件的位置(正左下方),無偏移
showAsDropDown(View anchor, int xoff, int yoff):相對(duì)某個(gè)控件的位置(正下方),有偏移
showAtLocation(View parent, int gravity, int x, int y):相對(duì)于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以設(shè)置偏移或無偏移
這里只是達(dá)到同樣的顯示效果,僅示范下showAtBottom的使用:
public class ConfirmPopWindow extends PopupWindow{
private Context context;
private TextView titleTv,contentTv;
private View okBtn,cancelBtn;
private OnDialogClickListener dialogClickListener;
public ConfirmPopWindow(Context context) {
super(context);
this.context = context;
initalize();
}
private void initalize() {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.confirm_dialog, null);
setContentView(view);
initWindow();
titleTv = (TextView) view.findViewById(R.id.title_name);
contentTv = (TextView) view.findViewById(R.id.text_view);
okBtn = view.findViewById(R.id.btn_ok);
cancelBtn = view.findViewById(R.id.btn_cancel);
okBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
if(dialogClickListener != null){
dialogClickListener.onOKClick();
}
}
});
cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
if(dialogClickListener != null){
dialogClickListener.onCancelClick();
}
}
});
}
private void initWindow() {
this.setBackgroundDrawable(new ColorDrawable(0));
DisplayMetrics d = context.getResources().getDisplayMetrics();
this.setWidth((int) (d.widthPixels * 0.8));
this.setHeight(LayoutParams.WRAP_CONTENT);
this.setFocusable(true);
this.setOutsideTouchable(true);
this.update();
}
public void showAtBottom(View view){
showAsDropDown(view, Math.abs((view.getWidth() - getWidth())/2), 20);
}
public void setOnDialogClickListener(OnDialogClickListener clickListener){
dialogClickListener = clickListener;
}
public interface OnDialogClickListener{
void onOKClick();
void onCancelClick();
}
}
2.3 自定義Layout
前邊兩種是系統(tǒng)封裝好的View ,同樣的,我們也可以自定義layout布局來實(shí)現(xiàn)的彈出式對(duì)話框效果。既然是自定義,有必要細(xì)致講述一下,
ConfirmLayout繼承自FrameLayout,通過獲取窗口管理器WindowManager 將我們的自定義view添加到窗口最前端并顯示出來,達(dá)到預(yù)期效果。
1.初始化View
先初始化半透明黑色背景和對(duì)應(yīng)的confirm_layout,然后給窗體添加按鍵返回事件
protected void initialize() {
initBackground();//初始化黑色背景
initContentView();//初始化confirm_layout 對(duì)應(yīng)的View
windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
setOnKeyListener(new OnKeyListener() { //添加按鍵返回事件
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode && KeyEvent.ACTION_DOWN == event.getAction()) {
hide();//隱藏當(dāng)前view
return true;
}
return false;
}
setFocusable(true); //可獲得焦點(diǎn)
setFocusableInTouchMode(true); //可觸碰獲得焦點(diǎn)
}
2.顯示自定義VIew : show()
調(diào)用顯示的時(shí)候保證在主線程中,如果當(dāng)前View沒有被添加至窗口中,則添加;然后使用動(dòng)畫漸變效果顯示背景,最后動(dòng)畫完成時(shí)顯示當(dāng)前對(duì)話框View.
public void show() {
((Activity) getContext()).runOnUiThread(new Runnable() {
@Override
public void run() {
if (getParent() == null) { //沒有添加則添加至窗體
//獲取窗體的布局屬性,設(shè)置左上角對(duì)齊,填充父容器
WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
wlp.type = WindowManager.LayoutParams.TYPE_APPLICATION;
wlp.format = PixelFormat.TRANSPARENT;
wlp.gravity = Gravity.LEFT | Gravity.TOP;
wlp.width = LayoutParams.MATCH_PARENT;
wlp.height = LayoutParams.MATCH_PARENT;
windowManager.addView(ConfirmLayout.this, wlp);
}
showBackGround();//顯示背景動(dòng)畫和自定義View
}
});
}
/**
*顯示背景動(dòng)畫
*/
protected void showBackGround() {
if (isShowing)
return;
isShowing = true;
background.clearAnimation();
background.setVisibility(View.VISIBLE);
AlphaAnimation an = new AlphaAnimation(0, 1);
an.setDuration(durationMillis);
background.startAnimation(an);
}
3.隱藏自定義VIew : hide()
隱藏對(duì)話框的方法跟show()恰恰相反,首先調(diào)用隱藏動(dòng)畫,動(dòng)畫結(jié)束從窗體中移除View
public void hide() {
((Activity) getContext()).runOnUiThread(new Runnable() {
@Override
public void run() {
hideBackGround();//隱藏背景
if (getParent() != null)
windowManager.removeView(ConfirmLayout.this);//移除view
}
});
}
/**
*隱藏背景背景動(dòng)畫
*/
protected void hideBackGround() {
if (!isShowing)
return;
isShowing = false;
background.clearAnimation();
AlphaAnimation an = new AlphaAnimation(1, 0);
an.setDuration(durationMillis);
an.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
background.setVisibility(View.GONE);
}
});
background.startAnimation(an);
}
其他部分同上,不再一一貼出,詳細(xì)可查看示例源碼。
2.4 Activity的Dialog樣式
通過使用主題Theme來實(shí)現(xiàn)Activity作為一個(gè)dialog來顯示的效果。我們首先在 AndroidManifest.xml 中配置該activity,使得
android:theme=”@android:style/Theme.Dialog”
同樣我們可以自定義繼承于Theme.Dialog的style樣式增加自定義屬性,比如:
<resources> <style name="DialogStyle" parent="@android:style/Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowFrame">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowFullscreen">true</item> <item name="android:backgroundDimEnabled">true</item> </style> </resources>
然后使用 > android:theme=”@style/DialogStyle” 達(dá)到上述效果。具體實(shí)現(xiàn)跟dialog類似:
public class ConfirmActivity extends Activity{
private TextView titleTv,contentTv;
private View okBtn,cancelBtn;
private OnDialogClickListener dialogClickListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.confirm_dialog);
initViews();
initListeners();
}
private void initViews() {
initWindow();
titleTv = (TextView) findViewById(R.id.title_name);
contentTv = (TextView) findViewById(R.id.text_view);
okBtn = findViewById(R.id.btn_ok);
cancelBtn = findViewById(R.id.btn_cancel);
okBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
if(dialogClickListener != null){
dialogClickListener.onOKClick();
}
}
});
}
private void initWindow() {
getWindow().setBackgroundDrawable(new ColorDrawable(0));
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN |
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
private void initListeners() {
cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
if(dialogClickListener != null){
dialogClickListener.onCancelClick();
}
}
});
}
public void setOnDialogClickListener(OnDialogClickListener clickListener){
dialogClickListener = clickListener;
}
public interface OnDialogClickListener{
void onOKClick();
void onCancelClick();
}
}
2.5 DialogFragment
DialogFragment在android 3.0時(shí)被引入并被加以推廣。
我們?cè)谑褂肈ialogFragment時(shí),至少需要實(shí)現(xiàn)onCreateView或者onCreateDIalog方法。這里在onCreateDIalog中直接返回前面寫好的ConfirmDialog來實(shí)現(xiàn)這個(gè)Fragment。
public class ConfirmFragment extends DialogFragment{
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new ConfirmDialog(getActivity());
}
public void setOnDialogClickListener(OnDialogClickListener clickListener){
((ConfirmDialog)getDialog()).setOnDialogClickListener(clickListener);
}
}
當(dāng)然并不推薦偷懶直接返回前面定義好的ConfirmDialog。我們實(shí)際上使用fragment的onCreateView也更合理和簡介,他可以產(chǎn)生同樣的Dialog效果,同時(shí)可以作為內(nèi)嵌fragment引用。從使用總結(jié)來看,F(xiàn)ragmentDialog 相較于Dialog有兩點(diǎn)好處:
在手機(jī)配置變化,導(dǎo)致Activity需要重新創(chuàng)建時(shí),例如旋屏,DialogFragment對(duì)話框?qū)?huì)由FragmentManager自動(dòng)重建,然而Dialog實(shí)現(xiàn)的對(duì)話框則不會(huì)重新生成;
DialogFragment還擁有fragment的優(yōu)點(diǎn),即可以在一個(gè)Activity內(nèi)部實(shí)現(xiàn)回退(因?yàn)镕ragmentManager會(huì)管理一個(gè)回退棧 ,另外,他可以直接作為一個(gè)普通Fragment嵌套在其他布局里邊;
3.小結(jié)
從實(shí)現(xiàn)效果來看我們確實(shí)有很多選擇,當(dāng)然我們用的最多的必然要數(shù)Dialog(FragmentDialog)和PopupWindow了。但在一般情況下,選擇Dialog和PopupWindow是由我們的具體使用場景來定。比如有些提示消息,比較適合Dialog,而彈出一些具體選項(xiàng),需要等待選擇結(jié)果等情況,更傾向于使用PopupWindow了。
FragmentDialog的幾種使用場景

PopupWindow的幾種使用場景

4.補(bǔ)充
其實(shí)還有種長按彈出菜單,這種除了可以通過上述方法彈出菜單選項(xiàng)外,還可以通過系統(tǒng)提供的 View.setOnCreateContextMenu()方法來實(shí)現(xiàn)。比如:
itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.add("刪除").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
//執(zhí)行刪除操作
return true;
}
});
}
});
長按彈出,基本效果為:

有興趣的不妨試一下。
后邊的話我們先從源碼角度來看一下這里講的幾種實(shí)現(xiàn)方案的具體原理,最后通過一些簡易封裝來做一個(gè)類似IOS上的ActionSheet控件的效果。
演示效果大概為:

詳情請(qǐng)繼續(xù)關(guān)注接下來的博文。
最后附上本篇所講內(nèi)容的源碼:示例源碼demo(已重新更新)
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中自定義對(duì)話框(Dialog)的實(shí)例代碼
- Android自定義對(duì)話框Dialog的簡單實(shí)現(xiàn)
- Android實(shí)現(xiàn)底部對(duì)話框BottomDialog彈出實(shí)例代碼
- 詳解Android 全局彈出對(duì)話框SYSTEM_ALERT_WINDOW權(quán)限
- Android實(shí)現(xiàn)點(diǎn)擊AlertDialog上按鈕時(shí)不關(guān)閉對(duì)話框的方法
- 實(shí)例詳解Android自定義ProgressDialog進(jìn)度條對(duì)話框的實(shí)現(xiàn)
- Android 之BottomsheetDialogFragment仿抖音評(píng)論底部彈出對(duì)話框效果(實(shí)例代碼)
- Android實(shí)現(xiàn)退出界面彈出提示對(duì)話框
- Android中AlertDialog各種對(duì)話框的用法實(shí)例詳解
- Android對(duì)話框使用方法詳解
相關(guān)文章
Android5.0以上版本錄屏實(shí)現(xiàn)代碼(完整代碼)
這篇文章主要介紹了Android5.0以上版本錄屏實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01
Android仿泡泡窗實(shí)現(xiàn)下拉菜單條實(shí)例代碼
最近參與android的項(xiàng)目開發(fā),其中遇到這樣的需求:點(diǎn)擊下拉按鈕,顯示出所有的條目,有刪除和點(diǎn)擊功能,點(diǎn)擊后將條目顯示。下面通過實(shí)例代碼給大家介紹下Android仿泡泡窗實(shí)現(xiàn)下拉菜單條效果,需要的朋友參考下吧2017-05-05
Android permission denied原因歸納和解決辦法
大家好,本篇文章主要講的是Android permission denied原因歸納和解決辦法,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下哦2021-12-12
淺談android性能優(yōu)化之啟動(dòng)過程(冷啟動(dòng)和熱啟動(dòng))
本篇文章主要介紹了淺談android性能優(yōu)化之啟動(dòng)過程(冷啟動(dòng)和熱啟動(dòng)) ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
Android自定義View實(shí)現(xiàn)圓弧進(jìn)度的效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)圓弧進(jìn)度的效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01
Android CountDownTimer實(shí)現(xiàn)定時(shí)器和倒計(jì)時(shí)效果
這篇文章主要為大家詳細(xì)介紹了Android CountDownTimer實(shí)現(xiàn)定時(shí)器和倒計(jì)時(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
在Android打包中區(qū)分測(cè)試和正式環(huán)境淺析
這篇文章主要給大家介紹了關(guān)于在Android打包中如何區(qū)分測(cè)試和正式環(huán)境的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起看看吧。2017-10-10
為Android系統(tǒng)添加config.xml 新配置的設(shè)置
這篇文章主要介紹了為Android系統(tǒng)添加config.xml 新配置的設(shè)置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03

