Date Picker |
Time Picker |
What we want to create is a DialogFragment which is actually a Dialog wrapped in a Fragment. Inside this fragment, we will be creating an AlertDialog using the AlertDialog.Builder. After creating the AlertDialog, we would now inflate a view and attach it to the AlertDialog. This view contains the TabHost and the contents DatePicker and TimePicker.
I wrote this library in Android Studio Preview (0.5.8) so doing it in Eclipse will be different and I'm not sure how to create it there.
The first thing we you need is to have the Android Support Repository and Android Support Library installed in your system. This can be installed using the Android SDK Manager.
After making sure you have installed this library, we can now start creating a library in Android Studio. Create a New Project and make sure to tick the options "Mark this project as library" and the Support Mode "Fragments". Remove the "Create activity" option.
We first create a DateTime class that will be used to manipulate Date object into different date formats and vice versa. This class can accept a Date object and can return a Date String that can be formatted or it can accept a Date String and convert it to a Date object. The class can also return the Year, Month, Day, Hour, Minute and Second.
package com.pagilagan.lib.datetimepicker.datetimepicker; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created by Arman.Pagilagan on 15/05/14. */ public class DateTime { public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; private Date mDate; private Calendar mCalendar; /** * Constructor with no parameters which will create DateTime * Object with the current date and time */ public DateTime() { this(new Date()); } /** * Constructor with Date parameter which will initialize the * object to the date specified * @param date */ public DateTime(Date date) { mDate = date; mCalendar = Calendar.getInstance(); mCalendar.setTime(mDate); } /** * Constructor with DateFormat and DateString which will be * used to initialize the object to the date string specified * @param dateFormat String DateFormat * @param dateString Date in String */ public DateTime(String dateFormat, String dateString) { mCalendar = Calendar.getInstance(); SimpleDateFormat mFormat = new SimpleDateFormat(dateFormat); try { mDate = mFormat.parse(dateString); mCalendar.setTime(mDate); } catch (ParseException e) { e.printStackTrace(); } } /** * Constructor with DateString formatted as default DateFormat * defined in DATE_FORMAT variable * @param dateString */ public DateTime(String dateString) { this(DATE_FORMAT, dateString); } /** * Constructor with Year, Month, Day, Hour, Minute, and Second * which will be use to set the date to * @param year Year * @param monthOfYear Month of Year * @param dayOfMonth Day of Month * @param hourOfDay Hour of Day * @param minuteOfHour Minute * @param secondOfMinute Second */ public DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute) { mCalendar = Calendar.getInstance(); mCalendar.set(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute); mDate = mCalendar.getTime(); } /** * Constructor with Year, Month Day, Hour, Minute which * will be use to set the date to * @param year Year * @param monthOfYear Month of Year * @param dayOfMonth Day of Month * @param hourOfDay Hour of Day * @param minuteOfHour Minute */ public DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour) { this(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, 0); } /** * Constructor with Date only (no time) * @param year Year * @param monthOfYear Month of Year * @param dayOfMonth Day of Month */ public DateTime(int year, int monthOfYear, int dayOfMonth) { this(year, monthOfYear, dayOfMonth, 0,0,0); } public Date getDate() { return mDate; } public Calendar getCalendar() { return mCalendar; } public String getDateString(String dateFormat) { SimpleDateFormat mFormat = new SimpleDateFormat(dateFormat); return mFormat.format(mDate); } public String getDateString() { return getDateString(DATE_FORMAT); } public int getYear() { return mCalendar.get(Calendar.YEAR); } public int getMonthOfYear() { return mCalendar.get(Calendar.MONTH); } public int getDayOfMonth() { return mCalendar.get(Calendar.DAY_OF_MONTH); } public int getHourOfDay() { return mCalendar.get(Calendar.HOUR_OF_DAY); } public int getMinuteOfHour() { return mCalendar.get(Calendar.MINUTE); } public int getSecondOfMinute() { return mCalendar.get(Calendar.SECOND); } }
Create the layout date_time_picker.xml which will contain the view that will be attached to the AlertDialog. This contains the TabHost, its TabWidgets, as well as the contents of the tab: DatePicker and TimePicker widgets.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/date_time" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- TabHost that will hold the Tabs and its content --> <TabHost android:id="@+id/tab_host" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- TabWidget that will hold the Tabs --> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- Layout that will hold the content of the Tabs --> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Layout for the DatePicker --> <LinearLayout android:id="@+id/date_content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <DatePicker android:id="@+id/date_picker" android:layout_width="match_parent" android:layout_height="match_parent" android:calendarViewShown="false" /> </LinearLayout> <!-- Layout for the TimePicker --> <LinearLayout android:id="@+id/time_content" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TimePicker android:id="@+id/time_picker" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </FrameLayout> </LinearLayout> </TabHost> </LinearLayout>
Then we create the class called DateTimePicker which is the DialogFragment that will be called when showing the DateTimePicker.
package com.pagilagan.lib.datetimepicker.datetimepicker; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; import android.widget.DatePicker; import android.widget.TabHost; import android.widget.TimePicker; import java.util.Date; /** * Created by Arman.Pagilagan on 15/05/14. */ public class DateTimePicker extends DialogFragment { public static final String TAG_FRAG_DATE_TIME = "fragDateTime"; private static final String KEY_DIALOG_TITLE = "dialogTitle"; private static final String KEY_INIT_DATE = "initDate"; private static final String TAG_DATE = "date"; private static final String TAG_TIME = "time"; private Context mContext; private ButtonClickListener mButtonClickListener; private OnDateTimeSetListener mOnDateTimeSetListener; private Bundle mArgument; private DatePicker mDatePicker; private TimePicker mTimePicker; // DialogFragment constructor must be empty public DateTimePicker() { } @Override public void onAttach(Activity activity) { super.onAttach(activity); mContext = activity; mButtonClickListener = new ButtonClickListener(); } /** * * @param dialogTitle Title of the DateTimePicker DialogFragment * @param initDate Initial Date and Time set to the Date and Time Picker * @return Instance of the DateTimePicker DialogFragment */ public static DateTimePicker newInstance(CharSequence dialogTitle, Date initDate) { // Create a new instance of DateTimePicker DateTimePicker mDateTimePicker = new DateTimePicker(); // Setup the constructor parameters as arguments Bundle mBundle = new Bundle(); mBundle.putCharSequence(KEY_DIALOG_TITLE, dialogTitle); mBundle.putSerializable(KEY_INIT_DATE, initDate); mDateTimePicker.setArguments(mBundle); // Return instance with arguments return mDateTimePicker; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Retrieve Argument passed to the constructor mArgument = getArguments(); // Use an AlertDialog Builder to initially create the Dialog AlertDialog.Builder mBuilder = new AlertDialog.Builder(mContext); // Setup the Dialog mBuilder.setTitle(mArgument.getCharSequence(KEY_DIALOG_TITLE)); mBuilder.setNegativeButton(android.R.string.no,mButtonClickListener); mBuilder.setPositiveButton(android.R.string.yes,mButtonClickListener); // Create the Alert Dialog AlertDialog mDialog = mBuilder.create(); // Set the View to the Dialog mDialog.setView( createDateTimeView(mDialog.getLayoutInflater()) ); // Return the Dialog created return mDialog; } /** * Inflates the XML Layout and setups the tabs * @param layoutInflater Layout inflater from the Dialog * @return Returns a view that will be set to the Dialog */ private View createDateTimeView(LayoutInflater layoutInflater) { // Inflate the XML Layout using the inflater from the created Dialog View mView = layoutInflater.inflate(R.layout.date_time_picker,null); // Extract the TabHost TabHost mTabHost = (TabHost) mView.findViewById(R.id.tab_host); mTabHost.setup(); // Create Date Tab and add to TabHost TabHost.TabSpec mDateTab = mTabHost.newTabSpec(TAG_DATE); mDateTab.setIndicator(getString(R.string.tab_date)); mDateTab.setContent(R.id.date_content); mTabHost.addTab(mDateTab); // Create Time Tab and add to TabHost TabHost.TabSpec mTimeTab = mTabHost.newTabSpec(TAG_TIME); mTimeTab.setIndicator(getString(R.string.tab_time)); mTimeTab.setContent(R.id.time_content); mTabHost.addTab(mTimeTab); // Retrieve Date from Arguments sent to the Dialog DateTime mDateTime = new DateTime((Date) mArgument.getSerializable(KEY_INIT_DATE)); // Initialize Date and Time Pickers mDatePicker = (DatePicker) mView.findViewById(R.id.date_picker); mTimePicker = (TimePicker) mView.findViewById(R.id.time_picker); mDatePicker.init(mDateTime.getYear(), mDateTime.getMonthOfYear(), mDateTime.getDayOfMonth(), null); mTimePicker.setCurrentHour(mDateTime.getHourOfDay()); mTimePicker.setCurrentMinute(mDateTime.getMinuteOfHour()); // Return created view return mView; } /** * Sets the OnDateTimeSetListener interface * @param onDateTimeSetListener Interface that is used to send the Date and Time * to the calling object */ public void setOnDateTimeSetListener(OnDateTimeSetListener onDateTimeSetListener) { mOnDateTimeSetListener = onDateTimeSetListener; } private class ButtonClickListener implements DialogInterface.OnClickListener { @Override public void onClick(DialogInterface dialogInterface, int result) { // Determine if the user selected Ok if(DialogInterface.BUTTON_POSITIVE == result) { DateTime mDateTime = new DateTime( mDatePicker.getYear(), mDatePicker.getMonth(), mDatePicker.getDayOfMonth(), mTimePicker.getCurrentHour(), mTimePicker.getCurrentMinute() ); mOnDateTimeSetListener.DateTimeSet(mDateTime.getDate()); } } } /** * Interface for sending the Date and Time to the calling object */ public interface OnDateTimeSetListener { public void DateTimeSet(Date date); } }
Lastly, we will create a wrapper class called SimpleDateTimePicker that will call the DateTimePicker and also handle the Fragment maintenance.
package com.pagilagan.lib.datetimepicker.datetimepicker; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import java.util.Date; /** * Created by Arman.Pagilagan on 16/05/14. */ public class SimpleDateTimePicker { private CharSequence mDialogTitle; private Date mInitDate; private DateTimePicker.OnDateTimeSetListener mOnDateTimeSetListener; private FragmentManager mFragmentManager; /** * Private constructor that can only be access using the make method * @param dialogTitle Title of the Date Time Picker Dialog * @param initDate Date object to use to set the initial Date and Time * @param onDateTimeSetListener OnDateTimeSetListener interface * @param fragmentManager Fragment Manager from the activity */ private SimpleDateTimePicker(CharSequence dialogTitle, Date initDate, DateTimePicker.OnDateTimeSetListener onDateTimeSetListener, FragmentManager fragmentManager) { // Find if there are any DialogFragments from the FragmentManager FragmentTransaction mFragmentTransaction = fragmentManager.beginTransaction(); Fragment mDateTimeDialogFrag = fragmentManager.findFragmentByTag( DateTimePicker.TAG_FRAG_DATE_TIME ); // Remove it if found if(mDateTimeDialogFrag != null) { mFragmentTransaction.remove(mDateTimeDialogFrag); } mFragmentTransaction.addToBackStack(null); mDialogTitle = dialogTitle; mInitDate = initDate; mOnDateTimeSetListener = onDateTimeSetListener; mFragmentManager = fragmentManager; } /** * Creates a new instance of the SimpleDateTimePicker * @param dialogTitle Title of the Date Time Picker Dialog * @param initDate Date object to use to set the initial Date and Time * @param onDateTimeSetListener OnDateTimeSetListener interface * @param fragmentManager Fragment Manager from the activity * @return Returns a SimpleDateTimePicker object */ public static SimpleDateTimePicker make(CharSequence dialogTitle, Date initDate, DateTimePicker.OnDateTimeSetListener onDateTimeSetListener, FragmentManager fragmentManager) { return new SimpleDateTimePicker(dialogTitle, initDate, onDateTimeSetListener, fragmentManager); } /** * Shows the DateTimePicker Dialog */ public void show() { // Create new DateTimePicker DateTimePicker mDateTimePicker = DateTimePicker.newInstance(mDialogTitle,mInitDate); mDateTimePicker.setOnDateTimeSetListener(mOnDateTimeSetListener); mDateTimePicker.show(mFragmentManager, DateTimePicker.TAG_FRAG_DATE_TIME); } }
Since this is a library project, we need to create another module to test this library. Create a new module with an Activity:
File > New Module
Make sure to select the correct minimum required SDK correctly, tick the Create Activity and Fragments for the support:
Continue creating a Blank Activity. It will create a new module on the same project.
We then need to set the dependency of this new module to the library we have created. Go to File > Project Structure
On the left side, find the new module that you have created, click on the Dependencies tab, and then on the right side, click on the "+" button and select Module dependency. Select the DateTimePicker module library that we have created.
package com.pagilagan.lib.datepickertest; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; import com.pagilagan.lib.datetimepicker.datetimepicker.DateTime; import com.pagilagan.lib.datetimepicker.datetimepicker.DateTimePicker; import com.pagilagan.lib.datetimepicker.datetimepicker.SimpleDateTimePicker; import java.util.Date; public class MainActivity extends ActionBarActivity implements DateTimePicker.OnDateTimeSetListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create a SimpleDateTimePicker and Show it SimpleDateTimePicker simpleDateTimePicker = SimpleDateTimePicker.make( "Set Date & Time Title", new Date(), this, getSupportFragmentManager() ); // Show It baby! simpleDateTimePicker.show(); // Or we can chain it to simplify SimpleDateTimePicker.make( "Set Date & Time Title", new Date(), this, getSupportFragmentManager() ).show(); } @Override public void DateTimeSet(Date date) { // This is the DateTime class we created earlier to handle the conversion // of Date to String Format of Date String Format to Date object DateTime mDateTime = new DateTime(date); // Show in the LOGCAT the selected Date and Time Log.v("TEST_TAG","Date and Time selected: " + mDateTime.getDateString()); } }
You have to implement the interface OnDateTimeSetListener in the calling activity as this will be called by the DateTimePicker if the date and/or time was changed.
In Logcat, you should see that this interface method is called with the date and time:
On the next blog, I will show you how you can use this library to other Project using Gradle.