21 February 2015

ลองหัดสร้าง Class และ Listener กันเถอะ - ตอนที่ 3

Updated on

        ยินดีด้วยกับผู้ที่หลงเข้ามาอ่านที่อ่านตั้งแต่ตอนที่ 1 จนถึงตอนนี้ เพราะเขียนไปเขียนมาดันกลายเป็นบทความไตรภาคซะงั้น แต่ก็ทนๆอ่านกันให้จบหน่อยนะ เพราะนี่คือบทความสุดท้ายของเรื่องนี้แล้ว

        โดยบทความนี้ก็จะยกตัวอย่างมาจากตอนที่ 2 มาทำให้มันเรียกใช้งานได้ง่ายขึ้นกว่านี้อีก

บทความทั้งหมดในชุดนี้

        • ลองหัดสร้าง Class และ Listener กันเถอะ - ตอนที่ 1
        • ลองหัดสร้าง Class และ Listener กันเถอะ - ตอนที่ 2
        • ลองหัดสร้าง Class และ Listener กันเถอะ - ตอนที่ 3

มาเริ่มกันเถอะ

        จากเดิม ถ้าเจ้าของบล็อกอยากเรียกใช้งาน MyAlertDialog โดยกำหนดข้อความด้วย ก็จะใช้คำสั่งแบบนี้

MyAlertDialog dialog = new MyAlertDialog(this);
dialog.setMessage("ข้อความ");
dialog.setOnDialogDismissListener(MainActivity.this);
dialog.show();

        และถ้าอยากกำหนด Cancelable เป็น False (กดนอกพื้นที่ Dialog เพื่อปิดไม่ได้) ก็จะใช้คำสั่งแบบนี้

MyAlertDialog dialog = new MyAlertDialog(this);
dialog.setMessage("ข้อความ");
dialog.setCancelable(false);
dialog.show();


        และถ้าอยากกำหนดให้มี OnDialogDismissListener ด้วยก็จะใช้คำสั่งแบบนี้

MyAlertDialog dialog = new MyAlertDialog(this);
dialog.setMessage(R.string.android_is_great);
dialog.setCancelable(false);
dialog.setOnDialogDismissListener(new MyAlertDialog.OnDialogDismissListener() {
    @Override
    public void onDismiss() {

    }
});
dialog.show();

        จะเห็นว่ายิ่งกำหนดเยอะก็ยิ่งใช้จำนวนบรรทัดเยอะ ดังนั้นเจ้าของบล็อกจะมาทำให้มันรวบรัดกว่านี้หน่อยดีกว่า

        นั่นก็คือทำเป็น Static Method ซะเลย ซึ่งข้อดีคือรวบรัดและจบในคำสั่งชุดเดียว แต่ข้อเสียก็คือไม่สามารถกำหนดค่าแยกกันได้ (เพราะต้องกำหนดทั้งหมดในคำสั่งเดียว)

        โดยการทำ Static Method ให้กับ MyAlertDialog จะเริ่มจากโค๊ดแบบนี้

package com.example.akexorcist.customdialogclass;

import android.content.Context;

public class MyAlertDialog {
    public static void show(Context context, CharSequence message
            , boolean cancelable, OnDialogDismissListener listener) {

    }

    public interface OnDialogDismissListener {
        public void onDismiss();
    }
}

       Static Method จะตั้งเป็นชื่ออะไรก็ได้ แต่เพื่อความเหมาะสมก็ขอใช้ชื่อว่า show เพราะนี่จะเป็นคำสั่งแสดง Dialog โดยจะเห็นว่าเจ้าของบล็อกกำหนด Parameter ไว้ยาวเหยียดเลยเพื่อให้กำหนดค่าสำหรับ Dialog ในทีเดียวจบ

        จากนั้นเจ้าของบล็อกก็เอาคำสั่งเดิมมาเรียบเรียงใหม่ในนี้ จะได้ออกมาเป็น

MyAlertDialog.java
package com.example.akexorcist.customdialogclass;

import android.app.Dialog;
import android.content.Context;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

public class MyAlertDialog {

    public static void show(Context context, CharSequence message
            , boolean cancelable, OnDialogDismissListener listener) {

        final Dialog dialog = new Dialog(context);
        dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.layout_dialog);
        dialog.setCancelable(cancelable);

        Button buttonOk = (Button) dialog.findViewById(R.id.button_ok);
        buttonOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        TextView tvMessage = (TextView) dialog.findViewById(R.id.tv_message);
        tvMessage.setText(message);

        dialog.show();
    }

    public interface OnDialogDismissListener {
        public void onDismiss();
    }
}

        โดย message ก็จะเอามากำหนดให้กับ Text View และ cancelable ก็จะมากำหนดให้กับ Dialog จากนั้นก็สั่งให้แสดง Dialog ใน Method นี้เลย

        ส่วน listener ยังไม่ได้เพิ่ม เพราะเจ้าของบล็อกจะเอามาอธิบายทีหลัง


        Lisetener ที่กำหนดลงไปใน Method ก็จะเรียกใช้งานแบบนี้

