show code block

2017年9月22日 星期五

Android元件(CountDownTimer)– 倒數計時、ProgressBar、CountDownTimer整合

前言:

前陣子收到一個問題。
其實收到問題的當下就想到怎麼做了,順便實作一下吧!
其實這三個東西感覺自己都會,整合起來就會卡卡的,可能就像英文一樣吧XD
本篇就以CountDownTimer為主。

情境大致上是這樣 —
使用者按下Button然後跳出TimePicker,選好時間按確定之後,開始倒數。
在倒數的同時,ProgressBar會跟著百分比做改動。






重點程式碼:

會用到之前寫過的東西

ProgrssBar
http://nikeru8.blogspot.tw/2017/09/android-progressbar.html

時間選擇器
http://nikeru8.blogspot.tw/2016/12/androidtimepickerdialog.html

 CountDownTimer 這是官方文檔 —
https://developer.android.com/reference/android/os/CountDownTimer.html

CountDownTimer使用方法
new CountDownTimer(30000, 1000) {

     public void onTick(long millisUntilFinished) {
         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
     }

     public void onFinish() {
         mTextField.setText("done!");
     }
  }.start();
 


上面這段直接copy官方文檔內的範例
可以看到CountDownTimer( 你要計時的毫秒 , 多少毫秒運行一次 )

上圖可以直接講完這method的所有使用方式。



完整程式碼:

activity_main.xml

 

 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hello.kaiser.progressbardemo.MainActivity">

    <LinearLayout
        android:id="@+id/bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ProgressBar
            android:id="@+id/progress_bar"
            style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"
            android:layout_weight="9" />

        <TextView
            android:id="@+id/progress_tv"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="5dp"
            android:layout_weight="1"
            android:text="0%" />

    </LinearLayout>


    <View
        android:id="@+id/line"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_below="@+id/bar_layout"
        android:layout_marginTop="20dp"
        android:background="#ffaa" />

    <TextView
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/line"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:text="time" />

    <Button
        android:id="@+id/show_picker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/show"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:onClick="timePickerBtn"
        android:text="picker" />

    <Button
        android:onClick="canelTimeBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/show_picker"
        android:layout_centerHorizontal="true"
        android:text="取消時間" />


</RelativeLayout>




在Java的部分,我連import都貼出來了,基本上照著做不會出問題。
在下方的code裡面我有下很多註解
如果還有不懂可以再提出
比較需要注意的事情是

1、SimpleDateFormat的使用,因為我只要抓取小時和分鐘,這邊只填上"HHmm"
2 、時間的相加減,其實都是用“毫秒”再作轉換
3、ProgressBar內的setMax和setProgress只能裝int,long轉換成int的方法我寫在下面





