Android中用Builder模式自定义Dialog的方法
前言
我们开发人员在实际项目过程中遇到的需求是多种多样的,有时我们要匹配app自己的设计风格,有时我们会觉得系统的对话框使用起来不够*,因此自己定义一个适合自己的dialog是很有必要的。
为什么要用builder模式
builder设计模式是一步步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。它的优点就在于将对象的构建和表示分离从而解耦。我们都知道android系统自身的对话框如alertdialog就采用了builder模式,因此可见builder模式很适合用来构建dialog对象。
下面话不多说了,上代码。
basedialog.java
package com.acker.android.dialog; import android.app.dialog; import android.content.context; import android.os.bundle; import android.text.textutils; import android.view.view; import android.widget.button; import android.widget.framelayout; import android.widget.linearlayout; import android.widget.progressbar; import android.widget.textview; /** * 自定义dialog基类 * * @author guojinyu */ public class basedialog extends dialog { private textview tvtitle; private textview tvmsg; private progressbar pbloading; private button btnpositive; private button btnnegative; private framelayout flcustom; private view.onclicklistener ondefaultclicklistener = new view.onclicklistener() { @override public void onclick(view view) { cancel(); } }; private view.onclicklistener onpositivelistener = ondefaultclicklistener; private view.onclicklistener onnegativelistener = ondefaultclicklistener; private string mtitle; private string mmessage; private string positivetext; private string negativetext; private boolean isprogressbarshow = false; private boolean isnegativebtnshow = true; private view mview; private basedialog(context context) { super(context, r.style.mydialog); } @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.dialog_base); flcustom = (framelayout) findviewbyid(r.id.fl_dialog_content); tvtitle = (textview) findviewbyid(r.id.tv_title); pbloading = (progressbar) findviewbyid(r.id.pb_loading); tvmsg = (textview) findviewbyid(r.id.tv_msg); btnpositive = (button) findviewbyid(r.id.btn_positive); btnnegative = (button) findviewbyid(r.id.btn_negative); } /** * 调用完builder类的create()方法后显示该对话框的方法 */ @override public void show() { super.show(); show(this); } private void show(basedialog mdialog) { if (!textutils.isempty(mdialog.mtitle)) { mdialog.tvtitle.settext(mdialog.mtitle); } if (mdialog.mview != null) { mdialog.flcustom.addview(mdialog.mview); mdialog.pbloading.setvisibility(view.gone); mdialog.tvmsg.setvisibility(view.gone); } else { if (!textutils.isempty(mdialog.mmessage)) { mdialog.tvmsg.settext(mdialog.mmessage); mdialog.tvmsg.setvisibility(view.visible); } if (isprogressbarshow) { mdialog.pbloading.setvisibility(view.visible); mdialog.btnpositive.setvisibility(view.gone); mdialog.btnnegative.setvisibility(view.gone); } } if (!mdialog.isnegativebtnshow) { mdialog.btnnegative.setvisibility(view.gone); linearlayout.layoutparams layoutparams = (linearlayout.layoutparams) mdialog.btnpositive .getlayoutparams(); layoutparams.setmargins(150, layoutparams.topmargin, 150, layoutparams.bottommargin); mdialog.btnpositive.setlayoutparams(layoutparams); } else { mdialog.btnnegative.setonclicklistener(mdialog.onnegativelistener); if (!textutils.isempty(mdialog.negativetext)) { mdialog.btnnegative.settext(mdialog.negativetext); } } mdialog.btnpositive.setonclicklistener(mdialog.onpositivelistener); if (!textutils.isempty(mdialog.positivetext)) { mdialog.btnpositive.settext(mdialog.positivetext); } } public static class builder { private basedialog mdialog; public builder(context context) { mdialog = new basedialog(context); } /** * 设置对话框标题 * * @param title */ public builder settitle(string title) { mdialog.mtitle = title; return this; } /** * 设置对话框文本内容,如果调用了setview()方法,该项失效 * * @param msg */ public builder setmessage(string msg) { mdialog.mmessage = msg; return this; } /** * 设置确认按钮的回调 * * @param onclicklistener */ public builder setpositivebutton(view.onclicklistener onclicklistener) { mdialog.onpositivelistener = onclicklistener; return this; } /** * 设置确认按钮的回调 * * @param btntext,onclicklistener */ public builder setpositivebutton(string btntext, view.onclicklistener onclicklistener) { mdialog.positivetext = btntext; mdialog.onpositivelistener = onclicklistener; return this; } /** * 设置取消按钮的回掉 * * @param onclicklistener */ public builder setnegativebutton(view.onclicklistener onclicklistener) { mdialog.onnegativelistener = onclicklistener; return this; } /** * 设置取消按钮的回调 * * @param btntext,onclicklistener */ public builder setnegativebutton(string btntext, view.onclicklistener onclicklistener) { mdialog.negativetext = btntext; mdialog.onnegativelistener = onclicklistener; return this; } /** * 设置手否显示progressbar,默认不显示 * * @param isprogressbarshow */ public builder setprogressbarshow(boolean isprogressbarshow) { mdialog.isprogressbarshow = isprogressbarshow; return this; } /** * 设置是否显示取消按钮,默认显示 * * @param isnegativebtnshow */ public builder setnegativebtnshow(boolean isnegativebtnshow) { mdialog.isnegativebtnshow = isnegativebtnshow; return this; } /** * 设置自定义内容view * * @param view */ public builder setview(view view) { mdialog.mview = view; return this; } /** * 设置该对话框能否被cancel掉,默认可以 * * @param cancelable */ public builder setcancelable(boolean cancelable) { mdialog.setcancelable(cancelable); return this; } /** * 设置对话框被cancel对应的回调接口,cancel()方法被调用时才会回调该接口 * * @param oncancellistener */ public builder setoncancellistener(oncancellistener oncancellistener) { mdialog.setoncancellistener(oncancellistener); return this; } /** * 设置对话框消失对应的回调接口,一切对话框消失都会回调该接口 * * @param ondismisslistener */ public builder setondismisslistener(ondismisslistener ondismisslistener) { mdialog.setondismisslistener(ondismisslistener); return this; } /** * 通过builder类设置完属性后构造对话框的方法 */ public basedialog create() { return mdialog; } } }
代码很简单,basedialog类内定义一些对话框要显示的控件和这些控件对应的一些属性,以及最终将所有属性填入到控件的方法show()
。builder类是basedialog的一个内部类,其中定义了basedialog类的所有属性的set方法以及装配完毕后的create()
方法。
对应的自定义dialog的布局文件 dialog_base.xml如下:
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginleft="20dp" android:layout_marginright="20dp" android:background="@drawable/bg_base_dialog" android:orientation="vertical"> <textview android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginbottom="10dp" android:layout_margintop="10dp" android:gravity="center" android:textcolor="@android:color/black" android:textsize="18sp" /> <view android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/black" /> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginleft="20dp" android:layout_marginright="20dp" android:orientation="horizontal"> <progressbar android:id="@+id/pb_loading" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" android:layout_marginbottom="20dp" android:layout_marginright="20dp" android:layout_margintop="20dp" android:visibility="gone" /> <textview android:id="@+id/tv_msg" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginbottom="20dp" android:layout_margintop="20dp" android:textcolor="@android:color/black" android:textsize="18sp" android:visibility="gone" /> </linearlayout> <framelayout android:id="@+id/fl_dialog_content" android:layout_width="match_parent" android:layout_height="wrap_content"></framelayout> <linearlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <button android:id="@+id/btn_negative" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginbottom="20dp" android:layout_marginleft="20dp" android:layout_marginright="20dp" android:layout_weight="1" android:background="@drawable/bg_dialog_btn_negative" android:gravity="center" android:text="取消" android:textcolor="@android:color/white" android:textsize="18sp" /> <button android:id="@+id/btn_positive" android:layout_width="0dp" android:layout_height="40dp" android:layout_marginbottom="20dp" android:layout_marginleft="20dp" android:layout_marginright="20dp" android:layout_weight="1" android:background="@drawable/bg_dialog_btn_positive" android:gravity="center" android:text="确定" android:textcolor="@android:color/white" android:textsize="18sp" /> </linearlayout> </linearlayout>
涉及到的其他资源文件如下:
对话框样式 styles.xml
<resources> <!-- 全局dialog样式 --> <style name="mydialog" 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:background">@android:color/transparent</item> <item name="android:backgrounddimenabled">true</item> </style> </resources>
通过设置这些属性可以保证对话框背景透明无黑边。
确定取消按钮的颜色值 colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="btn_dialog_negative_normal">#ff0000</color> <color name="btn_dialog_negative_pressed">#bf0000</color> <color name="btn_dialog_positive_normal">#368bff</color> <color name="btn_dialog_positive_pressed">#0067f3</color> </resources>
对话框背景 bg_base_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="4dp" /> <solid android:color="@android:color/white" /> <stroke android:width="1dp" android:color="#e5e7ea" /> </shape>
包含背景、圆角、阴影效果。
确定按钮背景 bg_dialog_btn_positive.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/btn_dialog_positive_normal" android:state_pressed="false"></item> <item android:drawable="@color/btn_dialog_positive_pressed" android:state_pressed="true"></item> </selector>
包含正常和按下的效果。
取消按钮背景 bg_dialog_btn_negative.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/btn_dialog_negative_normal" android:state_pressed="false"></item> <item android:drawable="@color/btn_dialog_negative_pressed" android:state_pressed="true"></item> </selector>
包含正常和按下的效果。
以上就是整个自定义dialog的所有内容,接下来我们通过一个简单的demo来演示如何使用它。
mainactivity.java
package com.acker.android.dialog; import android.content.dialoginterface; import android.os.bundle; import android.support.v7.app.appcompatactivity; import android.view.view; import android.widget.toast; public class mainactivity extends appcompatactivity { basedialog dialog; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); dialog = new basedialog.builder(this).settitle("标题").setmessage("内容") .setpositivebutton("哈哈", new view.onclicklistener() { @override public void onclick(view view) { dialog.dismiss(); } }).setoncancellistener(new dialoginterface.oncancellistener() { @override public void oncancel(dialoginterface dialoginterface) { toast.maketext(mainactivity.this, "cancel", toast.length_short).show(); } }).setondismisslistener(new dialoginterface.ondismisslistener() { @override public void ondismiss(dialoginterface dialoginterface) { toast.maketext(mainactivity.this, "dismiss", toast.length_short).show(); } }).create(); dialog.show(); } }
来看下效果图:
很丑有木有,不过没关系,这里我们只是展示它的用法,如何把对话框做的好看一点就看各位的发挥了。可以看出自定义的basedialog的使用方法与andorid自身的alertdialog基本一致,都是通过其builder类进行对象的构建。
该自定义对话框还支持显示progressbar以及自定义内容填充的功能。
显示progressbar且触摸屏幕不可取消:
.setprogressbarshow(true) .setcancelable(false)
效果图如下:
自定义内容区域且不显示取消按钮:
view view = getlayoutinflater().inflate(r.layout.dialog_input_amount, null); final edittext amountedit = (edittext) view.findviewbyid(r.id.dialog_et_amount); amountedit.settext("123456789");
.setview(view) .setnegativebtnshow(false)
其对应的布局文件为 dialog_input_amount.xml:
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <edittext android:id="@+id/dialog_et_amount" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginbottom="20dp" android:layout_marginleft="20dp" android:layout_marginright="20dp" android:layout_margintop="20dp" android:gravity="center_vertical" android:paddingleft="10dp" android:paddingright="10dp" android:inputtype="numberdecimal" android:singleline="true" android:textsize="18sp" > </edittext> </linearlayout>
效果图如下:
总结
通过本文可以看出通过builder模式自定义dialog既可以维持原有android对话框的使用方法,同时使用方便,*度更高,大家完全可以按照各自的需求来对代码作出相应的修改。需要说明的是本文并没有严格按照传统的builder设计模式来实现对话框,而是做了一些简化以更适合于我们的场景。以上就是本文的全部内容了,希望这篇文章的内容对各位android开发者们能有所帮助。