MyAlertDialog.show(this, "Android is great", false, new MyAlertDialog.OnDialogDismissListener() {
    @Override
    public void onDismiss() {

    }
});

        จบในตัวเลยใช่มั้ยล่ะ


        แล้วถ้าอยากกำหนดข้อความเป็น String Resource แทนล่ะ?

        ให้กลับไปที่ MyAlertDialog อีกครั้ง แล้วเพิ่ม Static Method อีกตัวขึ้นมาเพื่อให้รองรับ String Resource

public static void show(Context context, int resourceId
        , boolean cancelable, OnDialogDismissListener listener) {
    show(context, context.getString(resourceId), cancelable, listener);
}

        จะเห็นว่าเจ้าของบล็อก Overload Method แล้วเรียกไปที่ Method ตัวหลักอีกที แต่ทว่าจะมีการแปลงจาก String Resource ให้เป็น String เพื่อส่งไป Method ตัวหลักด้วย

        และถ้าไม่อยากกำหนด Listener ก็ Overload Method ขึ้นมาใหม่แทนแล้วส่ง Null ไปให้ Method หลัก

public static void show(Context context, int resourceId
        , boolean cancelable) {
    show(context, context.getString(resourceId), cancelable, null);
}

        จึงเป็นที่มาว่าทำไมถึงต้องมีการเช็ค Null ก่อนจะเรียกใช้งานทุกครั้ง

        เมื่อลองนั่งคิดดูก็จะได้ Overload Method ดังนี้

        • context, message, cancelable, listener
        • context, resourceId, cancelable, listener
        • context, message, cancelable
        • context, resourceId, cancelable
        • context, message, listener
        • context, resourceId, listener
        • context, message
        • context, resourceId

        ดังนั้นโค๊ดก็จะออกมาลักษณะดังนี้


MyAlertDialog.java
package com.example.akexorcist.customdialogclass;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

public class MyAlertDialog {
    public static void show(Context context, int resourceId) {
        show(context, context.getString(resourceId), true, null);
    }

    public static void show(Context context, CharSequence message) {
        show(context, message, true, null);
    }
    public static void show(Context context, int resourceId
            , final OnDialogDismissListener listener) {
        show(context, context.getString(resourceId), true, listener);
    }

    public static void show(Context context, CharSequence message
            , OnDialogDismissListener listener) {
        show(context, message, true, listener);
    }

    public static void show(Context context, int resourceId
            , boolean cancelable) {
        show(context, context.getString(resourceId), cancelable, null);
    }

    public static void show(Context context, CharSequence message
            , boolean cancelable) {
        show(context, message, cancelable, null);
    }

    public static void show(Context context, int resourceId
            , boolean cancelable, final OnDialogDismissListener listener) {
        show(context, context.getString(resourceId), cancelable, listener);
    }

    public static void show(Context context, CharSequence message
            , boolean cancelable, final OnDialogDismissListener listener) {

        final Dialog dialog = new Dialog(context);
        dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.layout_dialog);
        dialog.setCancelable(cancelable);
        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if(listener != null)
                    listener.onDismiss();
            }
        });

        Button buttonOk = (Button) dialog.findViewById(R.id.button_ok);
        buttonOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        TextView tvMessage = (TextView) dialog.findViewById(R.id.tv_message);
        tvMessage.setText(message);

        dialog.show();
    }

    public interface OnDialogDismissListener {
        public void onDismiss();
    }
}

        ทำไมต้อง Overload ซะเยอะแยะล่ะ?

        เหตุผลง่ายๆคือเผื่อการเรียกใช้งานในกรณีที่แตกต่างกันออกไปนั่นเอง แต่ถ้าอันไหนไม่ได้ใช้จริงๆก็เอาออกซะ แล้ว Overload อันที่เรียกใช้งานก็พอ (เจ้าของบล็อกแค่ยกตัวอย่างเท่านั้น)

     
        การสร้าง Class แบบใช้ Constructor (ตอนที่ 2) กับทำ Static Method (ตอนที่ 3) แบบไหนดีกว่ากัน?

        ไม่มีอันไหนดีกว่านะครับ เพราะขึ้นอยู่กับการใช้งานของผู้ที่หลงเข้ามาอ่านเอง เจ้าของบล็อกแค่นำเสนอรูปแบบในการสร้างที่เจ้าของบล็อกใช้อยู่บ่อยๆ เพื่อที่ว่าผู้ที่หลงเข้ามาอ่านจะได้ทำความเข้าใจแล้วลองนำไปประยุกต์เป็นรูปแบบของตัวเอง (และสำหรับคนที่ยังสร้าง Class กับ Listener เองไม่เป็นด้วย)


        เอ้าเสร็จแล้ว!! ดังนั้นมาดูสรุปโค๊ดทั้งหมดในบทความนี้กัน!!! (อันไหนไม่ได้พูดถึงก็คือไม่ได้มีการแก้ไขอะไร)

