Requirement: When the user clicks on the TextView, a date picker should open up. The default date selected should be the date in the TextView. If the date is in the past, the DatePicker dialog's 'Set' button should be disabled. If the clickable TextView is empty, the default date in the DatePicker should be today's date.
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
This is a scenario I've already solved and am sharing here in order to help the Xamarin community. The code isn't very optimized, just FYI.
So, what we exactly need in this scenario is access to the event that the user is changing dates on the DatePicker Dialog. This can only be done if you use a DatePicker inside your own Dialog for more control. In my opinion, you cannot get access to this event if you use the default DatePickerDialog. Thus, we create a dialog extending the DialogFragment class and then implement the DatePicker inside of it. When the user clicks the TextView, we use show the fragment. Let's begin:
Here's the MainActivity:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Java.Util;
using Java.Text;
namespace DatePickerTest
{
[Activity(Label = "DatePickerTest", MainLauncher = true, Icon = "@drawable/icon", Theme = "@android:style/Theme.Holo.Light")]
public class MainActivity : Activity
{
private string dueDate;
private TextView dateLabel;
private DateTime date;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
dateLabel = (TextView)FindViewById(Resource.Id.dateLabel);
dueDate = dateLabel.Text;
dateLabel.Click += delegate { ShowDialog(); };
}
public void ShowDialog()
{
var transaction = FragmentManager.BeginTransaction();
var dialogFragment = new mDialogFragment();
dialogFragment.Show(transaction, "dialog_fragment");
}
//Used for communication with the fragment
public string GetDueDate()
{
return dueDate;
}
//Used for communication with the fragment
public void SetDueDate(DateTime date)
{
//Additional check so that date isn't set in the past
if (date < DateTime.Now.Date)
Toast.MakeText(this, "Something went wrong! Please try again", ToastLength.Long).Show();
else
{
SimpleDateFormat MdyFormat = new SimpleDateFormat("MM/dd/yyyy");
dueDate = MdyFormat.Format(Date.Parse(date.ToString()));
dateLabel.Text = dueDate;
}
}
}
}
Main.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/MyButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/Hello" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:background="#FAFAFA"
android:layout_height="wrap_content">
<TextView
android:id="@+id/dueDateLabel"
android:layout_height="45dp"
android:layout_width="wrap_content"
android:text="Due Date:"
android:padding="15dp"
android:textColor="#2E2E2E" />
<TextView
android:id="@+id/dateLabel"
android:layout_height="45dp"
android:layout_width="fill_parent"
android:hint="Some Date"
android:textColor="#2E2E2E"
android:text="03/16/2015" />
</LinearLayout>
</LinearLayout>
MDialogFragment.cs :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Java.Util;
using Java.Text;
namespace DatePickerTest
{
public class mDialogFragment : DialogFragment
{
DatePicker picker;
private MainActivity MActivity;
private int Year, Month, Day;
private string DueDate;
private DateTime SelectedDueDate;
private string tempString = "";
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//Get the instance of the MainActivity
MActivity = (MainActivity) this.Activity;
//Get the currently set due date
DueDate = MActivity.GetDueDate();
//Get instance of the Calendar
Calendar Today = Calendar.Instance;
//Update the class variables
Year = Today.Get(Calendar.Year);
Month = Today.Get(Calendar.Month);
Day = Today.Get(Calendar.Date);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflating the dialog layout
var view = inflater.Inflate(Resource.Layout.MDialogLayout, container, false);
//Finding all the views in it:
var cancel = (Button)view.FindViewById(Resource.Id.cancel);
var set = (Button)view.FindViewById(Resource.Id.set);
picker = (DatePicker)view.FindViewById(Resource.Id.pickerdate);
//DatePicker flag to make it look like the default DatePicker
picker.CalendarViewShown = false;
//Checking to see if current date is in the past, if YES, disable the 'Set' button
if ((DateTime.Parse(DueDate) < DateTime.Now)) { set.Enabled = false; }
//Initate the picker with the current due date OR today's date
picker.Init(GetDefaultYear(), GetDefaultMonth(), GetDefaultDayOfMonth(), new onDateChangedListener((picker1, year, month, day) =>
{
//Getting the DatePicker value in a string
tempString = (month + 1) + "/" + day + "/" + year;
//Parsing the value into a variable
SelectedDueDate = (DateTime.Parse(tempString).Date);
//Setting the MDatePicker dialog's Title
Dialog.SetTitle(GetDateDetails(SelectedDueDate));
//Enable/Disalbe 'Set' button depending on the condition
if (SelectedDueDate >= DateTime.Now.Date)
set.Enabled = true;
else
set.Enabled = false;
}));
//Setting Dialog Title for the first time when it opens
Dialog.SetTitle(GetDateDetails(DateTime.Parse(DueDate)));
//Click function for Cancel button
cancel.Click += delegate{Dismiss();};
//Click function for Set button
set.Click += (object sender, EventArgs e) =>
{
SetSelectedDueDate(sender, e);
};
return view;
}
private string GetDateDetails(DateTime date)
{
string DateDetails;
Calendar cal = Calendar.Instance;
SimpleDateFormat DayOfWeekFormat = new SimpleDateFormat("EEE");
SimpleDateFormat MonthFormat = new SimpleDateFormat("MMM");
DateDetails = DayOfWeekFormat.Format(Date.Parse(date.ToString())) + ", " + date.Day + " " + MonthFormat.Format(Date.Parse(date.ToString())) + " " + date.Year;
return DateDetails;
}
private void SetSelectedDueDate(object sender, EventArgs e)
{
MActivity.SetDueDate(SelectedDueDate);
Dismiss();
}
private int GetDefaultMonth()
{
//The currently set due date is in the format "MM/DD/YYYY"
if(MActivity.GetDueDate()==null || MActivity.GetDueDate() == "")
return Month;
return Convert.ToInt32(MActivity.GetDueDate().Substring(0, 2)) - 1;
}
private int GetDefaultDayOfMonth()
{
if (MActivity.GetDueDate() == null || MActivity.GetDueDate() == "")
return Day;
return Convert.ToInt32(MActivity.GetDueDate().Substring(3, 2));
}
private int GetDefaultYear()
{
if (MActivity.GetDueDate() == null || MActivity.GetDueDate() == "")
return Year;
return Convert.ToInt32(MActivity.GetDueDate().Substring(6, 4));
}
}
//We need this class and interface implementation to create and Init the DatePicker
class onDateChangedListener : Java.Lang.Object, DatePicker.IOnDateChangedListener
{
Action<DatePicker, int, int, int> callback;
public onDateChangedListener(Action<DatePicker, int, int, int> callback)
{
this.callback = callback;
}
public void OnDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)
{
callback(view, year, monthOfYear, dayOfMonth);
}
}
}
MDialogLayout.axml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<DatePicker
android:id="@+id/pickerdate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle" />
<Button
android:id="@+id/set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SET"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:paddingTop="1dp" />
</LinearLayout>
</LinearLayout>