首先抱怨一下,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都長得不太一樣,可以用模擬器測試不同版本是否能夠正常執行。