1. 程式人生 > >安卓下Builder模式解析+自定義Dialog實戰演練

安卓下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

博主原創未經允許不許轉載。

這裡寫圖片描述

—————————————————————————————

作者推薦:

—————————————————————————————