MainActivity.java
import android.app.TimePickerDialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.TimePicker;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class MainActivity extends AppCompatActivity {


    private ProgressBar mProgressBar;
    private TextView mPercentage;

    private int progress = 0;//變化參數
    private int hours, mins;
    private Context mContext;
    private TextView mShow;
    private CountDownTimer countDownTimer;
    private Button show_picker;

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

    }

    private void initView() {
        show_picker = (Button) findViewById(R.id.show_picker);
        mShow = (TextView) findViewById(R.id.show);
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        mPercentage = (TextView) findViewById(R.id.progress_tv);
    }


    //設定時間的按鈕
    public void timePickerBtn(View view) {
        //防呆
        show_picker.setClickable(true);
//        //假如重複選擇時間,暫停上一個計時器
        if (countDownTimer != null) {
            countDownTimer.cancel();
            countDownTimer.onFinish();
        }
        TimePickerDialog timepicker = new TimePickerDialog(mContext, onTimeSetListener, hours, mins, true);
        timepicker.show();
    }

    //取消倒數
    public void canelTimeBtn(View view) {
        show_picker.setClickable(true);
        mPercentage.setText("0%");
        mProgressBar.setProgress(0);
        progress = 0;
        if (countDownTimer != null) {
            countDownTimer.cancel();
            countDownTimer.onFinish();
        }
    }

    //TimePicker監聽
    TimePickerDialog.OnTimeSetListener onTimeSetListener = new TimePickerDialog.OnTimeSetListener() {

        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            //只format hours and mins
            SimpleDateFormat sdf = new SimpleDateFormat("HHmm");

            //抓取現在的時間
            Calendar calendar = Calendar.getInstance();
            int nowHours = calendar.get(Calendar.HOUR_OF_DAY);
            int nowMins = calendar.get(Calendar.MINUTE);

            Long time = null;
            //確認格式是我們在SimpleDateFormat內指定的樣子"HHmm"
            Log.d("checkpoint", " 確認選取的時間 = " + format(hourOfDay) + format(minute));
            Log.d("checkpoint", "卻線線在的時間 = " + format(nowHours) + format(nowMins));

            try {
                //選取時間 - 現在時間
                Date pickDate = sdf.parse(format(hourOfDay) + format(minute));
                Date nowDate = sdf.parse(format(nowHours) + format(nowMins));
                Long pickLongDate = pickDate.getTime();
                Long nowLongDate = nowDate.getTime();
                time = pickLongDate - nowLongDate;
                //設定ProgressBar
                mProgressBar.setMax(toIntExact(time));
                Log.d("checkpoint", "checkpoint = " + time);
            } catch (ParseException e) {
                e.printStackTrace();
                Log.d("checkpoint", "error - " + e);
            }

            if (time != null) {
                //time為毫秒 帶入倒數計時器CountDownTimer
                final Long finalTime = time;
                countDownTimer = new CountDownTimer(finalTime, 500) {
                    @Override
                    public void onFinish() {
                        mShow.setText("Done!");
                        if (progress != 0) {
                            mPercentage.setText("100%");
                            mProgressBar.setProgress(toIntExact(finalTime));
                        } else {
                            mPercentage.setText("0%");
                        }
                        show_picker.setClickable(true);
                    }

                    @Override
                    public void onTick(long millisUntilFinished) {
                        //這方法中間如果遇到個位數字,前方自動補0 "08"
                        NumberFormat f = new DecimalFormat("00");
                        long hour = (millisUntilFinished / 3600000) % 24;
                        long min = (millisUntilFinished / 60000) % 60;
                        long sec = (millisUntilFinished / 1000) % 60;

                        //換算必須用Double,如果使用int不管怎麼除都會是0
                        progress = (int) ((Double.valueOf(finalTime - millisUntilFinished) / Double.valueOf(finalTime)) * 100);
                        //在計時器內對ProgrssBar做每秒的更新
                        mProgressBar.setProgress(toIntExact(finalTime - millisUntilFinished));
                        //設定旁邊的文字%
                        mPercentage.setText(progress + "%");
                        //設定倒數計時
                        mShow.setText(f.format(hour) + ":" + f.format(min) + ":" + f.format(sec));
                    }
                }.start();
            }
        }
    };


    //重新把long變成String,中間如果遇到個位數字,前方自動補0 "08"
    private String format(long value) {
        String valueTwo = stringValue(value);
        if (valueTwo.length() == 1) {
            return "0" + valueTwo;
        }
        return valueTwo;
    }

    //轉換器long convert to String
    private String stringValue(long value) {
        return String.valueOf(value);
    }

    //轉換器 Long convert to int
    public static int toIntExact(long value) {
        if ((int) value != value) {
            throw new ArithmeticException("integer overflow");
        }
        return (int) value;
    }


}

 



歡迎提出討論喔!
DEMO:https://github.com/nikeru8/ProgressBarDemo/tree/IntegrationDemo



2 則留言:

  1. 你好,我想請問一下,要怎麼樣才能把遇到個位數補08在前面的功能刪掉呢?
    因為這樣在倒數計時的時候好像有點怪,假設設定倒數一分鐘的時候,會顯示剩下08:00:59(8小時1分鐘)
    還是設計這樣功能是有什麼特殊的作用嗎?
    在麻煩您,謝謝

    回覆刪除
    回覆
    1. 不好意思回覆晚了。
      不太懂你的意思,能再詳細一點嗎

      刪除

協程(coroutine) - 協程為什麼要學它?

 Coroutine 協程 再強調一次 協程就是由kotlin官方所提供的線程api //Thread Thread { }.start() //Executor val execu...