12 August 2013

สร้าง Timer สำหรับนับเวลาถอยหลังด้วยคลาส CountDownTimer

Updated on


        รอบนี้มาต่อกันด้วยบทความเรื่อง Timer หรือตัวนับเวลากัน เพราะมีผู้ที่หลงเข้ามาอ่านหลายคนอยากทำตัวจับเวลา โดยเฉพาะกับแอพจำพวก Quiz หรือตอบปัญหาที่ต้องการนับเวลาถอยหลังในการตอบคำถามแต่ละข้อ

        ในการนับเวลาถอยหลังจะใช้ Thread หรือ Handler ก็ได้ แต่ว่าก็มีคลาส CountDownTimer ให้ใช้อยู่แล้ว ไม่ต้องเขียนเอง

        สำหรับการใช้งานคลาสดังกล่าว จะอยู่ในรูปง่ายๆดังนี้


CountDownTimer cdt = new CountDownTimer(10000, 1000) {
    public void onTick(long millisUntilFinished) {
        // Tick
    }

    public void onFinish() {
        // Finish
    }
}.start();

        สำหรับคลาสนี้จะเป็นว่ามีการกำหนดค่าให้สองตัว ตัวแรกคือเวลาทั้งหมดที่ต้องการให้นับ [10000] และเวลาที่ต้องการให้นับในแต่ละช่วง [1000] โดยทั้งสองค่านี้จะมีหน่วยเป็นมิลลิวินาที (ms)
        จากตัวอย่างที่ได้กำหนดค่าไว้เป็น 10000 กับ 1000 เมื่อคลาสนี้ทำงาน จะนับเวลาทั้งหมด 10 วินาที โดยที่ทุกๆ 1 วินาที ฟังก์ชัน onTick จะทำงาน และเมื่อนับครบ 10 วินาที ฟังก์ชัน onFinish จะทำงาน

        หลักการง่ายๆเลย ทุกๆ 1 วินาทีจะทำอะไร ครบ 10 วินาทีแล้วอยากจะให้ทำอะไร


        ดูที่ฟังก์ชัน onTick มีตัวแปรมาด้วยเป็นตัวแปร Long ซึ่งตัวแปรตัวนี้เอาไว้ดูได้ว่าเหลือเวลาอีกกี่มิลลิวินาที ดังนั้นก็เอาตัวแปรนี้มาแปลงเป็นวินาทีก็จะได้ว่า ตอนนี้เหลือเวลาอีกกี่วินาที เอาไปแสดงยังไงก็แล้วแต่ และเมื่อนับครบแล้วหรือ onFinish ก็หยุดการนับ


        มาดูตัวอย่างการใช้งานจริงคร่าวๆ กันเลยดีกว่า ในตัวอย่างนี้จะให้มีปุ่ม Toggle Button แทน Button เพราะอยากให้กดเพื่อจับเวลาและหยุดจับเวลาได้ ก็เลยใช้ Toggle Button ไปเลย จะได้สะดวกดี สำหรับภาพที่ใช้กับ Toggle Button ก็มี 4 ภาพดังนี้


        โดยนำมา Custom Toggle Button ให้ดูเหมาะสมกันหน่อย

button_start.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:state_checked="false"
        android:drawable="@drawable/button_start_pressed" />
    <item android:state_pressed="false" android:state_checked="false"
        android:drawable="@drawable/button_start_normal" />
    <item android:state_pressed="true" android:state_checked="true"
        android:drawable="@drawable/button_stop_pressed" />
    <item android:state_pressed="false" android:state_checked="true"
        android:drawable="@drawable/button_stop_normal" />
</selector>


main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Main" >

    <ToggleButton
        android:id="@+id/btnCount"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@+id/pbTimer"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_marginTop="10dp"
        android:background="@drawable/button_start"
        android:textOn=""
        android:textOff="" />

    <ProgressBar
        android:id="@+id/pbTimer"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/tvTimer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="0"
        android:textSize="40sp" />

</RelativeLayout>


        สำหรับ Progress Bar ในโค๊ด Java จะใช้คำสั่งซ่อนเอาไว้ก่อน จะแสดงก็ต่อเมื่อกดจับเวลาเท่านั้น เมื่อจับเวลาเสร็จก็ซ่อนไว้ เพื่อให้ดูเหมือนว่ากดจับเวลาแล้วมีแถบวิ่งเพื่อให้รู้ว่าจับเวลา


Main.java
package app.akexorcist.countdowntimer;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.app.Activity;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.ToggleButton;

public class Main extends Activity {
    CountDownTimer cdt;
    TextView tvTimer;
    ToggleButton btnCount;
    ProgressBar pbTimer;
    
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        tvTimer = (TextView)findViewById(R.id.tvTimer);
        
        pbTimer = (ProgressBar)findViewById(R.id.pbTimer);
        pbTimer.setVisibility(View.INVISIBLE);
        
        btnCount = (ToggleButton)findViewById(R.id.btnCount);
        btnCount.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton view
                    , boolean isChecked) {
                if(isChecked) {
                    pbTimer.setVisibility(View.VISIBLE);
                    
                    cdt = new CountDownTimer(10000, 50) {
                        public void onTick(long millisUntilFinished) {
                            String strTime = String.format("%.1f"
                                    , (double)millisUntilFinished / 1000);
                            tvTimer.setText(String.valueOf(strTime));
                        }
                        
                        public void onFinish() {
                            tvTimer.setText("0");
                            btnCount.setChecked(false);
                            pbTimer.setVisibility(View.INVISIBLE);
                        }
                    }.start();
                } else {
                    cdt.cancel();
                    tvTimer.setText("0");
                    pbTimer.setVisibility(View.INVISIBLE);
                }
            }
        });
    }
}

        สำหรับตัวอย่างนี้ก็ไม่ยากมากนัก กดเพื่อนับเวลาถอยหลัง 10 วินาที โดยแสดงเวลาที่เหลืออยู่เป็นตัวเลขทศนิยม 1 หลัก อยู่ตรงกลางจอที่มี Progress Bar แสดงอยู่รอบตัวเลข ซึ่งจะแสดงเมื่อกดจับเวลาเท่านั้น 

        และเมื่อกดเริ่มนับเวลาถอยหลัง ปุ่ม Toggle Button ก็จะเปลี่ยนเป็น Off หรือเรียกอีกอย่างว่า False ก็จะให้แสดงภาพปุ่มหยุดที่ได้กำหนดไว้ ถ้านับเวลาจนครบแล้วก็จะให้แสดงเลข 0 ทำการซ่อน Progress Bar และปุ่ม Toggle Button เป็น On เพื่อให้ผู้ใช้ได้กดจับเวลาใหม่ได้

        แต่กรณีที่ผู้ใช้กดปุ่มหยุดกลางคัน ก็จะให้หยุดนับเวลาถอยหลัง ซ่อน Progress Bar และแสดงข้อความใน Text View เป็น 0 ทันที