安卓下Builder模式解析+自定義Dialog實戰演練
**本文將從一下幾個方面展開:
1Bulider模式的原理和使用
2系統Dailog的呼叫
3自定義Dailog
4自定義Dailog的一些小坑**
自定義Dailog效果:
1.Bulider模式的原理和使用
當你看到這樣的一串程式碼是不是覺得很炫酷?
airDialog = new AirDialog.Builder(this).
setLeftText("gogo").
setRightText("nono").
create();
通過幾個點點,像一個鏈子一樣配置組裝一個物件。今天就教給大家自己也可以設計這樣一條鏈子。相比構造方法,這種模式更加靈活,更加清晰。
先看看設計圖:
別怕相信大多數人都看不懂。不過對設計模式瞭解比較多的人呢還是可以看懂的,今天就咱不談這個類圖(你看懂不如會用)。
為什麼可以裡連續…….的呼叫方法,其實就是每次呼叫方法後return一個物件就好嘍。當前的表示式值就一直是這個物件所以可以無限的….下去。
public Builder setLeftText(String left) {
this.left=left;
return this;
}
程式碼很清晰沒個設定方法都是這樣的,方法的返回型別就是當前的Builder。那麼我們就可以繼續的呼叫Builder類中的方法嘍。
2.系統Dailog的呼叫
new AlertDialog.Builder(MainActivity.this).setTitle("系統提示")//設定對話方塊標題
.setMessage("!")//設定顯示的內容
.setPositiveButton("確定",new DialogInterface.OnClickListener() {//新增確定按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).setNegativeButton("返回" ,new DialogInterface.OnClickListener() {//新增返回按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).show();//在按鍵響應事件中顯示此對話方塊
系統提供了很多的Dialog的子類本文不去詳細介紹,接下來就說如何自己寫一個類似Dialog的子類。
3.自定義Dailog
1繼承Dialog並且實現構造方法:
public class AirDialog extends Dialog {
public AirDialog(Context context) {
super(context, R.style.custom_dialog);
}
super方法的第二個引數是為Dialog指定一個主題,那就在Styles。xml中加入如下程式碼:
<style name="custom_dialog" parent="@android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item> <!-- 邊框 -->
<item name="android:windowIsFloating">true</item> <!-- 是否浮現在activity之上 -->
<item name="android:windowIsTranslucent">true</item> <!-- 半透明 -->
<item name="android:windowNoTitle">true</item> <!-- 無標題 -->
<item name="android:windowBackground">@android:color/transparent</item>
</style>
常用的幾個標籤的意思已經註釋。可以通過配置跟多的標籤來擴充套件dialog的樣式。
這樣就可以去呼叫一個dialog了,但是不行,我們要加入Builder模式,於是參考安卓原始碼後新建內部靜態類Builder
public static class Builder{
private Context context;
private Context context;
private String left;
private String right;
private View.OnClickListener leftOCL;
private View.OnClickListener rightOCL;
private Button leftButton;
private Button rightButton;
public Builder(Context context){
this.context=context;
}
指定各種組裝配置方法:
public Builder setLeftText(String left) {
this.left=left;
return this;
}
public Builder setRightText(String right) {
this.right=right;
return this;
}
public Builder setLeftOnClick(View.OnClickListener left) {
this.leftOCL=left;
return this;
}
public Builder setRightOnClick(View.OnClickListener right) {
this.rightOCL=right;
return this;
}
可以看出沒個方法都return 了一個Builder方便下次呼叫。
Builder類中的create方法最後呼叫根據前面的配置生成一個airDialog的物件:
public AirDialog create() {
final AirDialog airDialog=new AirDialog(context);
View view = LayoutInflater.from(context).inflate(R.layout.dialog, null);
leftButton=(Button)view.findViewById(R.id.button2);
rightButton=(Button)view.findViewById(R.id.quxiao_btn);
if (!TextUtils.isEmpty(left))
leftButton.setText(left);
if (!TextUtils.isEmpty(right))
rightButton.setText(right);
if (leftOCL!=null)
leftButton.setOnClickListener(leftOCL);
if (rightOCL!=null)
rightButton.setOnClickListener(rightOCL);
Window win = airDialog.getWindow();
win.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams lp = win.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
win.setAttributes(lp);
// win.setGravity(Gravity.NO_GRAVITY);
win.setGravity(Gravity.CENTER);
airDialog.setContentView(view);
airDialog.setCanceledOnTouchOutside(false);
// airDialog.setCancelable(false);
return airDialog;
}
可以看出Dialog的現實原理,是在WindowManager的控制下現實出來的:
Window win = airDialog.getWindow();
win.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams lp = win.getAttributes();
Activity像一個工匠(控制單元),
Window像窗戶(承載模型),
View像窗花(顯示檢視)
LayoutInflater像剪刀,
Xml配置像窗花圖紙。(大家都這麼比喻,不是我發明的)
清晰的說出了幾者關係。
最後呼叫我們的dialog出來吧:
if (airDialog == null)
airDialog = new AirDialog.Builder(this).
setLeftText("gogo").
setRightText("nono").
setRightOnClick(new View.OnClickListener() {
@Override
public void onClick(View view) {
airDialog.dismiss();
Toast.makeText(MainActivity.this, "nono", Toast.LENGTH_SHORT).show();
}
}).
create();
airDialog.show();
是不是很爽呢,自已可以做出這種鏈了。
4.自定義Dailog的一些小坑
第一次我xml是這樣寫的:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/text_content"
android:layout_width="200dp"
android:layout_height="150dp"
android:background="#FFF222"
android:gravity="center"
android:text="(自定義Dialog彈窗)"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
<Button
android:id="@+id/button2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="yes"
android:background="#FFF000"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@+id/quxiao_btn"
android:layout_toStartOf="@+id/quxiao_btn"/>
<Button
android:id="@+id/quxiao_btn"
android:layout_width="100dp"
android:background="#FFF999"
android:layout_height="wrap_content"
android:text="no"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/text_content"
android:layout_alignEnd="@+id/text_content"/>
<TextView
android:id="@+id/textView"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="#cecb9e"
android:gravity="center"
android:text="標題"
android:layout_above="@+id/text_content"
android:layout_alignLeft="@+id/text_content"
android:layout_alignStart="@+id/text_content"/>
</RelativeLayout>
textView設定再了劇中的content之上,在xml中正常顯示但是到程式中就無法顯示了。問題在於這行程式碼,高度設定成了WRAP_CONTENT。結果沒有吧titleView計算到view中來。
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
後來我就在想安卓為什麼這麼設計,為什麼不MATCH_PARENT,其實原因很簡單:
// win.setGravity(Gravity.NO_GRAVITY);
win.setGravity(Gravity.CENTER);
彈窗之所以叫彈窗,因為它只是視窗不是全螢幕的東西,所謂安卓希望我們的視窗可以動態指定位置如Gravity.CENTER,Gravity.BOTTOM等。這樣視窗更加靈活不用多個xml支援。所以建議大家寫dialog的佈局的時候從“頭“開始,不要以一個center的view為基準,本文的正確的xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
>
<TextView
android:id="@+id/text_content"
android:layout_width="200dp"
android:layout_height="150dp"
android:layout_below="@+id/text_title"
android:layout_centerHorizontal="true"
android:background="#FFF222"
android:gravity="center"
android:text="(自定義Dialog彈窗)"/>
<Button
android:id="@+id/quxiao_btn"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/text_content"
android:layout_alignRight="@+id/text_content"
android:layout_below="@+id/text_content"
android:background="#FFF999"
android:text="no"/>
<TextView
android:id="@+id/text_title"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_alignEnd="@+id/text_content"
android:layout_alignParentTop="true"
android:layout_alignRight="@+id/text_content"
android:background="#cecb9e"
android:gravity="center"
android:text="標題"/>
<ImageView
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignLeft="@+id/text_content"
android:layout_alignParentTop="true"
android:background="#111FFF"
android:layout_alignStart="@+id/text_content"
/>
<Button
android:id="@+id/button2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="#bfba21"
android:text="yes"
android:layout_alignBaseline="@+id/quxiao_btn"
android:layout_alignBottom="@+id/quxiao_btn"
android:layout_alignLeft="@+id/text_content"
android:layout_alignStart="@+id/text_content"/>
</RelativeLayout>
相信閱讀本文後自定義花哨的dialog肯定不成問題了。
如果覺得這篇文章對你有幫助 歡迎star我的github。也算對筆者的一種支援。
歡迎加安卓開發交流群:308372687
博主原創未經允許不許轉載。
—————————————————————————————
作者推薦:
—————————————————————————————