動手造輪子——用Builder模式擼一個通用版本的Dialog

晨雨細曲發表於2018-11-08

前言

在Android開發中我們常常需要使用Dialog來處理一些彈窗操作。雖然Android系統本身為我們封裝了一個自帶的彈窗Dialog,但是由於Android作業系統的不同,導致了每個手機彈窗頁面的不同,以至於我們很難用系統的去統一樣式。並且UI會覺得系統的彈窗過於醜陋,希望自己來做一個於是我們便需要自己來處理一個Dialog彈窗。今天叫大家利用Builder構造者模式自己來封裝一個彈窗Dialog。

需求分析

首先確定一下,彈窗我們需要哪些功能?

  1. 需要可以自己自定義樣式。因為每個彈窗會根據邏輯和需求的不同擁有不同的樣式。例如有些彈窗有title標題,有些只有message提示;有些有確定和取消按鈕,有些又只有一個確定按鈕。我們必須用一套程式碼來載入不同的樣式才行。
  2. 設定title或者message的內容。這裡可以根據傳入的不同id設定不同文字內容。
  3. 設定按鈕的點選事件
  4. 設定是否需要點選彈窗外部使得彈窗消失。

開始操作

分析完了需求之後我們便可以根據需求來擼程式碼了。

首先我們寫一個CommonDialog類讓他繼承Dialog。 此時我們必須複寫onCreat(),在onCreat()我麼進行一些設定操作。這裡我們設定setContentView()載入佈局樣式,設定setCancelable()是否點選彈窗以外使得彈窗消失。

public class CommonDialog extends Dialog {
    private Context context;
    private View view;
    private boolean cancelTouchout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(view);
        setCancelable(cancelTouchout);
    }
}
複製程式碼

其實這時候我們就開始要編寫Builder構造者模式的程式碼了。

構造者模式,顧名思義,就是根據我們的需要往程式碼上新增需求。例如,我們需要設定title標題的內容,此時我們就新增上這個功能,如果需要設定message內容的功能我們也設定上這個功能。如果不需要則不新增這段程式碼。

先根據不同樣式載入不同的佈局。此時我們利用LayoutInflater來處理。佈局樣式由引數傳遞進來。

        public Builder view(int resView) {
            view = LayoutInflater.from(context).inflate(resView, null);
            return this;
        }
複製程式碼

其次,再根據是否可以點選彈窗外部使得彈窗消失。

        public Builder cancelTouchout(boolean val) {
            cancelTouchout = val;
            return this;
        }
複製程式碼

傳入的val若為true代表可以消失,如果傳入的為false則代表不能消失。

緊接著我們再來看看如果根據title和message來設定不同的內容。 這裡我們的引數設定為兩個,一個是需要設定內容的id,一個是設定內容的文字title/message。我們先通過findViewById去查詢到這個View中的控制元件,之後再根據文字內容將文字設定到控制元件上去。

        //設定標題title
        public Builder setTitle(int viewRes,String title){
            TextView txtTitle = (TextView) view.findViewById(viewRes);
            txtTitle.setText(title);
            return this;
        }
        
        //設定內容message
        public Builder setMessage(int viewRes,String message){
            TextView txtMessage = (TextView)view.findViewById(viewRes);
            txtMessage.setText(message);
            return this;
        }
複製程式碼

然後我們就該去設定點選事件了。 點選事件我們可以借鑑設定title和message的情況來處理,通過傳入一個點選控制元件的id來查詢id,之後通過View.OnClickListener來回撥這個點選事件處理點選邏輯即可。

        public Builder addViewOnclick(int viewRes,View.OnClickListener listener){
            view.findViewById(viewRes).setOnClickListener(listener);
            return this;
        }
複製程式碼

最後我們只需要把最後寫一個build返回一個CommonDialog即可。但是如果此時我們呼叫使用這個CommonDialog時你會發現我們不管如何設定載入的View,發現大小其實都是不變的。這是怎麼回事?其實這是因為我們的背景色一起被設定進入了View佈局中。如果此時我們把我們載入的View背景設定為黑色(#000000),就會神奇的發現,在View旁邊還有一些白色的存在。正是因為這些白色,導致了我們無論如何設定彈窗寬度都顯示的是原來的大小。如果你要設定為圓角,那更是不太可能。

動手造輪子——用Builder模式擼一個通用版本的Dialog

那該如何處理這種情況?其實很簡單,我們僅僅需要設定一下將彈窗風格的style設定一個透明的背景即可處理好這種情況。

我們在value-style中設定一個style風格樣式,將背景設定為透明即可。

    <!--Dialog將白色背景變透明-->
    <style name="Dialog" parent="android:style/Theme.Dialog">
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
    </style>
複製程式碼

此時我們再在CommonDialog中設定一個style樣式,根據傳入的樣式來處理風格。

        public Builder style(int resStyle) {
            this.resStyle = resStyle;
            return this;
        }
複製程式碼

最後我們再寫兩個CommonDialog的構造方法,根據是否有傳入style來呼叫不同父類。完成操作。

    private CommonDialog(Builder builder) {
        super(builder.context);
        context = builder.context;
        cancelTouchout = builder.cancelTouchout;
        view = builder.view;
    }


    private CommonDialog(Builder builder, int resStyle) {
        super(builder.context, resStyle);
        context = builder.context;
        cancelTouchout = builder.cancelTouchout;
        view = builder.view;
    }
複製程式碼

動手造輪子——用Builder模式擼一個通用版本的Dialog

到此我們的整個程式碼就全部操作完畢。

使用

使用起來也非常簡單,我們只需要建立一個CommonDialog.Builder根據不同的業務需求來新增不同操作即可。

        CommonDialog build;

        //dialog彈窗
        private void dialog(){
         build = new CommonDialog.Builder(this)
                .view(R.layout.dialog)//設定彈窗的樣式layout
                .style(R.style.Dialog) //設定主題,這裡可以將背景設為透明,這樣只顯示你需要顯示的dialog部分
                .cancelTouchout(true) //設定點選dialog之外是否彈窗消失,true為消失,false為不消失
                .setTitle(R.id.txt_title, "這是一個彈窗標題")//根據id來設定標題的顯示文字
                .setMessage(R.id.txt_message, "這是一個彈窗訊息內容")//根據id來設定訊息內容的顯示文字
                .addViewOnclick(R.id.txt_sure, new View.OnClickListener() {//處理確認點選事件
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "點選了確定按鈕", Toast.LENGTH_SHORT).show();
                        build.dismiss();
                    }
                })
                .addViewOnclick(R.id.txt_cancel, new View.OnClickListener() {//處理取消的點選事件
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "點選了取消按鈕", Toast.LENGTH_SHORT).show();
                        build.dismiss();
                    }
                }).build();

                build.show();

    }

複製程式碼

程式碼已經上傳至github,只需將CommonDialog這個類複製到自己專案中再複製style中的Dialog即可使用,方便快捷。歡迎start。github傳送門

有興趣可以關注我的小專欄,學習更多職場產品思考知識:小專欄

動手造輪子——用Builder模式擼一個通用版本的Dialog

相關文章