1. 程式人生 > >結合Dialog的onClick()事件理解Android中的回撥函式三部曲

結合Dialog的onClick()事件理解Android中的回撥函式三部曲

App都少不了的一個需求,那就是Dialog彈窗,而且通常一個應用中都不止一個Dialog彈窗,多的時候可能達到六七個,那麼你是否會為你的應用重複寫六七個彈窗:ExitLoginDialog、DownDialog、UpdateDialog……如果是使用系統的Dialog還好,可以直接new一個就行了,但是如果有自定義的需求呢?比如這樣:

ExitLoginDialog

UpdateDialog

乍一看只用一點點文字的區別,可是如果不使用回撥機制,那麼你該如何控制彈窗的點選事件呢?很明顯點選第一個彈窗(ExitLoginDialog)的事件對應為退出登入,而點選第二個彈窗(UpdateDialog)則會新建應用升級任務。最笨的方法是這樣:分別寫兩個差不多的Dialog,找到各自的btnSure(確認按鈕),然後各自設定為:

ExitLoginDialog中:

btnSure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //此處為執行退出登入程式碼
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

UpdateDialog中:

btnSure.setOnClickListener(new View.OnClickListener() {
            @Override
public void onClick(View v) { //此處為執行升級app程式碼 } });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

實現沒有問題,但從程式碼設計上肯定是有問題的,我們發現這兩個類主要的邏輯中只有點選確認後執行的操作是不同的,其他邏輯幾乎相同,所以我們就思考是不是可以將Dialog寫成一個類,然後對外提供一個方法,當確認按鈕點選onClick()執行的時候執行我們自己設定的方法。那麼這就是事件回撥的思想的了:當一個事件執行的時候,我們希望能自己定義一個函式來執行不同的操作,這個函式就是回撥函式,它會在某個事件執行之後回撥執行。

注意:Android系統本身的onClick(),onPause()等就是回撥函式,當手指點選的時候回撥onClick()函式,當Activity介面隱藏的時候回撥onPause()函式。

下面我們以事件回撥的方法實現以上邏輯。

事件回撥設定三部曲:

1.定義回撥事件的介面 
2.宣告回撥事件 
3.對外提供設定事件回撥的介面

PS:我們新建了一個繼承自Dialog的類,命名為BaseDialog,介面回撥定義在該類中!

按照以上步驟,首先我們定義回撥事件介面:

public interface OnDialogClickListener{
        void onSureClickListener();     //當點選了確認按鈕之後執行
        void onCancelClickListener();   //當點選了取消按鈕之後執行(取消操作通常都是彈窗消失)
    }
  • 1
  • 2
  • 3
  • 4

介面定義好了,我們很明顯能看到裡面兩個函式的意思,那就是當點選了取消按鈕後執行的操作和點選了確認按鈕後執行的操作,哪裡呼叫呢?當然是點選了相應按鈕的時候了!但是我們還沒有例項來呼叫,所以我們執行步驟2先宣告一個:

private OnDialogClickListener onDialogClickListener;
  • 1

先不管初始化問題,反正我們是要呼叫的,所以就先呼叫,當然呼叫之前最好先判空一下,防止你一直都忘記初始化:

btnSure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onDialogClickListener != null) {
                    onDialogClickListener.onSureClickListener();
                }
            }
        });
btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onDialogClickListener != null) {
                    onDialogClickListener.onCancelClickListener();
                }
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

我們呼叫了這兩個方法,由於是介面,方法沒有例項化,先不管,繼續按照步驟3,對外提供設定事件回撥的介面,這一步也就是提供onDialogClickListener的初始化操作,執行完這一步,onDialogClickListener就不會為空了:

public void setOnDialogClickListener(OnDialogClickListener onDialogClickListener){
        this.onDialogClickListener = onDialogClickListener;
    }
  • 1
  • 2
  • 3

到此整個函式回撥的定義都已經完成了,那麼怎麼使用呢?

1.new 一個BaseDialog:

BaseDialog baseDialog = new BaseDialog(activity,"退出登入","確認退出登入嗎?","確認");
  • 1

2.呼叫該Dialog對外提供的設定事件回撥介面的方法:

