1. 程式人生 > >android封裝框架入門之從自定義對話方塊開始callback幫你忙

android封裝框架入門之從自定義對話方塊開始callback幫你忙

android封裝框架入門之從封裝確定、取消對話方塊開始

最近,封裝了一個輕量級的網路非同步網路請求框架MHttpUtils(傳送門),由於非常喜歡RxJava和Retrofit等的鏈式程式設計風格,於是自己搗鼓著按這種風格來封裝,期間的一些封裝思路抽取出來以備記錄查閱。

為什麼要封裝

封裝在減少重複程式碼的同時還能提高開發效率,這便是java中的提高程式碼重用性的思想,好的封裝還能提高app的效能和效率。

封裝應有哪些特點

1、易用性

封裝的框架應該要容易使用,減少程式碼量,降低入門的門檻,提供相應的使用者操作反饋回撥,使用者只用關心經常變動的地方。

2、實用性

封裝的框架應該要具有很好的實用性,否則封裝就沒有了意義

3、儘量保證單例

保持單例主要是為了減少記憶體資源的佔用率,提高app的效率。(這裡說的儘量,因為有些東西使用單例模式反而會有諸多限制)

開始封裝確定、取消對話方塊

下面通過封裝一個確定、取消對話方塊的簡單例子來看看一種封裝的思路。

以前你可能會在每個需要使用對話方塊的地方寫上下面的程式碼:

        AlertDialog.Builder builder = new AlertDialog.Builder(context);      
        builder.setTitle(title);       
        builder.setMessage(message);      
        builder.setIcon(iconResId);      
        builder.setNegativeButton("取消"
, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //取消的邏輯處理 } }); builder.setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public
void onClick(DialogInterface dialog, int which) { //確定的邏輯處理 } }); builder.create().show();

看著不多,如果是同一個類有多處呼叫的話還可以提為全域性變數,但是如果有很多activity都要呼叫的話就覺得好沒意義,必須抽取出來呀。

首先來看如何保證單例(必須單例模式啊):

//確定、取消對話方塊
public class MDialog {

    private static MDialog mDialog;

    private MDialog() {
    }

    public static MDialog build() {
        if (mDialog == null) {
            mDialog = new MDialog();
        }
        return mDialog;
    }
}

再來考慮一下哪些是使用者經常變動的屬性,寫了那麼多builder的你一定知道title、message、icon是由使用者決定的,其實既然抽取出來了,在哪裡顯示的話一定要告訴MDialog,所以還要加上一個上下文物件context。

為MDialog加上title、message、icon、context四個屬性,如何賦值呢?又如何像rxjava、retrofit等的一條鏈的就寫完整個呼叫過程呢?

我猜你肯定想到鏈式程式設計了,鏈式程式設計的核心思想就是方法的返回值為物件自己,這樣每呼叫一個方法返回的還是物件本身,那麼有多少這樣的方法就能調多少了。

完善一下程式碼:

//確定、取消對話方塊
public class MDialog {
    ...//省略了上面的程式碼
    //設定上下文物件     
    public MDialog with(Context context) {
        this.context = context;
        return this;
    }

    //設定標題
    public MDialog title(String title) {
        this.title = title;
        return this;
    }

   // 設定訊息
    public MDialog message(String message) {
        this.message = message;
        return this;
    }

   //設定圖示資源id
    public MDialog iconResId(int iconResId) {
        this.iconResId = iconResId;
        return this;
    }
}

重點來了,使用者點選確定之後的邏輯處理應該由呼叫者自行處理,這裡就要使用非常有用的回撥思想了,提供一個介面,使用者實現了這個介面,當用戶互動的時候觸發某個事件而回調給使用者實現的這個介面中。

如何定義回撥介面?使用者互動的無非就是點選確認、取消兩個按鈕,所以這樣定義:

interface IOnClickCallback {
        void onOk();//點選確定的時候回撥
        void onCancel();//點選取消的時候回撥
    }

細心的你會發現其實很多時候使用者點選取消是不需要做任何事的,如何讓onCancel方法可以重寫但又不是必須重寫呢?對,抽象類就可以做到,所以我們改造一下上面的程式碼(效果在下面給出):

  interface IOnClickCallback {
        void onOk();//點選確定的時候回撥
    }
    //這裡加上static是因為這個介面是寫在MDialog中的,屬於內部類
   public static abstract class OnClickCallback implements IOnClickCallback {
        public void onCancel() {//點選取消的時候回撥

        }
    }

最後呼叫一個show方法即顯示對話方塊:

public void show(final OnClickCallback callback) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        if (!TextUtils.isEmpty(title)) {
            builder.setTitle(title);
        }
        if (!TextUtils.isEmpty(message)) {
            builder.setMessage(message);
        }
        if (iconResId != 0) {
            builder.setIcon(iconResId);
        }
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                callback.onCancel();//點選取消的時候回撥
            }
        });
        builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                callback.onOk();//點選確認的時候回撥
            }
        });
        builder.create().show();
    }

