首先抱怨一下,Android Developers網站又改版了…
先學習相關的知識:Android上日期與時間的處理
在APP UI上要取得或是設定時間和日期最方便的方式就是使用TimePickerDialog和DatePickerDialog了。Google建議我們以DialogFragment為基礎來建置各種對話方塊(Dialog)。使用DialogFragment來建置日期和時間的挑選器相關的說明與範例程式碼可以參考這裡。
因為上面介紹的程式碼太過於繁雜,對於每一個對話框都需要建立一個java程式類別,對於物件導向程式基礎不好的人來說難以處理。所以在教學上我們用直接呼叫TimePickerDialog和DatePickerDialog的方式來介紹這兩個元件,先理解元件的使用,等基礎好一些以後再轉換成獨立的DialogFragment。
程式說明與範例:

我們希望點擊日期欄位的時候可以呼叫DatePickerDialog或是TimePickerDialog,設定日期與時間。
// 簡化版的程式,只剩下與日期相關的部分
package cycu.nclab.example.accounting2018;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
public class Bookkeeping2 extends AppCompatActivity implements View.OnClickListener {
TextView mDate, mTime;
SimpleDateFormat df_date = new SimpleDateFormat("yyyy/MM/dd",
Locale.getDefault());
SimpleDateFormat df_time_am = new SimpleDateFormat("hh a",
Locale.US);
Calendar c;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bookkeeping_constraint_layout);
mDate = this.findViewById(R.id.textView7);
mTime = this.findViewById(R.id.textView8);
}
@Override
protected void onStart() {
super.onStart();
c = Calendar.getInstance(); // 取得目前日期與時間
/*
這行是一個隱式宣告,事實上是new了一個新的Calendar物件。
關於c = Calendar.getInstance到底要寫在onCreate()還是onStart()各有優劣。
寫在onCreate()裡面可以讓c的生命週期一直延續到onDestroy(),不需要去recheck c裡面的內容;
寫在onStart()裡面則是每一次從背景被翻出來就產生一個新的物件,同時就需要對新的物件的內容(初值)
做檢查與設定。同時這樣並沒有節省記憶體空間,如果我們沒有在onStop()主動釋放掉c的話,這個物件
還是會被保留在記憶體中,然而在onStart()時建立新物件的時候,舊物件則被Garbage Collector回收。
但是,在某幾次的經驗,如果APP被放到背景太久,OS有機會會去回收部分APP使用的資源,但又不回收整個
APP。這會讓APP重新回到onStart()的時候c變成一個未定義的狀態而產生系統錯誤。因此我個人的習慣是
在onStart()的時候重新整理所有的變數,檢查其數值是否正確。
*/
setListener();
initVarable();
}
@Override
protected void onStop() {
releaseListener();
super.onStop();
}
private void setListener() {
mDate.setOnClickListener(this);
mTime.setOnClickListener(this);
}
private void releaseListener() {
mDate.setOnClickListener(null);
mTime.setOnClickListener(null);
}
private void initVarable() {
// 顯示目前時間
mDate.setText(df_date.format(c.getTime()));
mTime.setText(df_time_am.format(c.getTime()));
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.textView7:
// on Date
new DatePickerDialog(this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, monthOfYear);
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 將日期寫入日期欄位
mDate.setText(df_date.format(c.getTime()));
}
}, c.get(Calendar.YEAR), c.get(Calendar.MONTH),
c.get(Calendar.DAY_OF_MONTH)).show();
break;
case R.id.textView8:
// on Time
new TimePickerDialog(this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
mTime.setText(df_time_am.format(c.getTime()));
}
}, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE),
false).show();
break;
}
}
}
這兩個欄位是使用TextView來製作的,所以我們在line 23定義兩個TextView參照(reference)。
line 37, 38利用findViewById()將物件指定給參照。
在onStart()的時候註冊監聽,在onStop()的時候取消監聽。
line 25-28定義日期和時間想要顯示的格式。
line 30 宣告一個日期參照c。
line 45 new一個新的Calendar 物件。然後在line 60行呼叫變數初始化,顯示目前的日期與時間到UI上。
line81和82的操作可以這樣解釋:由日曆物件中取出日期和時間,然後交給df_date物件將日期和時間轉換成預定的字串的形式,最後使用mDate的setText方法將字串顯示在UI上。
line 91~103為日期設定,line 107~117為時間設定,這裡反而最簡單,就是拷貝+貼上
每個Android版的的Picker都長得不太一樣,可以用模擬器測試不同版本是否能夠正常執行。