MainActivity.java
package com.example.akexorcist.customdialogclass;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener, MyAlertDialog.OnDialogDismissListener {
    Button buttonAlert = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        buttonAlert = (Button) findViewById(R.id.button_alert);
        buttonAlert.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_alert:
                MyAlertDialog.show(this, R.string.android_is_great, this);
                break;
        }
    }

    @Override
    public void onDismiss() {
        Toast.makeText(this, R.string.dialog_closed, Toast.LENGTH_SHORT).show();
    }
}


activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/orange"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:id="@+id/button_alert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/selector_button"
        android:text="@string/ok"
        android:textColor="@color/orange"
        android:textSize="@dimen/text_size" />
</LinearLayout>


MyAlertDialog.java
package com.example.akexorcist.customdialogclass;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

public class MyAlertDialog {
    public static void show(Context context, int resourceId) {
        show(context, context.getString(resourceId), true, null);
    }

    public static void show(Context context, CharSequence message) {
        show(context, message, true, null);
    }
    public static void show(Context context, int resourceId
            , final OnDialogDismissListener listener) {
        show(context, context.getString(resourceId), true, listener);
    }

    public static void show(Context context, CharSequence message
            , OnDialogDismissListener listener) {
        show(context, message, true, listener);
    }

    public static void show(Context context, int resourceId
            , boolean cancelable) {
        show(context, context.getString(resourceId), cancelable, null);
    }

    public static void show(Context context, CharSequence message
            , boolean cancelable) {
        show(context, message, cancelable, null);
    }

    public static void show(Context context, int resourceId
            , boolean cancelable, final OnDialogDismissListener listener) {
        show(context, context.getString(resourceId), cancelable, listener);
    }

    public static void show(Context context, CharSequence message
            , boolean cancelable, final OnDialogDismissListener listener) {

        final Dialog dialog = new Dialog(context);
        dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.layout_dialog);
        dialog.setCancelable(cancelable);
        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if(listener != null)
                    listener.onDismiss();
            }
        });

        Button buttonOk = (Button) dialog.findViewById(R.id.button_ok);
        buttonOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        TextView tvMessage = (TextView) dialog.findViewById(R.id.tv_message);
        tvMessage.setText(message);

        dialog.show();
    }

    public interface OnDialogDismissListener {
        public void onDismiss();
    }
}


layout_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_dialog_bg"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="@dimen/dialog_padding">

    <TextView
        android:id="@+id/tv_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="@dimen/dialog_padding"
        android:gravity="center"
        android:text="@string/do_not_press"
        android:textColor="@color/white"
        android:textSize="@dimen/text_size" />

    <Button
        android:id="@+id/button_ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:background="@drawable/selector_button"
        android:text="@string/ok"
        android:textColor="@color/orange"
        android:textSize="@dimen/text_size" />
</LinearLayout>


colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <color name="orange">#f1592a</color>
    <color name="white">#ffffff</color>
    <color name="gray">#999999</color>

</resources>


strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">CustomDialogClass</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>

    <string name="dialog_closed">Dialog Closed</string>
    <string name="android_is_great">Android is Great!</string>
    <string name="do_not_press">Hey!\nDon\'t press this button</string>
    <string name="ok">OK</string>

</resources>


dimens.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <dimen name="dialog_button_radius">100dp</dimen>
    <dimen name="dialog_bg_radius">20dp</dimen>
    <dimen name="dialog_padding">20dp</dimen>
    <dimen name="dialog_button_padding_vertical">20dp</dimen>
    <dimen name="dialog_button_padding_horizontal">40dp</dimen>

    <dimen name="text_size">18sp</dimen>

</resources>


shape_dialog_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid
        android:color="@color/orange" />

    <corners
        android:radius="@dimen/dialog_bg_radius" />

</shape>


shape_button_bg_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid
        android:color="@color/white" />

    <corners
        android:radius="@dimen/dialog_button_radius" />

    <padding
        android:left="@dimen/dialog_button_padding_horizontal"
        android:right="@dimen/dialog_button_padding_horizontal"
        android:top="@dimen/dialog_button_padding_vertical"
        android:bottom="@dimen/dialog_button_padding_vertical" />

</shape>


shape_button_bg_pressed.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid
        android:color="@color/gray" />

    <corners
        android:radius="@dimen/dialog_button_radius" />

    <padding
        android:left="@dimen/dialog_button_padding_horizontal"
        android:right="@dimen/dialog_button_padding_horizontal"
        android:top="@dimen/dialog_button_padding_vertical"
        android:bottom="@dimen/dialog_button_padding_vertical" />

</shape>


selector_button.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_pressed="true"
        android:drawable="@drawable/shape_button_bg_pressed" />

    <item
        android:drawable="@drawable/shape_button_bg_normal" />

</selector>


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.akexorcist.customdialogclass" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

        จบแล้วจ้า~