至此,所有的封裝已經完畢,貼一下完整的程式碼:

import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;

/**
 * 自定義確定取消對話方塊
 * Created by HDL on 2016/11/22.
 */
//確定、取消對話方塊
public class MDialog {
    private static MDialog mDialog;
    private String title;//標題
    private String message;//提示內容
    private int iconResId;//圖示
    private Context context;//上下文物件


    private MDialog() {
    }


    public static MDialog build() {
        if (mDialog == null) {
            mDialog = new MDialog();
        }
        return mDialog;
    }

    //設定上下文物件
    public MDialog with(Context context) {
        this.context = context;
        return this;
    }

    // 設定標題
    public MDialog title(String title) {
        this.title = title;
        return this;
    }

    //設定訊息
    public MDialog message(String message) {
        this.message = message;
        return this;
    }

    //設定圖示
    public MDialog iconResId(int iconResId) {
        this.iconResId = iconResId;
        return this;
    }
    //顯示對話方塊
    public void show(final OnClickCallback callback) {
    //這裡建議使用V7包中的AlertDialog,導包見標頭檔案
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        if (!TextUtils.isEmpty(title)) {
            builder.setTitle(title);
        }
        if (!TextUtils.isEmpty(message)) {
            builder.setMessage(message);
        }
        if (iconResId != 0) {
            builder.setIcon(iconResId);
        }
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                callback.onCancel();
            }
        });
        builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                callback.onOk();
            }
        });
        builder.create().show();
    }

    interface IOnClickCallback {
        void onOk();//點選確定的時候回撥
    }

    public static abstract class OnClickCallback implements IOnClickCallback {
        public void onCancel() {//點選取消的時候回撥

        }
    }
}

使用

下面來看看怎麼使用(是不是沒有重寫):

大多數情況下是這樣使用的:
1、設定標題、內容、處理確定邏輯

 MDialog.build()
                .with(this)
                .title("警告")
                .message("您確定要刪除嗎?")
                .show(new MDialog.OnClickCallback() {
                    @Override
                    public void onOk() {
                        ToastUtils.showMsg(MDialogDemoActivity.this, "點選確定了");
                    }
                });

是不是沒有重寫onCancel方法?因為很多時候是不需要處理的,如果需要,重寫一下就可以了

這裡寫圖片描述

2、全部呼叫

          MDialog.build()
                .with(this)
                .title("警告")
                .message("您確定要刪除嗎?")
                .iconResId(R.mipmap.warning)
                .show(new MDialog.OnClickCallback() {
                    @Override
                    public void onOk() {
                        ToastUtils.showMsg(MDialogDemoActivity.this, "點選確定了");
                    }

                    @Override
                    public void onCancel() {
                        ToastUtils.showMsg(MDialogDemoActivity.this, "點選取消了");
                    }
                });

這裡寫圖片描述

3、只設置內容
注意:當不設定title的時候,設定了icon,icon也是不顯示出來的

         MDialog.build()
                .with(this)
                .message("您確定要刪除嗎?")
                .show(new MDialog.OnClickCallback() {
                    @Override
                    public void onOk() {
                        ToastUtils.showMsg(MDialogDemoActivity.this, "點選確定了");
                    }
                });

這裡寫圖片描述

驗證單例

要驗證其實很簡單,只需將show方法的返回值型別從void改為物件本身即MDialog,然後在三個Activity中呼叫MDialog,然後答應一下物件就知道是不是同一個物件了。

下圖是單例的驗證:

這裡寫圖片描述

是同一個吧,驗證搞定。

總結

以上只是我在封裝框架過程中使用到的思想,demo也比較簡單,有人可能會覺得dialog不會封裝這麻煩的,仁者見仁智者見智吧,只是提供一種思路,如果你覺得這篇文章還不錯,頂一下