baseDialog.setOnDialogClickListener(new BaseDialog.OnDialogClickListener() {
                @Override
                public void onSureClickListener() {
                    //執行對應操作,如退出登入,升級,下載等
                    loginout();
                    baseDialog.dismiss();
                }

                @Override
                public void onCancelClickListener() {
                    baseDialog.dismiss();
                }
            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

setOnDialogClickListener(OnBaseDialogClickListener onBaseDialogClickListener)方法需要接收一個OnBaseDialogClickListener 作為引數,我們new一個之後就需要重寫該介面的兩個未例項化的方法,所以當執行了onClick()之後呼叫onDialogClickListener.onSureClickListener();它會去回撥真正的實現方法。

這種函式回撥我們在自定義控制元件或者自定義控制元件點選事件的使用的非常多,可以看到這也是模仿了Android底層的事件方法呼叫機制,好好琢磨該實現機制,更加能夠理解回撥函式的設計思想,至少我是通過onClick()這個方法重寫點選事件來理解回撥函式的,最開始接觸Android的時候不理解回撥函式,在網上找了好多文章,雖然他們都以通俗易懂的例子來描述這種機制,比如打電話問問題什麼的……但是恕本人愚鈍,例子看懂了,但是對於回撥函式的含義還是雲裡霧裡,直到看了網上一篇類似於這種自定義控制元件然後自定義點選事件的文章之後才恍然大悟,原來這就是所謂的函式回撥!

本文重點不在彈窗,而在回撥函式的理解。這是本人對於回撥函式的一些拙見,望讀者不喜勿噴,歡迎留言討論。

以下是該BaseDialog程式碼:

BaseDialog.java

package com.shixia.diudiuma.view;

import android.app.Dialog;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.StyleRes;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.shixia.diudiuma.R;

/**
 * Created by ShiXiuwen on 2017/2/9.
 * <p>
 * Description:退出登入等的通用彈窗
 */

public class BaseDialog extends Dialog {

    private Context context;
    private String title;           //dialog標題
    private String content;         //dialog提示內容
    private String positiveBtnText; //dialog確認按鈕文字

    private Button btnSure;
    private Button btnCancel;
    private TextView tvTitle;
    private TextView tvContent;

    public BaseDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, themeResId);
    }

    public BaseDialog(@NonNull Context context, String title, String content, String positiveBtnText) {
        super(context, 0);
        this.context = context;
        this.title = title;
        this.content = content;
        this.positiveBtnText = positiveBtnText;
        initView();
        initSetting();
        initListener();
    }

    private void initSetting() {
        setCanceledOnTouchOutside(false);
        setCancelable(true);
    }

    private void initView() {
        View view = LayoutInflater.from(context).inflate(R.layout.view_dialog_base,null);
        setContentView(view);
        btnSure = (Button) view.findViewById(R.id.btn_sure);
        btnCancel = (Button) view.findViewById(R.id.btn_cancel);
        tvTitle = (TextView) view.findViewById(R.id.tv_title);
        tvContent = (TextView) view.findViewById(R.id.tv_content);

        tvTitle.setText(title);
        tvContent.setText(content);
        btnSure.setText(positiveBtnText);
    }

    private void initListener() {
        btnSure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onDialogClickListener != null) {
                    onDialogClickListener.onSureClickListener();
                }
            }
        });
        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onDialogClickListener != null) {
                    onDialogClickListener.onCancelClickListener();
                }
            }
        });
    }

    /********************** 回撥 ************************/

    private OnDialogClickListener onDialogClickListener;

    public void setOnDialogClickListener(OnDialogClickListener onDialogClickListener){
        this.onDialogClickListener = onDialogClickListener;
    }

    public interface OnDialogClickListener{
        void onSureClickListener();     //當點選了確認按鈕之後執行
        void onCancelClickListener();   //當點選了取消按鈕之後執行(取消操作通常都是彈窗消失)
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

view_dialog_base.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="680px"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="80px"
            android:background="@drawable/shape_fill_green_half_8_round"
            android:gravity="center"
            android:text="通用彈窗"
            android:textColor="@color/color_white_A"
            android:textSize="36px" />

        <View
            android:layout_width="match_parent"
            android:layout_height="2px"
            android:background="@color/grey_c" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tv_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="320px"
                android:padding="24px"
                android:text="@string/test"
                android:textColor="@color/grey_a"
                android:textSize="36px"
                android:background="@color/page_bg_color"
                android:gravity="center"/>

        </ScrollView>

        <View
            android:layout_width="match_parent"
            android:layout_height="2px"
            android:background="@color/grey_c" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btn_cancel"
                android:layout_width="0dp"
                android:layout_height="100px"
                android:layout_weight="1"
                android:background="@drawable/selector_fill_gray_half_ld_8_round"
                android:text="@string/cancel"
                android:textColor="@color/grey_a" />

            <View
                android:layout_width="2px"
                android:layout_height="match_parent"
                android:background="@color/grey_c"/>

            <Button
                android:id="@+id/btn_sure"
                android:layout_width="0dp"
                android:layout_height="100px"
                android:layout_weight="1"
                android:background="@drawable/selector_fill_gray_half_rd_8_round"
                android:text="@string/sure"
                android:textColor="@color/color_green_boss" />

        </LinearLayout>

    </LinearLayout>

</RelativeLayout>