[Visual] Material Pickers (#5194)
authorShane Neuville <shane94@hotmail.com>
Mon, 25 Feb 2019 03:21:39 +0000 (20:21 -0700)
committerGitHub <noreply@github.com>
Mon, 25 Feb 2019 03:21:39 +0000 (20:21 -0700)
* [Material] Change Picker EditText fields to Material

* [Visual] add material placeholder properties

* - add ios placeholder changes

* - remove placeholder apis

* - shrink entry fields when they have no place holder

* - invalidate measure

* - remove marshalling

37 files changed:
Xamarin.Forms.Controls/ControlGalleryPages/VisualGallery.xaml
Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
Xamarin.Forms.Material.iOS/IMaterialEntryRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Material.iOS/MaterialDatePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Material.iOS/MaterialEntryRenderer.cs
Xamarin.Forms.Material.iOS/MaterialPickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Material.iOS/MaterialTextField.cs [new file with mode: 0644]
Xamarin.Forms.Material.iOS/MaterialTimePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Material.iOS/Properties/AssemblyInfo.cs
Xamarin.Forms.Material.iOS/Xamarin.Forms.Material.iOS.csproj
Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs
Xamarin.Forms.Platform.Android/Material/MaterialColors.cs
Xamarin.Forms.Platform.Android/Material/MaterialDatePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialEntryRenderer.cs
Xamarin.Forms.Platform.Android/Material/MaterialFormsEditText.cs
Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextBase.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextManager.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayout.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayoutBase.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialPickerEditText.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialPickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialPickerTextInputLayout.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Material/MaterialTimePickerRenderer.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/PickerManager.cs [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/EntryRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/FormsEditText.cs
Xamarin.Forms.Platform.Android/Renderers/PickerEditText.cs
Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs
Xamarin.Forms.Platform.Android/Resources/Layout/MaterialPickerTextInput.axml [new file with mode: 0644]
Xamarin.Forms.Platform.Android/Resources/Layout/TextInputLayoutFilledBox.axml
Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
Xamarin.Forms.Platform.iOS/Renderers/DatePickerRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/PickerRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/TimePickerRenderer.cs

index 983d5d2..527ddbe 100644 (file)
             <Label Text="Custom (Disabled)" Margin="0,0,0,-10" />
             <Slider Minimum="-100" Maximum="100" IsEnabled="false"
                     ThumbColor="{StaticResource DarkRedColor}" MaximumTrackColor="{StaticResource SecondaryColor}" MinimumTrackColor="{StaticResource PrimaryColor}" />
+
+
+            <Label Text="Pickers" FontSize="Large"></Label>
+            <Label Text="Date Picker" Margin="0,0,0,-10" />
+            <DatePicker></DatePicker>
+            <Label Text="Time Picker" Margin="0,0,0,-10" />
+            <TimePicker></TimePicker>
+            <Label Text="Picker" Margin="0,0,0,-10" />
+            <Picker  Title="Select a monkey">
+                <Picker.ItemsSource>
+                    <x:Array Type="{x:Type x:String}">
+                        <x:String>Baboon</x:String>
+                        <x:String>Capuchin Monkey</x:String>
+                        <x:String>Blue Monkey</x:String>
+                        <x:String>Squirrel Monkey</x:String>
+                        <x:String>Golden Lion Tamarin</x:String>
+                        <x:String>Howler Monkey</x:String>
+                        <x:String>Japanese Macaque</x:String>
+                    </x:Array>
+                </Picker.ItemsSource>
+            </Picker>
             
             <Label Text="Steppers" FontSize="Large" />
             <Label Text="Default" Margin="0,0,0,-10" />
index 45d72b7..9b817ce 100644 (file)
        </ItemGroup>
        <Import Project="..\Xamarin.Flex\Xamarin.Flex.projitems" Label="Shared" Condition="Exists('..\Xamarin.Flex\Xamarin.Flex.projitems')" />
        <UsingTask TaskName="XFCorePostProcessor.Tasks.FixXFCoreAssembly" AssemblyFile="..\XFCorePostProcessor.Tasks\bin\Debug\net461\XFCorePostProcessor.Tasks.dll" />
-       <Target
-               Condition="$(DesignTimeBuild) != true AND $(BuildingProject) == true"
-               AfterTargets="AfterCompile"
-               Name="XFCorePostProcessor"
-               Inputs="$(IntermediateOutputPath)$(TargetFileName)"
-               Outputs="$(IntermediateOutputPath)XFCorePostProcessor.stamp">
-           <Touch
-                   Files="$(IntermediateOutputPath)XFCorePostProcessor.stamp"
-                   AlwaysCreate="True" />
-           <FixXFCoreAssembly
-                   Assembly="$(IntermediateOutputPath)$(TargetFileName)"
-                   ReferencePath="@(ReferencePath)" />
+       <Target Condition="$(DesignTimeBuild) != true AND $(BuildingProject) == true" AfterTargets="AfterCompile" Name="XFCorePostProcessor" Inputs="$(IntermediateOutputPath)$(TargetFileName)" Outputs="$(IntermediateOutputPath)XFCorePostProcessor.stamp">
+           <Touch Files="$(IntermediateOutputPath)XFCorePostProcessor.stamp" AlwaysCreate="True" />
+           <FixXFCoreAssembly Assembly="$(IntermediateOutputPath)$(TargetFileName)" ReferencePath="@(ReferencePath)" />
        </Target>
 </Project>
diff --git a/Xamarin.Forms.Material.iOS/IMaterialEntryRenderer.cs b/Xamarin.Forms.Material.iOS/IMaterialEntryRenderer.cs
new file mode 100644 (file)
index 0000000..168d8a4
--- /dev/null
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Foundation;
+using UIKit;
+
+namespace Xamarin.Forms.Platform.iOS.Material
+{
+       public interface IMaterialEntryRenderer
+       {
+               Color TextColor { get; }
+               Color PlaceholderColor { get; }
+               Color BackgroundColor { get; }
+               string Placeholder { get; }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Material.iOS/MaterialDatePickerRenderer.cs b/Xamarin.Forms.Material.iOS/MaterialDatePickerRenderer.cs
new file mode 100644 (file)
index 0000000..8664295
--- /dev/null
@@ -0,0 +1,59 @@
+using System.ComponentModel;
+using UIKit;
+
+namespace Xamarin.Forms.Platform.iOS.Material
+{
+       public class MaterialDatePickerRenderer : DatePickerRendererBase<MaterialTextField>, IMaterialEntryRenderer
+       {
+               public MaterialDatePickerRenderer()
+               {
+                       VisualElement.VerifyVisualFlagEnabled();
+               }
+
+               protected override MaterialTextField CreateNativeControl()
+               {
+                       var field = new NoCaretMaterialTextField(this, Element);
+                       return field;
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       ApplyTheme();
+               }
+
+               protected internal override void UpdateFont()
+               {
+                       base.UpdateFont();
+                       Control?.ApplyTypographyScheme(Element);
+               }
+               
+
+               protected internal override void UpdateTextColor()
+               {
+                       Control?.UpdateTextColor(this);
+               }
+
+
+               protected virtual void ApplyTheme()
+               {
+                       Control?.ApplyTheme(this);
+               }
+
+               internal void UpdatePlaceholder()
+               {
+                       Control?.UpdatePlaceholder(this);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+               {
+                       base.OnElementChanged(e);
+                       UpdatePlaceholder();
+               }
+
+               string IMaterialEntryRenderer.Placeholder => string.Empty;
+               Color IMaterialEntryRenderer.PlaceholderColor => Color.Default;
+
+               Color IMaterialEntryRenderer.TextColor => Element?.TextColor ?? Color.Default;
+               Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
+       }
+}
\ No newline at end of file
index 024d261..1e9d9d9 100644 (file)
-using System;
-using System.ComponentModel;
-
-using System.Drawing;
-using CoreGraphics;
-using Foundation;
-using UIKit;
-using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
-using Specifics = Xamarin.Forms.PlatformConfiguration.iOSSpecific.Entry;
-using MTextField = MaterialComponents.TextField;
-using MTextInputControllerOutlined = MaterialComponents.TextInputControllerOutlined;
-using MTextInputControllerFilled = MaterialComponents.TextInputControllerFilled;
-using MTextInputControllerBase = MaterialComponents.TextInputControllerBase;
-using MTextInputControllerUnderline = MaterialComponents.TextInputControllerUnderline;
-
-using Xamarin.Forms;
-using MaterialComponents;
+using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS.Material
 {
-       public class MaterialEntryRenderer : EntryRenderer
+       public class MaterialEntryRenderer : EntryRendererBase<MaterialTextField>, IMaterialEntryRenderer
        {
-               SemanticColorScheme _colorScheme;
-               TypographyScheme _typographyScheme;
-
 
                public MaterialEntryRenderer()
                {
                        VisualElement.VerifyVisualFlagEnabled();
-                       _colorScheme = (SemanticColorScheme)CreateColorScheme();
-                       _typographyScheme = CreateTypographyScheme();
                }
 
-               public override CGSize SizeThatFits(CGSize size)
-               {
-                       var result =  base.SizeThatFits(size);
-                       if (nfloat.IsInfinity(result.Width))
-                               result = Control.SystemLayoutSizeFittingSize(result, (float)UILayoutPriority.FittingSizeLevel, (float)UILayoutPriority.DefaultHigh);
-
-                       return result;
-               }
-
-               IElementController ElementController => Element as IElementController;
-               MTextInputControllerBase _activeTextinputController;
-
-               public new MTextField Control { get; private set; }
-
-               protected override UITextField CreateNativeControl()
+               protected override MaterialTextField CreateNativeControl()
                {
-                       var field = new MTextField();
-                       Control = field;
-                       field.ClearButtonMode = UITextFieldViewMode.Never;
-                       _activeTextinputController = new MTextInputControllerFilled(field);
-                       field.TextInsetsMode = TextInputTextInsetsMode.IfContent;
-                       ApplyTypographyScheme();
-                       ApplyTheme();
-
+                       var field = new MaterialTextField(this, Element);
                        return field;
                }
 
-               protected virtual IColorScheming CreateColorScheme()
-               {
-                       var returnValue = MaterialColors.Light.CreateColorScheme();             
-                       
-                       return returnValue;
-               }
-
-               protected virtual TypographyScheme CreateTypographyScheme()
-               {
-                       return new TypographyScheme();
-               }
-               
                protected override void SetBackgroundColor(Color color)
                {
                        ApplyTheme();
                }
 
-               void ApplyTypographyScheme()
-               {
-                       if (Control == null)
-                               return;
-
-                       _typographyScheme.Subtitle1 = Control.Font;
-                       TextFieldTypographyThemer.ApplyTypographyScheme(_typographyScheme, Control);
-                       TextFieldTypographyThemer.ApplyTypographyScheme(_typographyScheme, _activeTextinputController);
-               }
-
                protected internal override void UpdateFont()
                {
                        base.UpdateFont();
-                       ApplyTypographyScheme();
+                       Control?.ApplyTypographyScheme(Element);
                }
 
 
                protected internal override void UpdateColor()
                {
-                       var uIColor = MaterialColors.GetEntryTextColor(Element.TextColor);
-
-                       _colorScheme.OnSurfaceColor = uIColor;
-                       _colorScheme.PrimaryColor = uIColor;                    
-
-                       ApplyTheme();
+                       Control?.UpdateTextColor(this);
                }
 
 
                protected virtual void ApplyTheme()
                {
-                       if (_activeTextinputController == null)
-                               return;
-
-                       FilledTextFieldColorThemer.ApplySemanticColorScheme(_colorScheme, (MTextInputControllerFilled)_activeTextinputController);
-
-                       OverrideThemeColors();
-               }
-
-               protected virtual void OverrideThemeColors()
-               {
-                       var textColor = MaterialColors.GetEntryTextColor(Element.TextColor);
-                       var placeHolderColors = MaterialColors.GetPlaceHolderColor(Element.PlaceholderColor, Element.TextColor);
-                       var underlineColors = MaterialColors.GetUnderlineColor(Element.TextColor);
-
-                       Control.TextColor = textColor;
-                       _activeTextinputController.InlinePlaceholderColor = placeHolderColors.InlineColor;
-                       _activeTextinputController.FloatingPlaceholderNormalColor = placeHolderColors.InlineColor;
-                       _activeTextinputController.FloatingPlaceholderActiveColor = placeHolderColors.FloatingColor;
-
-                       // BackgroundColor
-                       _activeTextinputController.BorderFillColor = MaterialColors.CreateEntryFilledInputBackgroundColor(Element.BackgroundColor, Element.TextColor);
-
-                       _activeTextinputController.ActiveColor = underlineColors.FocusedColor;
-                       _activeTextinputController.NormalColor = underlineColors.UnFocusedColor;
+                       Control?.ApplyTheme(this);
                }
 
                protected internal override void UpdatePlaceholder()
                {
-                       var placeholderText = Element.Placeholder;
+                       Control?.UpdatePlaceholder(this);
+                       
+               }
 
-                       if (placeholderText == null)
-                               return;
 
-                       _activeTextinputController.PlaceholderText = placeholderText;
-                       ApplyTheme();
-               }
+               Color IMaterialEntryRenderer.TextColor => Element?.TextColor ?? Color.Default;
+               Color IMaterialEntryRenderer.PlaceholderColor => Element?.PlaceholderColor ?? Color.Default;
+               Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
+               string IMaterialEntryRenderer.Placeholder => Element?.Placeholder ?? string.Empty;
        }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Material.iOS/MaterialPickerRenderer.cs b/Xamarin.Forms.Material.iOS/MaterialPickerRenderer.cs
new file mode 100644 (file)
index 0000000..d80db95
--- /dev/null
@@ -0,0 +1,59 @@
+using System.ComponentModel;
+using UIKit;
+
+namespace Xamarin.Forms.Platform.iOS.Material
+{
+       public class MaterialPickerRenderer : PickerRendererBase<MaterialTextField>, IMaterialEntryRenderer
+       {
+               public MaterialPickerRenderer()
+               {
+                       VisualElement.VerifyVisualFlagEnabled();
+               }
+
+               protected override MaterialTextField CreateNativeControl()
+               {
+                       var field = new ReadOnlyMaterialTextField(this, Element);
+                       return field;
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       ApplyTheme();
+               }
+
+               protected internal override void UpdateFont()
+               {
+                       base.UpdateFont();
+                       Control?.ApplyTypographyScheme(Element);
+               }
+               
+
+               protected internal override void UpdateTextColor()
+               {
+                       Control?.UpdateTextColor(this);
+               }
+
+
+               protected virtual void ApplyTheme()
+               {
+                       Control?.ApplyTheme(this);
+               }
+
+               protected internal override void UpdatePlaceholder()
+               {
+                       Control?.UpdatePlaceholder(this);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
+               {
+                       base.OnElementChanged(e);
+                       UpdatePlaceholder();
+               }
+
+               string IMaterialEntryRenderer.Placeholder => string.Empty;
+               Color IMaterialEntryRenderer.PlaceholderColor => Color.Default;
+               Color IMaterialEntryRenderer.TextColor => Element?.TextColor ?? Color.Default;
+               Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
+               
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Material.iOS/MaterialTextField.cs b/Xamarin.Forms.Material.iOS/MaterialTextField.cs
new file mode 100644 (file)
index 0000000..77ac995
--- /dev/null
@@ -0,0 +1,143 @@
+using System;
+using CoreGraphics;
+using MaterialComponents;
+using UIKit;
+using MTextField = MaterialComponents.TextField;
+using MTextInputControllerFilled = MaterialComponents.TextInputControllerFilled;
+using MTextInputControllerBase = MaterialComponents.TextInputControllerBase;
+using System.Collections.Generic;
+using ObjCRuntime;
+using Foundation;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.iOS.Material
+{
+       public class MaterialTextField : MTextField
+       {
+               SemanticColorScheme _colorScheme;
+               TypographyScheme _typographyScheme;
+               MTextInputControllerBase _activeTextinputController;
+
+               public MaterialTextField(IMaterialEntryRenderer element, IFontElement fontElement)
+               {
+                       VisualElement.VerifyVisualFlagEnabled();
+                       ClearButtonMode = UITextFieldViewMode.Never;
+                       _activeTextinputController = new MTextInputControllerFilled(this);
+                       TextInsetsMode = TextInputTextInsetsMode.IfContent;
+                       _typographyScheme = CreateTypographyScheme();
+                       _colorScheme = (SemanticColorScheme)CreateColorScheme();
+                       ApplyTypographyScheme(fontElement);
+                       ApplyTheme(element);
+
+               }
+
+               public override CGSize SizeThatFits(CGSize size)
+               {
+                       var result = base.SizeThatFits(size);
+
+                       if (nfloat.IsInfinity(result.Width))
+                               result = SystemLayoutSizeFittingSize(result, (float)UILayoutPriority.FittingSizeLevel, (float)UILayoutPriority.DefaultHigh);
+
+                       return result;
+               }
+
+               internal void ApplyTypographyScheme(IFontElement fontElement)
+               {
+                       Font = fontElement?.ToUIFont();
+                       _typographyScheme.Subtitle1 = Font;
+                       TextFieldTypographyThemer.ApplyTypographyScheme(_typographyScheme, this);
+                       TextFieldTypographyThemer.ApplyTypographyScheme(_typographyScheme, _activeTextinputController);
+               }
+
+               internal void ApplyTheme(IMaterialEntryRenderer element)
+               {
+                       if (element == null)
+                               return;
+
+                       if (_activeTextinputController == null)
+                               return;
+
+                       FilledTextFieldColorThemer.ApplySemanticColorScheme(_colorScheme, (MTextInputControllerFilled)_activeTextinputController);
+
+                       var textColor = MaterialColors.GetEntryTextColor(element.TextColor);
+                       var placeHolderColors = MaterialColors.GetPlaceHolderColor(element.PlaceholderColor, element.TextColor);
+                       var underlineColors = MaterialColors.GetUnderlineColor(element.TextColor);
+
+                       TextColor = textColor;
+                       _activeTextinputController.InlinePlaceholderColor = placeHolderColors.InlineColor;
+                       _activeTextinputController.FloatingPlaceholderNormalColor = placeHolderColors.InlineColor;
+                       _activeTextinputController.FloatingPlaceholderActiveColor = placeHolderColors.FloatingColor;
+
+                       // BackgroundColor
+                       _activeTextinputController.BorderFillColor = MaterialColors.CreateEntryFilledInputBackgroundColor(element.BackgroundColor, element.TextColor);
+
+                       _activeTextinputController.ActiveColor = underlineColors.FocusedColor;
+                       _activeTextinputController.NormalColor = underlineColors.UnFocusedColor;
+               }
+
+               internal void UpdatePlaceholder(IMaterialEntryRenderer element)
+               {
+                       var placeholderText = element.Placeholder ?? String.Empty;
+                       _activeTextinputController.PlaceholderText = placeholderText;
+                       ApplyTheme(element);
+
+                       var previous = _activeTextinputController.FloatingPlaceholderScale;
+                       if (String.IsNullOrWhiteSpace(placeholderText))
+                               _activeTextinputController.FloatingPlaceholderScale = 0;
+                       else
+                               _activeTextinputController.FloatingPlaceholderScale = (float)TextInputControllerBase.FloatingPlaceholderScaleDefault;
+
+                       if (previous != _activeTextinputController.FloatingPlaceholderScale && element is IVisualElementRenderer controller)
+                               controller.Element?.InvalidateMeasureInternal(InvalidationTrigger.VerticalOptionsChanged);
+               }
+
+
+               internal void UpdateTextColor(IMaterialEntryRenderer element)
+               {
+                       var uIColor = MaterialColors.GetEntryTextColor(element.TextColor);
+                       _colorScheme.OnSurfaceColor = uIColor;
+                       _colorScheme.PrimaryColor = uIColor;
+               }
+
+               protected virtual IColorScheming CreateColorScheme()
+               {
+                       var returnValue = MaterialColors.Light.CreateColorScheme();
+                       return returnValue;
+               }
+
+               protected virtual TypographyScheme CreateTypographyScheme()
+               {
+                       return new TypographyScheme();
+               }
+       }
+
+
+       internal class NoCaretMaterialTextField : MaterialTextField
+       {
+               public NoCaretMaterialTextField(IMaterialEntryRenderer element, IFontElement fontElement) : base(element, fontElement)
+               {
+                       SpellCheckingType = UITextSpellCheckingType.No;
+                       AutocorrectionType = UITextAutocorrectionType.No;
+                       AutocapitalizationType = UITextAutocapitalizationType.None;
+               }
+
+               public override CGRect GetCaretRectForPosition(UITextPosition position)
+               {
+                       return new CGRect();
+               }
+       }
+
+       internal class ReadOnlyMaterialTextField : NoCaretMaterialTextField
+       {
+               readonly HashSet<string> enableActions;
+
+               public ReadOnlyMaterialTextField(IMaterialEntryRenderer element, IFontElement fontElement) : base(element, fontElement)
+               {
+                       string[] actions = { "copy:", "select:", "selectAll:" };
+                       enableActions = new HashSet<string>(actions);
+               }
+
+               public override bool CanPerform(Selector action, NSObject withSender)
+                       => enableActions.Contains(action.Name);
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Material.iOS/MaterialTimePickerRenderer.cs b/Xamarin.Forms.Material.iOS/MaterialTimePickerRenderer.cs
new file mode 100644 (file)
index 0000000..5487d14
--- /dev/null
@@ -0,0 +1,58 @@
+using System.ComponentModel;
+using UIKit;
+
+namespace Xamarin.Forms.Platform.iOS.Material
+{
+       public class MaterialTimePickerRenderer : TimePickerRendererBase<MaterialTextField>, IMaterialEntryRenderer
+       {
+               public MaterialTimePickerRenderer()
+               {
+                       VisualElement.VerifyVisualFlagEnabled();
+               }
+
+               protected override MaterialTextField CreateNativeControl()
+               {
+                       var field = new NoCaretMaterialTextField(this, Element);
+                       return field;
+               }
+
+               protected override void SetBackgroundColor(Color color)
+               {
+                       ApplyTheme();
+               }
+
+               protected internal override void UpdateFont()
+               {
+                       base.UpdateFont();
+                       Control?.ApplyTypographyScheme(Element);
+               }
+
+               protected internal override void UpdateTextColor()
+               {
+                       Control?.UpdateTextColor(this);
+               }
+
+
+               protected virtual void ApplyTheme()
+               {
+                       Control?.ApplyTheme(this);
+               }
+
+               internal void UpdatePlaceholder()
+               {
+                       Control?.UpdatePlaceholder(this);
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+               {
+                       base.OnElementChanged(e);
+                       UpdatePlaceholder();
+               }
+
+               string IMaterialEntryRenderer.Placeholder => string.Empty;
+               Color IMaterialEntryRenderer.PlaceholderColor => Color.Default;
+
+               Color IMaterialEntryRenderer.TextColor => Element?.TextColor ?? Color.Default;
+               Color IMaterialEntryRenderer.BackgroundColor => Element?.BackgroundColor ?? Color.Default;
+       }
+}
\ No newline at end of file
index fad5b39..ec47abd 100644 (file)
@@ -20,4 +20,7 @@ using Xamarin.Forms;
 [assembly: ExportRenderer(typeof(Xamarin.Forms.Entry), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialEntryRenderer), new[] { typeof(VisualRendererMarker.Material) })]
 [assembly: ExportRenderer(typeof(Xamarin.Forms.Frame), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialFrameRenderer), new[] { typeof(VisualRendererMarker.Material) })]
 [assembly: ExportRenderer(typeof(Xamarin.Forms.ProgressBar), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialProgressBarRenderer), new[] { typeof(VisualRendererMarker.Material) })]
-[assembly: ExportRenderer(typeof(Xamarin.Forms.Slider), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialSliderRenderer), new[] { typeof(VisualRendererMarker.Material) })]
\ No newline at end of file
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Slider), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialSliderRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+[assembly: ExportRenderer(typeof(Xamarin.Forms.TimePicker), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialTimePickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Picker), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialPickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(Xamarin.Forms.Platform.iOS.Material.MaterialDatePickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
\ No newline at end of file
index 6160b9e..7d0d51c 100644 (file)
     <Compile Include="..\Xamarin.Forms.Platform.Android\Material\MaterialColors.cs">
       <Link>MaterialColors.cs</Link>
     </Compile>
+    <Compile Include="IMaterialEntryRenderer.cs" />
+    <Compile Include="MaterialPickerRenderer.cs" />
+    <Compile Include="MaterialDatePickerRenderer.cs" />
+    <Compile Include="MaterialTimePickerRenderer.cs" />
+    <Compile Include="MaterialTextField.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="MaterialActivityIndicatorRenderer.cs" />
     <Compile Include="MaterialButtonRenderer.cs" />
index 6865e89..7814fe1 100644 (file)
@@ -6,35 +6,30 @@ using System.ComponentModel;
 using System.Linq;
 using Android.Content;
 using Android.Widget;
-using AColor = Android.Graphics.Color;
 using Android.Text;
 using Android.Text.Style;
 
 namespace Xamarin.Forms.Platform.Android.AppCompat
 {
-       public class PickerRenderer : ViewRenderer<Picker, EditText>, IPickerRenderer
+       public abstract class PickerRendererBase<TControl> : ViewRenderer<Picker, TControl>, IPickerRenderer
+               where TControl : global::Android.Views.View
        {
                AlertDialog _dialog;
                bool _disposed;
-               TextColorSwitcher _textColorSwitcher;
-               int _originalHintTextColor;
 
-               public PickerRenderer(Context context) : base(context)
+               public PickerRendererBase(Context context) : base(context)
                {
                        AutoPackage = false;
                }
 
                [Obsolete("This constructor is obsolete as of version 2.5. Please use PickerRenderer(Context) instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
-               public PickerRenderer()
+               public PickerRendererBase()
                {
                        AutoPackage = false;
                }
 
-               protected override EditText CreateNativeControl()
-               {
-                       return new PickerEditText(Context, this);
-               }
+               protected abstract EditText EditText { get; }
 
                protected override void Dispose(bool disposing)
                {
@@ -59,13 +54,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
                                if (Control == null)
                                {
                                        var textField = CreateNativeControl();
-
-                                       var useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
-                                       _textColorSwitcher = new TextColorSwitcher(textField.TextColors, useLegacyColorManagement);
-
                                        SetNativeControl(textField);
-
-                                       _originalHintTextColor = Control.CurrentHintTextColor;
                                }
                                UpdateFont();
                                UpdatePicker();
@@ -151,28 +140,58 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
 
                void UpdateFont()
                {
-                       Control.Typeface = Element.ToTypeface();
-                       Control.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
+                       EditText.Typeface = Element.ToTypeface();
+                       EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
                }
 
                void UpdatePicker()
                {
-                       Control.Hint = Element.Title;
-
-                       if (Element.IsSet(Picker.TitleColorProperty))
-                               Control.SetHintTextColor(Element.TitleColor.ToAndroid());
-                       else
-                               Control.SetHintTextColor(new AColor(_originalHintTextColor));
+                       UpdatePlaceHolderText();
+                       UpdateTitleColor();
 
                        if (Element.SelectedIndex == -1 || Element.Items == null || Element.SelectedIndex >= Element.Items.Count)
-                               Control.Text = null;
+                               EditText.Text = null;
                        else
-                               Control.Text = Element.Items[Element.SelectedIndex];
+                               EditText.Text = Element.Items[Element.SelectedIndex];
+               }
+
+               abstract protected void UpdateTextColor();
+               abstract protected internal void UpdateTitleColor();
+               abstract protected internal void UpdatePlaceHolderText();
+       }
+
+       public class PickerRenderer : PickerRendererBase<EditText>
+       {
+               TextColorSwitcher _textColorSwitcher;
+               TextColorSwitcher _hintColorSwitcher;
+
+               [Obsolete("This constructor is obsolete as of version 2.5. Please use PickerRenderer(Context) instead.")]
+               public PickerRenderer()
+               {
+               }
+
+               public PickerRenderer(Context context) : base(context)
+               {
+               }
+
+               protected override EditText CreateNativeControl()
+               {
+                       return new PickerEditText(Context);
+               }
+
+               protected override EditText EditText => Control;
+
+               protected internal override void UpdateTitleColor()
+               {
+                       _hintColorSwitcher = _hintColorSwitcher ?? new TextColorSwitcher(EditText.HintTextColors, Element.UseLegacyColorManagement());
+                       _hintColorSwitcher.UpdateTextColor(EditText, Element.TitleColor, EditText.SetHintTextColor);
                }
 
-               void UpdateTextColor()
+               protected override void UpdateTextColor()
                {
-                       _textColorSwitcher?.UpdateTextColor(Control, Element.TextColor);
+                       _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
+                       _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
                }
+               protected internal override void UpdatePlaceHolderText() => EditText.Hint = Element.Title;
        }
 }
\ No newline at end of file
index dacae6b..b54b094 100644 (file)
@@ -4,12 +4,12 @@
 #if __ANDROID__
 using Android.Content.Res;
 using Android.Graphics;
-using AColor = Android.Graphics.Color;
 using AProgressBar = Android.Widget.ProgressBar;
 using ASeekBar = Android.Widget.AbsSeekBar;
+using PlatformColor = Android.Graphics.Color;
 #else
 using MaterialComponents;
-using AColor = UIKit.UIColor;
+using PlatformColor = UIKit.UIColor;
 #endif
 
 #if __ANDROID__
@@ -48,7 +48,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
 
                // State list from material-components-android
                // https://github.com/material-components/material-components-android/blob/71694616056012fe1162adb9144be903d1e510d5/lib/java/com/google/android/material/textfield/res/values/colors.xml#L28
-               public static AColor CreateEntryFilledInputBackgroundColor(Color backgroundColor, Color textColor)
+               public static PlatformColor CreateEntryFilledInputBackgroundColor(Color backgroundColor, Color textColor)
                {
                        var platformTextColor = GetEntryTextColor(textColor);
 
@@ -63,9 +63,9 @@ namespace Xamarin.Forms.Platform.iOS.Material
                        return ToPlatformColor(backgroundColor);
                }
 
-               public static (AColor InlineColor, AColor FloatingColor) GetPlaceHolderColor(Color placeholderColor, Color textColor)
+               public static (PlatformColor InlineColor, PlatformColor FloatingColor) GetPlaceHolderColor(Color placeholderColor, Color textColor)
                {
-                       AColor color;
+                       PlatformColor color;
 
                        if (placeholderColor == Color.Default)
                        {
@@ -83,13 +83,13 @@ namespace Xamarin.Forms.Platform.iOS.Material
                        return (inlineColor, floatingColor);
                }
 
-               public static (AColor FocusedColor, AColor UnFocusedColor) GetUnderlineColor(Color textColor)
+               public static (PlatformColor FocusedColor, PlatformColor UnFocusedColor) GetUnderlineColor(Color textColor)
                {
-                       AColor color = GetEntryTextColor(textColor);
+                       PlatformColor color = GetEntryTextColor(textColor);
                        return (color, WithAlpha(color, kFilledTextFieldIndicatorLineAlpha));
                }
 
-               public static AColor GetEntryTextColor(Color textColor)
+               public static PlatformColor GetEntryTextColor(Color textColor)
                {
                        return textColor != Color.Default ? ToPlatformColor(textColor) : MaterialColors.Light.PrimaryColor;
                }
@@ -115,7 +115,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
 
                // State list from material-components-android
                // https://github.com/material-components/material-components-android/blob/3637c23078afc909e42833fd1c5fd47bb3271b5f/lib/java/com/google/android/material/button/res/color/mtrl_btn_bg_color_selector.xml
-               public static ColorStateList CreateButtonBackgroundColors(AColor primary)
+               public static ColorStateList CreateButtonBackgroundColors(PlatformColor primary)
                {
                        var colors = new int[] { primary, primary.WithAlpha(0.12) };
                        return new ColorStateList(ButtonStates, colors);
@@ -123,13 +123,13 @@ namespace Xamarin.Forms.Platform.iOS.Material
 
                // State list from material-components-android
                // https://github.com/material-components/material-components-android/blob/3637c23078afc909e42833fd1c5fd47bb3271b5f/lib/java/com/google/android/material/button/res/color/mtrl_btn_text_color_selector.xml
-               public static ColorStateList CreateButtonTextColors(AColor primary, AColor text)
+               public static ColorStateList CreateButtonTextColors(PlatformColor primary, PlatformColor text)
                {
                        var colors = new int[] { text, primary.WithAlpha(0.38) };
                        return new ColorStateList(ButtonStates, colors);
                }
 
-               public static ColorStateList CreateEntryFilledPlaceholderColors(AColor inlineColor, AColor floatingColor)
+               public static ColorStateList CreateEntryFilledPlaceholderColors(PlatformColor inlineColor, PlatformColor floatingColor)
                {
                        int[][] States =
                        {
@@ -141,14 +141,14 @@ namespace Xamarin.Forms.Platform.iOS.Material
                        return new ColorStateList(States, colors);
                }
 
-               public static ColorStateList CreateEntryUnderlineColors(AColor focusedColor, AColor unfocusedColor)
+               public static ColorStateList CreateEntryUnderlineColors(PlatformColor focusedColor, PlatformColor unfocusedColor)
                {
                        var colors = new int[] { focusedColor, unfocusedColor };
                        return new ColorStateList(EntryUnderlineStates, colors);
                }
 
-               internal static AColor WithAlpha(this AColor color, double alpha) =>
-                       new AColor(color.R, color.G, color.B, (byte)(alpha * 255));
+               internal static PlatformColor WithAlpha(this PlatformColor color, double alpha) =>
+                       new PlatformColor(color.R, color.G, color.B, (byte)(alpha * 255));
 
                internal static void ApplySeekBarColors(this ASeekBar seekBar, Color progressColor, Color backgroundColor, Color thumbColor)
                {
@@ -170,7 +170,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
 
                internal static void ApplyProgressBarColors(this AProgressBar progressBar, Color progressColor, Color backgroundColor)
                {
-                       AColor defaultProgress = Dark.PrimaryColor;
+                       PlatformColor defaultProgress = Dark.PrimaryColor;
 
                        if (progressColor.IsDefault)
                        {
@@ -221,19 +221,19 @@ namespace Xamarin.Forms.Platform.iOS.Material
                {
                        // the Colors for "branding"
                        //  - we selected the "black" theme from the default DarkActionBar theme
-                       public static readonly AColor PrimaryColor = FromRgb(33, 33, 33);
-                       public static readonly AColor PrimaryColorVariant = AColor.Black;
-                       public static readonly AColor OnPrimaryColor = AColor.White;
-                       public static readonly AColor SecondaryColor = FromRgb(33, 33, 33);
-                       public static readonly AColor OnSecondaryColor = AColor.White;
+                       public static readonly PlatformColor PrimaryColor = FromRgb(33, 33, 33);
+                       public static readonly PlatformColor PrimaryColorVariant = PlatformColor.Black;
+                       public static readonly PlatformColor OnPrimaryColor = PlatformColor.White;
+                       public static readonly PlatformColor SecondaryColor = FromRgb(33, 33, 33);
+                       public static readonly PlatformColor OnSecondaryColor = PlatformColor.White;
 
                        // the Colors for "UI"
-                       public static readonly AColor BackgroundColor = AColor.White;
-                       public static readonly AColor OnBackgroundColor = AColor.Black;
-                       public static readonly AColor SurfaceColor = AColor.White;
-                       public static readonly AColor OnSurfaceColor = AColor.Black;
-                       public static readonly AColor ErrorColor = FromRgb(176, 0, 32);
-                       public static readonly AColor OnErrorColor = AColor.White;
+                       public static readonly PlatformColor BackgroundColor = PlatformColor.White;
+                       public static readonly PlatformColor OnBackgroundColor = PlatformColor.Black;
+                       public static readonly PlatformColor SurfaceColor = PlatformColor.White;
+                       public static readonly PlatformColor OnSurfaceColor = PlatformColor.Black;
+                       public static readonly PlatformColor ErrorColor = FromRgb(176, 0, 32);
+                       public static readonly PlatformColor OnErrorColor = PlatformColor.White;
 
 #if __IOS__
                        public static SemanticColorScheme CreateColorScheme()
@@ -261,19 +261,19 @@ namespace Xamarin.Forms.Platform.iOS.Material
                {
                        // the Colors for "branding"
                        //  - we selected the "black" theme from the default DarkActionBar theme
-                       public static readonly AColor PrimaryColor = FromRgb(33, 33, 33);
-                       public static readonly AColor PrimaryColorVariant = AColor.Black;
-                       public static readonly AColor OnPrimaryColor = AColor.White;
-                       public static readonly AColor SecondaryColor = FromRgb(33, 33, 33);
-                       public static readonly AColor OnSecondaryColor = AColor.White;
+                       public static readonly PlatformColor PrimaryColor = FromRgb(33, 33, 33);
+                       public static readonly PlatformColor PrimaryColorVariant = PlatformColor.Black;
+                       public static readonly PlatformColor OnPrimaryColor = PlatformColor.White;
+                       public static readonly PlatformColor SecondaryColor = FromRgb(33, 33, 33);
+                       public static readonly PlatformColor OnSecondaryColor = PlatformColor.White;
 
                        // the Colors for "UI"
-                       public static readonly AColor BackgroundColor = FromRgb(20, 20, 20);
-                       public static readonly AColor OnBackgroundColor = AColor.White;
-                       public static readonly AColor SurfaceColor = FromRgb(40, 40, 40);
-                       public static readonly AColor OnSurfaceColor = AColor.White;
-                       public static readonly AColor ErrorColor = FromRgb(194, 108, 122);
-                       public static readonly AColor OnErrorColor = AColor.White;
+                       public static readonly PlatformColor BackgroundColor = FromRgb(20, 20, 20);
+                       public static readonly PlatformColor OnBackgroundColor = PlatformColor.White;
+                       public static readonly PlatformColor SurfaceColor = FromRgb(40, 40, 40);
+                       public static readonly PlatformColor OnSurfaceColor = PlatformColor.White;
+                       public static readonly PlatformColor ErrorColor = FromRgb(194, 108, 122);
+                       public static readonly PlatformColor OnErrorColor = PlatformColor.White;
 
 #if __IOS__
                        public static SemanticColorScheme CreateColorScheme()
@@ -298,7 +298,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
                }
 
 
-               static AColor ToPlatformColor(Color color)
+               static PlatformColor ToPlatformColor(Color color)
                {
 #if __ANDROID__
                        return color.ToAndroid();
@@ -309,7 +309,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
 
 
 
-               static AColor WithMultipliedAlpha(AColor color, float alpha)
+               static PlatformColor WithMultipliedAlpha(PlatformColor color, float alpha)
                {
 #if __ANDROID__
                        return color.WithAlpha(color.A / 255f * alpha);
@@ -318,7 +318,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
 #endif
                }
 
-               static AColor WithAlpha(AColor color, float alpha)
+               static PlatformColor WithAlpha(PlatformColor color, float alpha)
                {
 #if __ANDROID__
                        return color.WithAlpha(alpha);
@@ -328,12 +328,12 @@ namespace Xamarin.Forms.Platform.iOS.Material
                }
 
 
-               static AColor FromRgb(int red, int green, int blue)
+               static PlatformColor FromRgb(int red, int green, int blue)
                {
 #if __ANDROID__
-                       return AColor.Rgb(red, green, blue);
+                       return PlatformColor.Rgb(red, green, blue);
 #else
-                       return AColor.FromRGB(red, green, blue);
+                       return PlatformColor.FromRGB(red, green, blue);
 
 #endif
                }
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialDatePickerRenderer.cs b/Xamarin.Forms.Platform.Android/Material/MaterialDatePickerRenderer.cs
new file mode 100644 (file)
index 0000000..e6ac79e
--- /dev/null
@@ -0,0 +1,54 @@
+#if __ANDROID_28__
+using System.ComponentModel;
+using Android.Content;
+using Android.Support.V4.View;
+using Android.Views;
+using Android.Widget;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Android.Material;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(MaterialDatePickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialDatePickerRenderer : DatePickerRendererBase<MaterialPickerTextInputLayout>
+       {
+               MaterialPickerTextInputLayout _textInputLayout;
+               MaterialPickerEditText _textInputEditText;
+
+               public MaterialDatePickerRenderer(Context context) : base(MaterialContextThemeWrapper.Create(context))
+               {
+               }
+
+               protected override EditText EditText => _textInputEditText;
+
+               protected override MaterialPickerTextInputLayout CreateNativeControl()
+               {
+                       LayoutInflater inflater = LayoutInflater.FromContext(Context);
+                       var view = inflater.Inflate(Resource.Layout.MaterialPickerTextInput, null);
+                       _textInputLayout = (MaterialPickerTextInputLayout)view;
+                       _textInputEditText = _textInputLayout.FindViewById<MaterialPickerEditText>(Resource.Id.materialformsedittext);
+
+                       return _textInputLayout;
+               }
+
+               protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+               {
+                       base.OnElementChanged(e);
+                       _textInputLayout.SetHint(string.Empty, Element);
+               }
+
+               protected override void UpdateBackgroundColor()
+               {
+                       if (_textInputLayout == null)
+                               return;
+
+                       _textInputLayout.BoxBackgroundColor = MaterialColors.CreateEntryFilledInputBackgroundColor(Element.BackgroundColor, Element.TextColor);
+               }
+
+               protected override void UpdateTextColor() => ApplyTheme();
+               void ApplyTheme() => _textInputLayout?.ApplyTheme(Element.TextColor, Color.Default);
+
+       }
+}
+#endif
\ No newline at end of file
index 98153e4..ff71648 100644 (file)
@@ -1,7 +1,5 @@
 #if __ANDROID_28__
-using System.Threading.Tasks;
 using Android.Content;
-using Android.Content.Res;
 using Android.OS;
 using Android.Support.V4.View;
 using Android.Util;
@@ -9,7 +7,6 @@ using Android.Views;
 using Android.Widget;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.Android.Material;
-using AColor = Android.Graphics.Color;
 
 [assembly: ExportRenderer(typeof(Xamarin.Forms.Entry), typeof(MaterialEntryRenderer), new[] { typeof(VisualRendererMarker.Material) })]
 namespace Xamarin.Forms.Platform.Android.Material
@@ -17,8 +14,8 @@ namespace Xamarin.Forms.Platform.Android.Material
        public sealed class MaterialEntryRenderer : EntryRendererBase<MaterialFormsTextInputLayout>
        {
                bool _disposed;
-               private MaterialFormsEditText _textInputEditText;
-               private MaterialFormsTextInputLayout _textInputLayout;
+               MaterialFormsEditText _textInputEditText;
+               MaterialFormsTextInputLayout _textInputLayout;
 
                public MaterialEntryRenderer(Context context) :
                        base(MaterialContextThemeWrapper.Create(context))
@@ -36,8 +33,6 @@ namespace Xamarin.Forms.Platform.Android.Material
                        var view = inflater.Inflate(Resource.Layout.TextInputLayoutFilledBox, null);
                        _textInputLayout = (MaterialFormsTextInputLayout)view;
                        _textInputEditText = _textInputLayout.FindViewById<MaterialFormsEditText>(Resource.Id.materialformsedittext);
-                       _textInputEditText.FocusChange += TextInputEditTextFocusChange;
-                       _textInputLayout.Hint = Element.Placeholder;
 
                        return _textInputLayout;
                }
@@ -99,16 +94,7 @@ namespace Xamarin.Forms.Platform.Android.Material
                        base.Dispose(disposing);
                }
 
-
-               void TextInputEditTextFocusChange(object sender, FocusChangeEventArgs e)
-               {
-                       // TODO figure out better way to do this
-                       // this is a hack that changes the active underline color from the accent color to whatever the user 
-                       // specified
-                       Device.BeginInvokeOnMainThread(() => UpdatePlaceholderColor());
-               }
-
-               protected internal override void UpdateColor() => ApplyTheme();
+               protected override void UpdateTextColor() => ApplyTheme();
 
                protected override void UpdateBackgroundColor()
                {
@@ -120,33 +106,13 @@ namespace Xamarin.Forms.Platform.Android.Material
 
                protected internal override void UpdatePlaceHolderText()
                {
-                       _textInputLayout.Hint = Element.Placeholder;
+                       _textInputLayout.SetHint(Element.Placeholder, Element);
+                       Element.InvalidateMeasureNonVirtual(Internals.InvalidationTrigger.VerticalOptionsChanged);
                }
 
-               protected internal override void UpdatePlaceholderColor() => ApplyTheme();
-
-               void ApplyTheme()
-               {
-                       if (_textInputLayout == null)
-                               return;
-
-                       // set text color
-                       var textColor = MaterialColors.GetEntryTextColor(Element.TextColor);
-                       UpdateTextColor(Color.FromUint((uint)textColor.ToArgb()));
-
-                       var placeHolderColors = MaterialColors.GetPlaceHolderColor(Element.PlaceholderColor, Element.TextColor);
-                       var underlineColors = MaterialColors.GetUnderlineColor(Element.TextColor);
-
-                       var colors = MaterialColors.CreateEntryUnderlineColors(underlineColors.FocusedColor, underlineColors.UnFocusedColor);
-
-                       ViewCompat.SetBackgroundTintList(_textInputEditText, colors);
-
-                                               
-                       if (HasFocus || !string.IsNullOrWhiteSpace(_textInputEditText.Text))
-                               _textInputLayout.DefaultHintTextColor = MaterialColors.CreateEntryFilledPlaceholderColors(placeHolderColors.FloatingColor, placeHolderColors.FloatingColor);
-                       else
-                               _textInputLayout.DefaultHintTextColor = MaterialColors.CreateEntryFilledPlaceholderColors(placeHolderColors.InlineColor, placeHolderColors.FloatingColor);
-               }
+               
+               protected override void UpdatePlaceholderColor() => ApplyTheme();
+               void ApplyTheme() => _textInputLayout?.ApplyTheme(Element.TextColor, Element.PlaceholderColor);
 
                protected internal override void UpdateFont()
                {
index 3320060..f3a4f42 100644 (file)
 #if __ANDROID_28__
 using System;
 using Android.Content;
-using Android.Graphics;
 using Android.Views;
-using Android.Widget;
-using Android.Support.V4.Graphics.Drawable;
-using Android.Support.Design.Widget;
 using Android.Runtime;
 using Android.Util;
 
-
 namespace Xamarin.Forms.Platform.Android.Material
 {
-
-       public class MaterialFormsTextInputLayout : TextInputLayout
+       public class MaterialFormsEditText : MaterialFormsEditTextBase, IFormsEditText
        {
-               public MaterialFormsTextInputLayout(Context context) : base(context)
-               {
-                       Init();
-               }
-
-               public MaterialFormsTextInputLayout(Context context, IAttributeSet attrs) : base(context, attrs)
-               {
-                       Init();
-               }
-
-               public MaterialFormsTextInputLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
-               {
-                       Init();
-               }
-
-               protected MaterialFormsTextInputLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
-               {
-                       Init();
-               }
-
-               void Init()
-               {
-                       VisualElement.VerifyVisualFlagEnabled();
-               }
-
-       }
-
-       public class MaterialFormsEditText : TextInputEditText, IDescendantFocusToggler, IFormsEditText
-       {
-               DescendantFocusToggler _descendantFocusToggler;
-
-               // These paddings are a hack to center the hint
-               // once this issue is resolved we can get rid of these paddings
-               // https://github.com/material-components/material-components-android/issues/120
-               // https://stackoverflow.com/questions/50487871/how-to-make-the-hint-text-of-textinputlayout-vertically-center
-
-               static Thickness _centeredText = new Thickness(16, 8, 12, 27);
-               static Thickness _alignedWithUnderlineText = new Thickness(16, 20, 12, 16);
 
                public MaterialFormsEditText(Context context) : base(context)
                {
-                       Init();
                }
 
                protected MaterialFormsEditText(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
                {
-                       Init();
                }
 
                public MaterialFormsEditText(Context context, IAttributeSet attrs) : base(context, attrs)
                {
-                       Init();
                }
 
                public MaterialFormsEditText(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
                {
-                       Init();
                }
 
-               void Init()
-               {
-                       VisualElement.VerifyVisualFlagEnabled();
-                       UpdatePadding();
-               }
-
-               void UpdatePadding()
-               {
-                       Thickness rect = _centeredText;
-
-                       if (!String.IsNullOrWhiteSpace(Text) || HasFocus)
-                       {
-                               rect = _alignedWithUnderlineText;
-                       }
-
-                       SetPadding((int)Context.ToPixels(rect.Left), (int)Context.ToPixels(rect.Top), (int)Context.ToPixels(rect.Right), (int)Context.ToPixels(rect.Bottom));
-               }
-
-               protected override void OnTextChanged(Java.Lang.ICharSequence text, int start, int lengthBefore, int lengthAfter)
-               {
-                       base.OnTextChanged(text, start, lengthBefore, lengthAfter);
-                       if (lengthBefore == 0 || lengthAfter == 0)
-                               UpdatePadding();
-               }
-
-               protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Rect previouslyFocusedRect)
-               {
-                       base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
-
-                       // Delay padding update until after the keyboard has showed up otherwise updating the padding
-                       // stops the keyboard from showing up
-                       if (gainFocus)
-                               Device.BeginInvokeOnMainThread(() => UpdatePadding());
-                       else
-                               UpdatePadding();
-               }
-
-               bool IDescendantFocusToggler.RequestFocus(global::Android.Views.View control, Func<bool> baseRequestFocus)
-               {
-                       _descendantFocusToggler = _descendantFocusToggler ?? new DescendantFocusToggler();
-
-                       return _descendantFocusToggler.RequestFocus(control, baseRequestFocus);
-               }
 
                public override bool OnKeyPreIme(Keycode keyCode, KeyEvent e)
                {
@@ -131,17 +40,6 @@ namespace Xamarin.Forms.Platform.Android.Material
                        return true;
                }
 
-               public override bool RequestFocus(FocusSearchDirection direction, Rect previouslyFocusedRect)
-               {
-                       return (this as IDescendantFocusToggler).RequestFocus(this, () => base.RequestFocus(direction, previouslyFocusedRect));
-               }
-
-               protected override void OnSelectionChanged(int selStart, int selEnd)
-               {
-                       base.OnSelectionChanged(selStart, selEnd);
-                       _selectionChanged?.Invoke(this, new SelectionChangedEventArgs(selStart, selEnd));
-               }
-
                event EventHandler _onKeyboardBackPressed;
                event EventHandler IFormsEditText.OnKeyboardBackPressed
                {
@@ -155,6 +53,12 @@ namespace Xamarin.Forms.Platform.Android.Material
                        add => _selectionChanged += value;
                        remove => _selectionChanged -= value;
                }
+
+               protected override void OnSelectionChanged(int selStart, int selEnd)
+               {
+                       base.OnSelectionChanged(selStart, selEnd);
+                       _selectionChanged?.Invoke(this, new SelectionChangedEventArgs(selStart, selEnd));
+               }
        }
 }
 #endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextBase.cs b/Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextBase.cs
new file mode 100644 (file)
index 0000000..422cb90
--- /dev/null
@@ -0,0 +1,55 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Graphics;
+using Android.Views;
+using Android.Support.Design.Widget;
+using Android.Runtime;
+using Android.Util;
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialFormsEditTextBase : TextInputEditText, IDescendantFocusToggler
+       {
+               DescendantFocusToggler _descendantFocusToggler;
+               public MaterialFormsEditTextBase(Context context) : base(context)
+               {
+                       MaterialFormsEditTextManager.Init(this);
+               }
+
+               protected MaterialFormsEditTextBase(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+               {
+                       MaterialFormsEditTextManager.Init(this);
+               }
+
+               public MaterialFormsEditTextBase(Context context, IAttributeSet attrs) : base(context, attrs)
+               {
+                       MaterialFormsEditTextManager.Init(this);
+               }
+
+               public MaterialFormsEditTextBase(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+               {
+                       MaterialFormsEditTextManager.Init(this);
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing)
+                               MaterialFormsEditTextManager.Dispose(this);
+
+                       base.Dispose(disposing);
+               }
+
+               bool IDescendantFocusToggler.RequestFocus(global::Android.Views.View control, Func<bool> baseRequestFocus)
+               {
+                       _descendantFocusToggler = _descendantFocusToggler ?? new DescendantFocusToggler();
+                       return _descendantFocusToggler.RequestFocus(this, baseRequestFocus);
+               }
+
+               public override bool RequestFocus(FocusSearchDirection direction, Rect previouslyFocusedRect)
+               {
+                       return (this as IDescendantFocusToggler).RequestFocus(this, () => base.RequestFocus(direction, previouslyFocusedRect));
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextManager.cs b/Xamarin.Forms.Platform.Android/Material/MaterialFormsEditTextManager.cs
new file mode 100644 (file)
index 0000000..c019c51
--- /dev/null
@@ -0,0 +1,68 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Support.Design.Widget;
+
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       internal static class MaterialFormsEditTextManager
+       {
+
+               // These paddings are a hack to center the hint
+               // once this issue is resolved we can get rid of these paddings
+               // https://github.com/material-components/material-components-android/issues/120
+               // https://stackoverflow.com/questions/50487871/how-to-make-the-hint-text-of-textinputlayout-vertically-center
+
+               static Thickness _centeredText = new Thickness(16, 8, 12, 27);
+               static Thickness _alignedWithUnderlineText = new Thickness(16, 20, 12, 16);
+
+               public static void Init(TextInputEditText textInputEditText)
+               {
+                       VisualElement.VerifyVisualFlagEnabled();
+
+                       textInputEditText.TextChanged += OnTextChanged;
+                       textInputEditText.FocusChange += OnFocusChanged;
+               }
+
+               public static void Dispose(TextInputEditText textInputEditText)
+               {
+                       textInputEditText.TextChanged -= OnTextChanged;
+                       textInputEditText.FocusChange -= OnFocusChanged;
+               }
+
+               private static void OnFocusChanged(object sender, global::Android.Views.View.FocusChangeEventArgs e)
+               {
+                       if (sender is TextInputEditText textInputEditText)
+                       {
+                               // Delay padding update until after the keyboard has showed up otherwise updating the padding
+                               // stops the keyboard from showing up
+                               // TODO closure
+                               if (e.HasFocus)
+                                       Device.BeginInvokeOnMainThread(() => UpdatePadding(textInputEditText));
+                               else
+                                       UpdatePadding(textInputEditText);
+                       }
+               }
+
+               private static void OnTextChanged(object sender, global::Android.Text.TextChangedEventArgs e)
+               {
+                       if (e.BeforeCount == 0 || e.AfterCount == 0)
+                               UpdatePadding(sender as TextInputEditText);
+               }
+
+               static void UpdatePadding(TextInputEditText textInputEditText)
+               {
+                       Thickness rect = _centeredText;
+
+                       if (!String.IsNullOrWhiteSpace(textInputEditText.Text) || textInputEditText.HasFocus)
+                       {
+                               rect = _alignedWithUnderlineText;
+                       }
+
+                       Context Context = textInputEditText.Context;
+                       textInputEditText.SetPadding((int)Context.ToPixels(rect.Left), (int)Context.ToPixels(rect.Top), (int)Context.ToPixels(rect.Right), (int)Context.ToPixels(rect.Bottom));
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayout.cs b/Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayout.cs
new file mode 100644 (file)
index 0000000..48d3150
--- /dev/null
@@ -0,0 +1,28 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Runtime;
+using Android.Util;
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialFormsTextInputLayout : MaterialFormsTextInputLayoutBase
+       {
+               public MaterialFormsTextInputLayout(Context context) : base(context)
+               {
+               }
+
+               public MaterialFormsTextInputLayout(Context context, IAttributeSet attrs) : base(context, attrs)
+               {
+               }
+
+               public MaterialFormsTextInputLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+               {
+               }
+
+               protected MaterialFormsTextInputLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+               {
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayoutBase.cs b/Xamarin.Forms.Platform.Android/Material/MaterialFormsTextInputLayoutBase.cs
new file mode 100644 (file)
index 0000000..f3256fd
--- /dev/null
@@ -0,0 +1,142 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Support.Design.Widget;
+using Android.Runtime;
+using Android.Util;
+using Android.Views;
+using Android.Graphics;
+using Android.Support.V4.View;
+using Android.Content.Res;
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialFormsTextInputLayoutBase : TextInputLayout
+       {
+               Color _formsTextColor;
+               Color _formsPlaceholderColor;
+               bool _isSetup = false;
+               ColorStateList _focusedFilledColorList;
+               ColorStateList _unfocusedEmptyColorList;
+               private ColorStateList _unfocusedUnderlineColorsList;
+               private ColorStateList _focusedUnderlineColorsList;
+               static readonly int[][] s_colorStates = { new[] { global::Android.Resource.Attribute.StateEnabled }, new[] { -global::Android.Resource.Attribute.StateEnabled } };
+               bool _isDisposed = false;
+
+               public MaterialFormsTextInputLayoutBase(Context context) : base(context)
+               {
+                       Init();
+               }
+
+               public MaterialFormsTextInputLayoutBase(Context context, IAttributeSet attrs) : base(context, attrs)
+               {
+                       Init();
+               }
+
+               public MaterialFormsTextInputLayoutBase(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+               {
+                       Init();
+               }
+
+               protected MaterialFormsTextInputLayoutBase(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+               {
+                       Init();
+               }
+
+               void Init()
+               {
+                       VisualElement.VerifyVisualFlagEnabled();        
+               }
+
+               void ResetTextColors(Color formsTextColor, Color formsPlaceHolderColor)
+               {
+                       _formsPlaceholderColor = formsPlaceHolderColor;
+                       _formsTextColor = formsTextColor;
+
+                       var underlineColors = MaterialColors.GetUnderlineColor(_formsTextColor);
+                       var placeHolderColors = MaterialColors.GetPlaceHolderColor(_formsPlaceholderColor, _formsTextColor);
+
+                       // I realize these are the same but I have to set it to a difference instance
+                       // otherwise when focused it won't change to the color I want it to and it'll just think
+                       // I'm not actually changing anything
+                       _unfocusedUnderlineColorsList = MaterialColors.CreateEntryUnderlineColors(underlineColors.FocusedColor, underlineColors.UnFocusedColor);
+                       _focusedUnderlineColorsList = MaterialColors.CreateEntryUnderlineColors(underlineColors.FocusedColor, underlineColors.UnFocusedColor);
+
+                       _focusedFilledColorList = MaterialColors.CreateEntryFilledPlaceholderColors(placeHolderColors.FloatingColor, placeHolderColors.FloatingColor);
+                       _unfocusedEmptyColorList = MaterialColors.CreateEntryFilledPlaceholderColors(placeHolderColors.InlineColor, placeHolderColors.FloatingColor);
+
+
+                       var textColor = MaterialColors.GetEntryTextColor(formsTextColor).ToArgb();
+                       EditText.SetTextColor(new ColorStateList(s_colorStates, new[] { textColor, textColor }));
+               }
+
+               internal void ApplyTheme(Color formsTextColor, Color formsPlaceHolderColor)
+               {
+                       if (_isDisposed)
+                               return;
+
+                       if(!_isSetup)
+                       {
+                               _isSetup = true;
+                               EditText.FocusChange += OnFocusChange;
+                               ResetTextColors(formsTextColor, formsPlaceHolderColor);
+                       }
+                       else if(formsTextColor != _formsTextColor || _formsPlaceholderColor != formsPlaceHolderColor)
+                       {
+                               ResetTextColors(formsTextColor, formsPlaceHolderColor);
+                       }
+
+                       if(HasFocus)
+                               ViewCompat.SetBackgroundTintList(EditText, _focusedUnderlineColorsList);
+                       else
+                               ViewCompat.SetBackgroundTintList(EditText, _unfocusedUnderlineColorsList);
+
+                       if (HasFocus || !string.IsNullOrWhiteSpace(EditText.Text))
+                               this.DefaultHintTextColor = _focusedFilledColorList;
+                       else
+                               this.DefaultHintTextColor = _unfocusedEmptyColorList;
+               }
+
+               void ApplyTheme() => ApplyTheme(_formsTextColor, _formsPlaceholderColor);
+
+               /*
+                * This currently does two things
+                * 1) It's a hacky way of keeping the underline color matching the TextColor.
+                * when the entry gets focused the underline gets changed to the themes active color 
+                * and this is the only way to set it away from that and to whatever the user specified
+                * 2) The HintTextColor has a different alpha when focused vs not focused
+                * */
+               void OnFocusChange(object sender, FocusChangeEventArgs e) => 
+                       Device.BeginInvokeOnMainThread(() => ApplyTheme());
+
+
+               internal void SetHint(string hint, VisualElement element)
+               {
+                       if (HintEnabled != !String.IsNullOrWhiteSpace(hint))
+                       {
+                               HintEnabled = !String.IsNullOrWhiteSpace(hint);
+                               Hint = hint ?? String.Empty;
+                               EditText.Hint = String.Empty;
+                               element?.InvalidateMeasureNonVirtual(Internals.InvalidationTrigger.VerticalOptionsChanged);
+                       }
+                       else
+                       {
+                               Hint = hint ?? String.Empty;
+                       }
+               }
+
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (!_isDisposed)
+                       {
+                               _isDisposed = true;
+                               if (EditText != null)
+                                       EditText.FocusChange -= OnFocusChange;
+                       }
+
+                       base.Dispose(disposing);
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialPickerEditText.cs b/Xamarin.Forms.Platform.Android/Material/MaterialPickerEditText.cs
new file mode 100644 (file)
index 0000000..55d1014
--- /dev/null
@@ -0,0 +1,60 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Graphics;
+using Android.Runtime;
+using Android.Support.Design.Widget;
+using Android.Util;
+using Android.Views;
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialPickerEditText : MaterialFormsEditTextBase
+       {
+               bool _isDisposed = false;
+               public MaterialPickerEditText(Context context) : base(context)
+               {
+                       PickerManager.Init(this);
+               }
+
+               protected MaterialPickerEditText(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+               {
+                       PickerManager.Init(this);
+               }
+
+               public MaterialPickerEditText(Context context, IAttributeSet attrs) : base(context, attrs)
+               {
+                       PickerManager.Init(this);
+               }
+
+               public MaterialPickerEditText(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+               {
+                       PickerManager.Init(this);
+               }
+
+               public override bool OnTouchEvent(MotionEvent e)
+               {
+                       PickerManager.OnTouchEvent(this, e);
+                       return base.OnTouchEvent(e); // raises the OnClick event if focus is already received
+               }
+
+               protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Rect previouslyFocusedRect)
+               {
+                       base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
+                       PickerManager.OnFocusChanged(gainFocus, this, (IPopupTrigger)Parent.Parent);
+
+               }
+
+               protected override void Dispose(bool disposing)
+               {
+                       if (disposing && !_isDisposed)
+                       {
+                               _isDisposed = true;
+                               PickerManager.Dispose(this);
+                       }
+
+                       base.Dispose(disposing);
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialPickerRenderer.cs b/Xamarin.Forms.Platform.Android/Material/MaterialPickerRenderer.cs
new file mode 100644 (file)
index 0000000..09c7924
--- /dev/null
@@ -0,0 +1,55 @@
+#if __ANDROID_28__
+using System.ComponentModel;
+using Android.Content;
+using Android.Support.V4.View;
+using Android.Views;
+using Android.Widget;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Android.Material;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Picker), typeof(MaterialPickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialPickerRenderer : AppCompat.PickerRendererBase<MaterialPickerTextInputLayout>
+       {
+               MaterialPickerTextInputLayout _textInputLayout;
+               MaterialPickerEditText _textInputEditText;
+
+               public MaterialPickerRenderer(Context context) : base(MaterialContextThemeWrapper.Create(context))
+               {
+               }
+
+
+               protected override EditText EditText => _textInputEditText;
+
+               protected override MaterialPickerTextInputLayout CreateNativeControl()
+               {
+                       var inflater = LayoutInflater.FromContext(Context);
+                       var view = inflater.Inflate(Resource.Layout.MaterialPickerTextInput, null);
+                       _textInputLayout = (MaterialPickerTextInputLayout)view;
+                       _textInputEditText = _textInputLayout.FindViewById<MaterialPickerEditText>(Resource.Id.materialformsedittext);
+                       
+                       return _textInputLayout;
+               }
+
+               protected override void UpdateBackgroundColor()
+               {
+                       if (_textInputLayout == null)
+                               return;
+
+                       _textInputLayout.BoxBackgroundColor = MaterialColors.CreateEntryFilledInputBackgroundColor(Element.BackgroundColor, Element.TextColor);
+               }
+
+               protected internal override void UpdatePlaceHolderText()
+               {
+                       _textInputLayout.SetHint(string.Empty, Element);
+               }
+
+               protected internal override void UpdateTitleColor() => ApplyTheme();
+               protected override void UpdateTextColor() => ApplyTheme();
+
+               void ApplyTheme() => _textInputLayout?.ApplyTheme(Element.TextColor, Color.Default);
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialPickerTextInputLayout.cs b/Xamarin.Forms.Platform.Android/Material/MaterialPickerTextInputLayout.cs
new file mode 100644 (file)
index 0000000..3e1648c
--- /dev/null
@@ -0,0 +1,30 @@
+#if __ANDROID_28__
+using System;
+using Android.Content;
+using Android.Runtime;
+using Android.Util;
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialPickerTextInputLayout : MaterialFormsTextInputLayoutBase, IPopupTrigger
+       {
+               public bool ShowPopupOnFocus { get; set; }
+
+               public MaterialPickerTextInputLayout(Context context) : base(context)
+               {
+               }
+
+               public MaterialPickerTextInputLayout(Context context, IAttributeSet attrs) : base(context, attrs)
+               {
+               }
+
+               public MaterialPickerTextInputLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
+               {
+               }
+
+               protected MaterialPickerTextInputLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
+               {
+               }
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Material/MaterialTimePickerRenderer.cs b/Xamarin.Forms.Platform.Android/Material/MaterialTimePickerRenderer.cs
new file mode 100644 (file)
index 0000000..f251261
--- /dev/null
@@ -0,0 +1,59 @@
+#if __ANDROID_28__
+using System.ComponentModel;
+using Android.Content;
+using Android.Support.V4.View;
+using Android.Views;
+using Android.Widget;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Android.Material;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.TimePicker), typeof(MaterialTimePickerRenderer), new[] { typeof(VisualRendererMarker.Material) })]
+
+namespace Xamarin.Forms.Platform.Android.Material
+{
+       public class MaterialTimePickerRenderer : TimePickerRendererBase<MaterialPickerTextInputLayout>
+       {
+               MaterialPickerTextInputLayout _textInputLayout;
+               MaterialPickerEditText _textInputEditText;
+
+               public MaterialTimePickerRenderer(Context context) : base(MaterialContextThemeWrapper.Create(context))
+               {
+               }
+
+               protected override EditText EditText => _textInputEditText;
+
+               protected override MaterialPickerTextInputLayout CreateNativeControl()
+               {
+                       LayoutInflater inflater = LayoutInflater.FromContext(Context);
+                       var view = inflater.Inflate(Resource.Layout.MaterialPickerTextInput, null);
+                       _textInputLayout = (MaterialPickerTextInputLayout)view;
+                       _textInputEditText = _textInputLayout.FindViewById<MaterialPickerEditText>(Resource.Id.materialformsedittext);
+                       
+                       return _textInputLayout;
+               }
+
+
+               protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+               {
+                       base.OnElementChanged(e);
+                       _textInputLayout.SetHint(string.Empty, Element);
+               }
+
+               protected override void UpdateBackgroundColor()
+               {
+                       if (_textInputLayout == null)
+                               return;
+
+                       _textInputLayout.BoxBackgroundColor = MaterialColors.CreateEntryFilledInputBackgroundColor(Element.BackgroundColor, Element.TextColor);
+               }
+
+               protected override void UpdateTextColor() => ApplyTheme();
+
+               void ApplyTheme()
+               {
+                       _textInputLayout?.ApplyTheme(Element.TextColor, Color.Default);
+               }
+
+       }
+}
+#endif
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/PickerManager.cs b/Xamarin.Forms.Platform.Android/PickerManager.cs
new file mode 100644 (file)
index 0000000..52fcd99
--- /dev/null
@@ -0,0 +1,88 @@
+using Android.Views;
+using Android.Widget;
+using Android.Text;
+using System.Collections.Generic;
+using AView = global::Android.Views.View;
+using Java.Lang;
+using Android.Text.Style;
+
+namespace Xamarin.Forms.Platform.Android
+{
+       internal static class PickerManager
+       {
+               readonly static HashSet<Keycode> availableKeys = new HashSet<Keycode>(new[] {
+                       Keycode.Tab, Keycode.Forward, Keycode.Back, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp
+               });
+
+               public static void Init(EditText editText)
+               {
+                       editText.Focusable = true;
+                       editText.Clickable = true;
+                       editText.InputType = InputTypes.Null;
+                       editText.KeyPress += OnKeyPress;
+
+                       editText.SetOnClickListener(PickerListener.Instance);
+               }
+
+               public static void OnTouchEvent(EditText sender, MotionEvent e)
+               {
+                       if (e.Action == MotionEventActions.Up && !sender.IsFocused)
+                       {
+                               sender.RequestFocus();
+                       }
+               }
+
+               public static void OnFocusChanged(bool gainFocus, EditText sender, IPopupTrigger popupTrigger)
+               {
+                       if (gainFocus && popupTrigger.ShowPopupOnFocus)
+                               sender.CallOnClick();
+
+                       popupTrigger.ShowPopupOnFocus = false;
+               }
+
+               static void OnKeyPress(object sender, AView.KeyEventArgs e)
+               {
+                       if (availableKeys.Contains(e.KeyCode))
+                       {
+                               e.Handled = false;
+                               return;
+                       }
+                       e.Handled = true;
+                       (sender as AView)?.CallOnClick();
+               }
+
+               public static void Dispose(EditText editText)
+               {
+                       editText.KeyPress -= OnKeyPress;
+                       editText.SetOnClickListener(null);
+               }
+
+               public static ICharSequence GetTitle(Color titleColor, string title)
+               {
+                       if(titleColor == Color.Default)
+                               return new Java.Lang.String(title);
+
+                       var spannableTitle = new SpannableString(title ?? "");
+                       spannableTitle.SetSpan(new ForegroundColorSpan(titleColor.ToAndroid()), 0, spannableTitle.Length(), SpanTypes.ExclusiveExclusive);
+                       return spannableTitle;
+               }
+
+               class PickerListener : global::Java.Lang.Object, AView.IOnClickListener
+               {
+                       public static readonly PickerListener Instance = new PickerListener();
+
+                       public void OnClick(global::Android.Views.View v)
+                       {
+                               if (v is AView picker)
+                               {
+                                       if (picker?.Parent is IPickerRenderer renderer1)
+                                               renderer1.OnClick();
+                                       else if (picker?.Parent?.Parent?.Parent is IPickerRenderer renderer2)
+                                               renderer2.OnClick();
+                                       else
+                                               throw new System.Exception("Renderer not found temp check for Shane things");
+                               }
+                       }
+               }
+       }
+}
\ No newline at end of file
index 398ac50..27e56ed 100644 (file)
@@ -3,17 +3,21 @@ using System.ComponentModel;
 using Android.App;
 using Android.Content;
 using Android.Util;
+using Android.Views;
 using Android.Widget;
+using AColor = Android.Graphics.Color;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       public class DatePickerRenderer : ViewRenderer<DatePicker, EditText>, IPickerRenderer
+       public abstract class DatePickerRendererBase<TControl> : ViewRenderer<DatePicker, TControl>, IPickerRenderer
+               where TControl : global::Android.Views.View
        {
+               int _originalHintTextColor;
                DatePickerDialog _dialog;
                bool _disposed;
-               TextColorSwitcher _textColorSwitcher;
+               protected abstract EditText EditText { get; }
 
-               public DatePickerRenderer(Context context) : base(context)
+               public DatePickerRendererBase(Context context) : base(context)
                {
                        AutoPackage = false;
                        if (Forms.IsLollipopOrNewer)
@@ -22,7 +26,7 @@ namespace Xamarin.Forms.Platform.Android
 
                [Obsolete("This constructor is obsolete as of version 2.5. Please use DatePickerRenderer(Context) instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
-               public DatePickerRenderer()
+               public DatePickerRendererBase()
                {
                        AutoPackage = false;
                        if (Forms.IsLollipopOrNewer)
@@ -50,9 +54,9 @@ namespace Xamarin.Forms.Platform.Android
                        base.Dispose(disposing);
                }
 
-               protected override EditText CreateNativeControl()
+               public override bool DispatchTouchEvent(MotionEvent e)
                {
-                       return new PickerEditText(Context, this);
+                       return base.DispatchTouchEvent(e);
                }
 
                protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
@@ -62,11 +66,8 @@ namespace Xamarin.Forms.Platform.Android
                        if (e.OldElement == null)
                        {
                                var textField = CreateNativeControl();
-
                                SetNativeControl(textField);
-
-                               var useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
-                               _textColorSwitcher = new TextColorSwitcher(textField.TextColors, useLegacyColorManagement); 
+                               _originalHintTextColor = EditText.CurrentHintTextColor;
                        }
 
                        SetDate(Element.Date);
@@ -158,7 +159,6 @@ namespace Xamarin.Forms.Platform.Android
 
                        UpdateMinimumDate();
                        UpdateMaximumDate();
-
                        if (Forms.IsLollipopOrNewer)
                                _dialog.CancelEvent += OnCancelButtonClicked;
 
@@ -172,13 +172,13 @@ namespace Xamarin.Forms.Platform.Android
 
                void SetDate(DateTime date)
                {
-                       Control.Text = date.ToString(Element.Format);
+                       EditText.Text = date.ToString(Element.Format);
                }
 
                void UpdateFont()
                {
-                       Control.Typeface = Element.ToTypeface();
-                       Control.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
+                       EditText.Typeface = Element.ToTypeface();
+                       EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
                }
 
                void UpdateMaximumDate()
@@ -197,9 +197,33 @@ namespace Xamarin.Forms.Platform.Android
                        }
                }
 
-               void UpdateTextColor()
+               abstract protected void UpdateTextColor();
+       }
+
+
+       public class DatePickerRenderer : DatePickerRendererBase<EditText>
+       {
+               TextColorSwitcher _textColorSwitcher;
+               [Obsolete("This constructor is obsolete as of version 2.5. Please use DatePickerRenderer(Context) instead.")]
+               public DatePickerRenderer()
+               {
+               }
+
+               public DatePickerRenderer(Context context) : base(context)
+               {
+               }
+
+               protected override EditText CreateNativeControl()
+               {
+                       return new PickerEditText(Context);
+               }
+
+               protected override EditText EditText => Control;
+
+               protected override void UpdateTextColor()
                {
-                       _textColorSwitcher?.UpdateTextColor(Control, Element.TextColor);
+                       _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
+                       _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
                }
        }
 }
\ No newline at end of file
index 8e422d2..ef712ba 100644 (file)
@@ -2,8 +2,6 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using Android.Content;
-using Android.Content.Res;
-using Android.OS;
 using Android.Text;
 using Android.Text.Method;
 using Android.Util;
@@ -17,6 +15,9 @@ namespace Xamarin.Forms.Platform.Android
 {
        public class EntryRenderer : EntryRendererBase<FormsEditText>
        {
+               TextColorSwitcher _hintColorSwitcher;
+               TextColorSwitcher _textColorSwitcher;
+
                public EntryRenderer(Context context) : base(context)
                {
                }
@@ -41,13 +42,23 @@ namespace Xamarin.Forms.Platform.Android
                        bool isReadOnly = !Element.IsReadOnly;
                        Control.SetCursorVisible(isReadOnly);
                }
+
+               protected override void UpdatePlaceholderColor()
+               {
+                       _hintColorSwitcher = _hintColorSwitcher ?? new TextColorSwitcher(EditText.HintTextColors, Element.UseLegacyColorManagement());
+                       _hintColorSwitcher.UpdateTextColor(EditText, Element.PlaceholderColor, EditText.SetHintTextColor);
+               }
+
+               protected override void UpdateTextColor()
+               {
+                       _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
+                       _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
+               }
        }
 
        public abstract class EntryRendererBase<TControl> : ViewRenderer<Entry, TControl>, ITextWatcher, TextView.IOnEditorActionListener
                where TControl : global::Android.Views.View
        {
-               TextColorSwitcher _hintColorSwitcher;
-               TextColorSwitcher _textColorSwitcher;
                bool _disposed;
                ImeAction _currentInputImeFlag;
                IElementController ElementController => Element as IElementController;
@@ -56,10 +67,8 @@ namespace Xamarin.Forms.Platform.Android
                bool _selectionLengthChangePending;
                bool _nativeSelectionIsUpdating;
 
-
                protected abstract EditText EditText { get; }
 
-
                public EntryRendererBase(Context context) : base(context)
                {
                        AutoPackage = false;
@@ -135,11 +144,6 @@ namespace Xamarin.Forms.Platform.Android
                                        formsEditText.OnKeyboardBackPressed += OnKeyboardBackPressed;
                                        formsEditText.SelectionChanged += SelectionChanged;
                                }
-
-                               var useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
-
-                               _textColorSwitcher = new TextColorSwitcher(EditText.TextColors, useLegacyColorManagement);
-                               _hintColorSwitcher = new TextColorSwitcher(EditText.HintTextColors, useLegacyColorManagement);
                        }
 
                        // When we set the control text, it triggers the SelectionChanged event, which updates CursorPosition and SelectionLength;
@@ -151,7 +155,7 @@ namespace Xamarin.Forms.Platform.Android
                        EditText.Text = Element.Text;
                        UpdateInputType();
 
-                       UpdateColor();
+                       UpdateTextColor();
                        UpdateAlignment();
                        UpdateFont();
                        UpdatePlaceholderColor();
@@ -191,7 +195,7 @@ namespace Xamarin.Forms.Platform.Android
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
                {
                        if (e.PropertyName == Entry.PlaceholderProperty.PropertyName)
-                               EditText.Hint = Element.Placeholder;
+                               UpdatePlaceHolderText();
                        else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
                                UpdateInputType();
                        else if (e.PropertyName == Entry.TextProperty.PropertyName)
@@ -207,7 +211,7 @@ namespace Xamarin.Forms.Platform.Android
                                }
                        }
                        else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
-                               UpdateColor();
+                               UpdateTextColor();
                        else if (e.PropertyName == InputView.KeyboardProperty.PropertyName)
                                UpdateInputType();
                        else if (e.PropertyName == InputView.IsSpellCheckEnabledProperty.PropertyName)
@@ -264,9 +268,7 @@ namespace Xamarin.Forms.Platform.Android
                        EditText.UpdateHorizontalAlignment(Element.HorizontalTextAlignment, Context.HasRtlSupport());
                }
 
-               internal protected virtual void UpdateColor() => UpdateTextColor(Element.TextColor);
-
-               internal protected void UpdateTextColor(Color color) => _textColorSwitcher.UpdateTextColor(EditText, color);
+               abstract protected void UpdateTextColor();
 
                protected internal virtual void UpdateFont()
                {
@@ -313,10 +315,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateFont();
                }
 
-               protected internal virtual void UpdatePlaceholderColor()
-               {
-                       _hintColorSwitcher.UpdateTextColor(EditText, Element.PlaceholderColor, EditText.SetHintTextColor);
-               }
+               abstract protected void UpdatePlaceholderColor();
 
                void OnKeyboardBackPressed(object sender, EventArgs eventArgs)
                {
index 1ec07ce..134f862 100644 (file)
@@ -8,21 +8,12 @@ using System.ComponentModel;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       public class FormsEditText : EditText, IDescendantFocusToggler, IFormsEditText
+       public class FormsEditText : FormsEditTextBase, IFormsEditText
        {
-               DescendantFocusToggler _descendantFocusToggler;
-
                public FormsEditText(Context context) : base(context)
                {
-                       DrawableCompat.Wrap(Background);
                }
 
-               bool IDescendantFocusToggler.RequestFocus(global::Android.Views.View control, Func<bool> baseRequestFocus)
-               {
-                       _descendantFocusToggler = _descendantFocusToggler ?? new DescendantFocusToggler();
-
-                       return _descendantFocusToggler.RequestFocus(control, baseRequestFocus);
-               }
 
                public override bool OnKeyPreIme(Keycode keyCode, KeyEvent e)
                {
@@ -37,11 +28,6 @@ namespace Xamarin.Forms.Platform.Android
                        return true;
                }
 
-               public override bool RequestFocus(FocusSearchDirection direction, Rect previouslyFocusedRect)
-               {
-                       return (this as IDescendantFocusToggler).RequestFocus(this, () => base.RequestFocus(direction, previouslyFocusedRect));
-               }
-
                protected override void OnSelectionChanged(int selStart, int selEnd)
                {
                        base.OnSelectionChanged(selStart, selEnd);
@@ -63,6 +49,31 @@ namespace Xamarin.Forms.Platform.Android
                }
        }
 
+       public class FormsEditTextBase : EditText, IDescendantFocusToggler
+       {
+               DescendantFocusToggler _descendantFocusToggler;
+
+               public FormsEditTextBase(Context context) : base(context)
+               {
+                       DrawableCompat.Wrap(Background);
+               }
+
+               bool IDescendantFocusToggler.RequestFocus(global::Android.Views.View control, Func<bool> baseRequestFocus)
+               {
+                       _descendantFocusToggler = _descendantFocusToggler ?? new DescendantFocusToggler();
+
+                       return _descendantFocusToggler.RequestFocus(control, baseRequestFocus);
+               }
+
+
+               public override bool RequestFocus(FocusSearchDirection direction, Rect previouslyFocusedRect)
+               {
+                       return (this as IDescendantFocusToggler).RequestFocus(this, () => base.RequestFocus(direction, previouslyFocusedRect));
+               }
+
+
+       }
+
        public class SelectionChangedEventArgs : EventArgs
        {
                public int Start { get; private set; }
index 34d3e31..5ac6558 100644 (file)
@@ -1,82 +1,39 @@
+using System;
 using Android.Content;
-using Android.Views;
-using Android.Widget;
-using Android.Text;
-using Java.Lang;
-using System.Collections.Generic;
 using Android.Graphics;
 using Android.Runtime;
+using Android.Views;
+using Android.Widget;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       public class PickerEditText : EditText, IPopupTrigger
+       public class PickerEditText : FormsEditTextBase, IPopupTrigger
        {
-               readonly static HashSet<Keycode> availableKeys = new HashSet<Keycode>(new[] {
-                       Keycode.Tab, Keycode.Forward, Keycode.Back, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp
-               });
-
-               System.WeakReference<IPickerRenderer> rendererRef;
-
                public bool ShowPopupOnFocus { get; set; }
 
-               public PickerEditText(Context context, IPickerRenderer pickerRenderer) : base(context)
-               {
-                       Focusable = true;
-                       Clickable = true;
-                       InputType = InputTypes.Null;
-                       KeyPress += OnKeyPress;
-                       rendererRef = new System.WeakReference<IPickerRenderer>(pickerRenderer);
-                       SetOnClickListener(PickerListener.Instance);
-               }
-
-               protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Rect previouslyFocusedRect)
-               {
-                       base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
-                       if (gainFocus && ShowPopupOnFocus)
-                               CallOnClick();
-                       ShowPopupOnFocus = false;
-               }
-
-               void OnKeyPress(object sender, KeyEventArgs e)
+               public PickerEditText(Context context) : base(context)
                {
-                       if (availableKeys.Contains(e.KeyCode))
-                       {
-                               e.Handled = false;
-                               return;
-                       }
-                       e.Handled = true;
-                       CallOnClick();
+                       PickerManager.Init(this);
                }
 
                public override bool OnTouchEvent(MotionEvent e)
                {
-                       if (e.Action == MotionEventActions.Up && !IsFocused)
-                               RequestFocus();
+                       PickerManager.OnTouchEvent(this, e);
                        return base.OnTouchEvent(e); // raises the OnClick event if focus is already received
                }
 
-               protected override void Dispose(bool disposing)
+               protected override void OnFocusChanged(bool gainFocus, [GeneratedEnum] FocusSearchDirection direction, Rect previouslyFocusedRect)
                {
-                       if (disposing)
-                       {
-                               KeyPress -= OnKeyPress;
-                               rendererRef = null;
-                       }
-                       base.Dispose(disposing);
+                       base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
+                       PickerManager.OnFocusChanged(gainFocus, this, this);
                }
 
-               class PickerListener : Object, IOnClickListener
+               protected override void Dispose(bool disposing)
                {
-                       public static readonly PickerListener Instance = new PickerListener();
+                       if (disposing)
+                               PickerManager.Dispose(this);
 
-                       public void OnClick(global::Android.Views.View v)
-                       {
-                               if (v is PickerEditText picker)
-                               {
-                                       picker.rendererRef.TryGetTarget(out IPickerRenderer renderer);
-                                       renderer?.OnClick();
-                               }
-                       }
+                       base.Dispose(disposing);
                }
        }
 }
\ No newline at end of file
index edcfdd2..60a6eb8 100644 (file)
@@ -48,7 +48,7 @@ namespace Xamarin.Forms.Platform.Android
 
                protected override EditText CreateNativeControl()
                {
-                       return new PickerEditText(Context, this);
+                       return new PickerEditText(Context);
                }
 
                protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
index ece4adc..d102b6d 100644 (file)
@@ -7,31 +7,35 @@ using Android.Text.Format;
 using ATimePicker = Android.Widget.TimePicker;
 using Android.OS;
 using Android.Widget;
+using AColor = Android.Graphics.Color;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       public class TimePickerRenderer : ViewRenderer<TimePicker, EditText>, TimePickerDialog.IOnTimeSetListener, IPickerRenderer
+       public abstract class TimePickerRendererBase<TControl> : ViewRenderer<TimePicker, TControl>, TimePickerDialog.IOnTimeSetListener, IPickerRenderer
+               where TControl : global::Android.Views.View
        {
+               int _originalHintTextColor;
                AlertDialog _dialog;
-               TextColorSwitcher _textColorSwitcher;
 
                bool Is24HourView
                {
                        get => (DateFormat.Is24HourFormat(Context) && Element.Format == (string)TimePicker.FormatProperty.DefaultValue) || Element.Format == "HH:mm";
                }
 
-               public TimePickerRenderer(Context context) : base(context)
+               public TimePickerRendererBase(Context context) : base(context)
                {
                        AutoPackage = false;
                }
 
                [Obsolete("This constructor is obsolete as of version 2.5. Please use TimePickerRenderer(Context) instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
-               public TimePickerRenderer()
+               public TimePickerRendererBase()
                {
                        AutoPackage = false;
                }
 
+               protected abstract EditText EditText { get; }
+
                IElementController ElementController => Element as IElementController;
 
                void TimePickerDialog.IOnTimeSetListener.OnTimeSet(ATimePicker view, int hourOfDay, int minute)
@@ -46,11 +50,6 @@ namespace Xamarin.Forms.Platform.Android
                        _dialog = null;
                }
 
-               protected override EditText CreateNativeControl()
-               {
-                       return new PickerEditText(Context, this);
-               }
-
                protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
                {
                        base.OnElementChanged(e);
@@ -60,9 +59,7 @@ namespace Xamarin.Forms.Platform.Android
                                var textField = CreateNativeControl();
 
                                SetNativeControl(textField);
-
-                               var useLegacyColorManagement = e.NewElement.UseLegacyColorManagement();
-                               _textColorSwitcher = new TextColorSwitcher(textField.TextColors, useLegacyColorManagement);
+                               _originalHintTextColor = EditText.CurrentHintTextColor;
                        }
 
                        SetTime(e.NewElement.Time);
@@ -132,21 +129,45 @@ namespace Xamarin.Forms.Platform.Android
                        Element.Unfocus();
                }
 
+
                void SetTime(TimeSpan time)
                {
                        var timeFormat = Is24HourView ? "HH:mm" : Element.Format;
-                       Control.Text = DateTime.Today.Add(time).ToString(timeFormat);
+                       EditText.Text = DateTime.Today.Add(time).ToString(timeFormat);
                }
 
                void UpdateFont()
                {
-                       Control.Typeface = Element.ToTypeface();
-                       Control.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
+                       EditText.Typeface = Element.ToTypeface();
+                       EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
                }
+               
+               abstract protected void UpdateTextColor();
+       }
 
-               void UpdateTextColor()
+       public class TimePickerRenderer : TimePickerRendererBase<EditText>
+       {
+               TextColorSwitcher _textColorSwitcher;
+               [Obsolete("This constructor is obsolete as of version 2.5. Please use TimePickerRenderer(Context) instead.")]
+               public TimePickerRenderer()
                {
-                       _textColorSwitcher?.UpdateTextColor(Control, Element.TextColor);
+               }
+
+               public TimePickerRenderer(Context context) : base(context)
+               {
+               }
+
+               protected override EditText CreateNativeControl()
+               {
+                       return new PickerEditText(Context);
+               }
+
+               protected override EditText EditText => Control;
+               protected override void UpdateTextColor()
+               {
+                       _textColorSwitcher = _textColorSwitcher ?? new TextColorSwitcher(EditText.TextColors, Element.UseLegacyColorManagement());
+                       _textColorSwitcher.UpdateTextColor(EditText, Element.TextColor);
                }
        }
+
 }
diff --git a/Xamarin.Forms.Platform.Android/Resources/Layout/MaterialPickerTextInput.axml b/Xamarin.Forms.Platform.Android/Resources/Layout/MaterialPickerTextInput.axml
new file mode 100644 (file)
index 0000000..fc3f9fd
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xamarin.forms.platform.android.material.MaterialPickerTextInputLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="@style/XamarinFormsMaterialEntryFilled">
+    <xamarin.forms.platform.android.material.MaterialPickerEditText
+               android:id="@+id/materialformsedittext"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="-7dp"/>
+</xamarin.forms.platform.android.material.MaterialPickerTextInputLayout>
index 41758bd..cbb68ff 100644 (file)
@@ -6,7 +6,7 @@
     style="@style/XamarinFormsMaterialEntryFilled">
     <xamarin.forms.platform.android.material.MaterialFormsEditText
                android:id="@+id/materialformsedittext"
-        android:layout_marginBottom="-7dp"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="-7dp" />
 </xamarin.forms.platform.android.material.MaterialFormsTextInputLayout>
index dce5e7d..a3ac088 100644 (file)
     <Compile Include="IBorderVisualElementRenderer.cs" />
     <Compile Include="IDeviceInfoProvider.cs" />
     <Compile Include="ITabStop.cs" />
+    <Compile Include="Material\MaterialFormsTextInputLayoutBase.cs" />
+    <Compile Include="Material\MaterialFormsEditTextBase.cs" />
+    <Compile Include="Material\MaterialFormsEditTextManager.cs" />
+    <Compile Include="Material\MaterialPickerTextInputLayout.cs" />
+    <Compile Include="Material\MaterialFormsTextInputLayout.cs" />
+    <Compile Include="Material\MaterialPickerEditText.cs" />
     <Compile Include="Material\MaterialContextThemeWrapper.cs" />
     <Compile Include="Material\MaterialFrameRenderer.cs" />
     <Compile Include="Material\MaterialFormsEditText.cs" />
     <Compile Include="Material\MaterialButtonRenderer.cs" />
     <Compile Include="Material\MaterialEntryRenderer.cs" />
+    <Compile Include="Material\MaterialDatePickerRenderer.cs" />
+    <Compile Include="Material\MaterialPickerRenderer.cs" />
+    <Compile Include="Material\MaterialTimePickerRenderer.cs" />
     <Compile Include="Material\MaterialProgressBarRenderer.cs" />
     <Compile Include="IPickerRenderer.cs" />
+    <Compile Include="PickerManager.cs" />
     <Compile Include="Renderers\CircularProgress.cs" />
     <Compile Include="Renderers\PickerEditText.cs" />
     <Compile Include="Renderers\FontImageSourceHandler.cs" />
       <Generator>MSBuild:UpdateGeneratedFiles</Generator>
       <SubType>Designer</SubType>
     </AndroidResource>
-  </ItemGroup>
-  <ItemGroup Condition=" '$(TargetFrameworkVersion)' == 'v9.0' ">
     <AndroidResource Include="Resources\values\styles.xml">
       <SubType>Designer</SubType>
     </AndroidResource>
+    <AndroidResource Include="Resources\Layout\MaterialPickerTextInput.axml">
+      <Generator>MSBuild:UpdateGeneratedFiles</Generator>
+      <SubType>Designer</SubType>
+    </AndroidResource>
   </ItemGroup>
   <ItemGroup>
     <AndroidResource Include="Resources\drawable\MaterialActivityIndicatorBackground.xml">
index 663a5b1..676ebbf 100644 (file)
@@ -22,7 +22,16 @@ namespace Xamarin.Forms.Platform.iOS
                }
        }
 
-       public class DatePickerRenderer : ViewRenderer<DatePicker, UITextField>
+       public class DatePickerRenderer : DatePickerRendererBase<UITextField>
+       {
+               protected override UITextField CreateNativeControl()
+               {
+                       return new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
+               }
+       }
+
+       public abstract class DatePickerRendererBase<TControl> : ViewRenderer<DatePicker, TControl>
+               where TControl : UITextField
        {
                UIDatePicker _picker;
                UIColor _defaultTextColor;
@@ -31,6 +40,9 @@ namespace Xamarin.Forms.Platform.iOS
 
                IElementController ElementController => Element as IElementController;
 
+
+               abstract protected override TControl CreateNativeControl();
+
                protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
                {
                        base.OnElementChanged(e);
@@ -40,7 +52,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                        if (Control == null)
                        {
-                               var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
+                               var entry = CreateNativeControl();
 
                                entry.EditingDidBegin += OnStarted;
                                entry.EditingDidEnd += OnEnded;
@@ -126,7 +138,7 @@ namespace Xamarin.Forms.Platform.iOS
                        (Control as UITextField).UpdateTextAlignment(Element);
                }
                
-               void UpdateFont()
+               protected internal virtual void UpdateFont()
                {
                        Control.Font = Element.ToUIFont();
                }
@@ -141,7 +153,7 @@ namespace Xamarin.Forms.Platform.iOS
                        _picker.MinimumDate = Element.MinimumDate.ToNSDate();
                }
 
-               void UpdateTextColor()
+               protected internal virtual void UpdateTextColor()
                {
                        var textColor = Element.TextColor;
 
index 7978cd8..8b6780a 100644 (file)
@@ -10,7 +10,24 @@ using Specifics = Xamarin.Forms.PlatformConfiguration.iOSSpecific.Entry;
 
 namespace Xamarin.Forms.Platform.iOS
 {
-       public class EntryRenderer : ViewRenderer<Entry, UITextField>
+       public class EntryRenderer : EntryRendererBase<UITextField>
+       {
+               public EntryRenderer()
+               {
+                       Frame = new RectangleF(0, 20, 320, 40);
+               }
+
+               protected override UITextField CreateNativeControl()
+               {
+                       var textField = new UITextField(RectangleF.Empty);
+                       textField.BorderStyle = UITextBorderStyle.RoundedRect;
+                       textField.ClipsToBounds = true;
+                       return textField;
+               }
+       }
+
+       public abstract class EntryRendererBase<TControl> : ViewRenderer<Entry, TControl>
+               where TControl : UITextField
        {
                UIColor _defaultTextColor;
 
@@ -30,9 +47,8 @@ namespace Xamarin.Forms.Platform.iOS
                static readonly int baseHeight = 30;
                static CGSize initialSize = CGSize.Empty;
 
-               public EntryRenderer()
+               public EntryRendererBase()
                {
-                       Frame = new RectangleF(0, 20, 320, 40);
                }
 
                public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
@@ -77,13 +93,8 @@ namespace Xamarin.Forms.Platform.iOS
                        base.Dispose(disposing);
                }
 
-               protected override UITextField CreateNativeControl()
-               {
-                       var textField = new UITextField(RectangleF.Empty);
-                       textField.BorderStyle = UITextBorderStyle.RoundedRect;
-                       textField.ClipsToBounds = true;
-                       return textField;
-               }
+               abstract protected override TControl CreateNativeControl();
+
                protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
                {
                        base.OnElementChanged(e);
index 3c54213..49a3678 100644 (file)
@@ -23,7 +23,16 @@ namespace Xamarin.Forms.Platform.iOS
                        => enableActions.Contains(action.Name);
        }
 
-       public class PickerRenderer : ViewRenderer<Picker, UITextField>
+       public class PickerRenderer : PickerRendererBase<UITextField>
+       {
+               protected override UITextField CreateNativeControl()
+               {
+                       return new ReadOnlyField { BorderStyle = UITextBorderStyle.RoundedRect };
+               }
+       }
+
+       public abstract class PickerRendererBase<TControl> : ViewRenderer<Picker, TControl>
+               where TControl : UITextField
        {
                UIPickerView _picker;
                UIColor _defaultTextColor;
@@ -32,6 +41,8 @@ namespace Xamarin.Forms.Platform.iOS
 
                IElementController ElementController => Element as IElementController;
 
+
+               protected abstract override TControl CreateNativeControl();
                protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
                {
                        if (e.OldElement != null)
@@ -42,7 +53,7 @@ namespace Xamarin.Forms.Platform.iOS
                                if (Control == null)
                                {
                                        // disabled cut, delete, and toggle actions because they can throw an unhandled native exception
-                                       var entry = new ReadOnlyField { BorderStyle = UITextBorderStyle.RoundedRect };
+                                       var entry = CreateNativeControl();
 
                                        entry.EditingDidBegin += OnStarted;
                                        entry.EditingDidEnd += OnEnded;
@@ -135,28 +146,41 @@ namespace Xamarin.Forms.Platform.iOS
                        UpdatePicker();
                }
 
-               void UpdateFont()
+               protected internal virtual void UpdateFont()
                {
                        Control.Font = Element.ToUIFont();
                }
 
-               void UpdatePicker()
+               readonly Color _defaultPlaceholderColor = ColorExtensions.SeventyPercentGrey.ToColor();
+               protected internal virtual void UpdatePlaceholder()
                {
-                       var selectedIndex = Element.SelectedIndex;
-                       var items = Element.Items;
+                       var formatted = (FormattedString)Element.Title;
+
+                       if (formatted == null)
+                               return;
 
-                       if (!Element.IsSet(Picker.TitleColorProperty))
+                       var targetColor = Element.TitleColor;
+
+                       if (_useLegacyColorManagement)
                        {
-                               Control.AttributedPlaceholder = null;
-                               Control.Placeholder = Element.Title;
+                               var color = targetColor.IsDefault || !Element.IsEnabled ? _defaultPlaceholderColor : targetColor;
+                               Control.AttributedPlaceholder = formatted.ToAttributed(Element, color);
                        }
                        else
                        {
-                               Control.AttributedPlaceholder = new NSAttributedString(Element.Title, new UIStringAttributes
-                               {
-                                       ForegroundColor = Element.TitleColor.ToUIColor()
-                               });
+                               // Using VSM color management; take whatever is in Element.PlaceholderColor
+                               var color = targetColor.IsDefault ? _defaultPlaceholderColor : targetColor;
+                               Control.AttributedPlaceholder = formatted.ToAttributed(Element, color);
                        }
+               }
+
+
+               void UpdatePicker()
+               {
+                       var selectedIndex = Element.SelectedIndex;
+                       var items = Element.Items;
+
+                       UpdatePlaceholder();
 
                        var oldText = Control.Text;
                        Control.Text = selectedIndex == -1 || items == null || selectedIndex >= items.Count ? "" : items[selectedIndex];
@@ -193,7 +217,7 @@ namespace Xamarin.Forms.Platform.iOS
                        _picker.Select(Math.Max(formsIndex, 0), 0, true);
                }
 
-               void UpdateTextColor()
+               protected internal virtual void UpdateTextColor()
                {
                        var textColor = Element.TextColor;
 
@@ -246,10 +270,10 @@ namespace Xamarin.Forms.Platform.iOS
 
                class PickerSource : UIPickerViewModel
                {
-                       PickerRenderer _renderer;
+                       PickerRendererBase<TControl> _renderer;
                        bool _disposed;
 
-                       public PickerSource(PickerRenderer renderer)
+                       public PickerSource(PickerRendererBase<TControl> renderer)
                        {
                                _renderer = renderer;
                        }
index ec00d98..fd41a16 100644 (file)
@@ -6,7 +6,16 @@ using RectangleF = CoreGraphics.CGRect;
 
 namespace Xamarin.Forms.Platform.iOS
 {
-       public class TimePickerRenderer : ViewRenderer<TimePicker, UITextField>
+       public class TimePickerRenderer : TimePickerRendererBase<UITextField>
+       {
+               protected override UITextField CreateNativeControl()
+               {
+                       return new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
+               }
+       }
+
+       public abstract class TimePickerRendererBase<TControl> : ViewRenderer<TimePicker, TControl>
+               where TControl : UITextField
        {
                UIDatePicker _picker;
                UIColor _defaultTextColor;
@@ -44,13 +53,16 @@ namespace Xamarin.Forms.Platform.iOS
                        base.Dispose(disposing);
                }
 
+
+               protected abstract override TControl CreateNativeControl();
+
                protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
                {
                        if (e.NewElement != null)
                        {
                                if (Control == null)
                                {
-                                       var entry = new NoCaretField { BorderStyle = UITextBorderStyle.RoundedRect };
+                                       var entry = CreateNativeControl();
 
                                        entry.EditingDidBegin += OnStarted;
                                        entry.EditingDidEnd += OnEnded;
@@ -101,8 +113,7 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateTextColor();
                        else if (e.PropertyName == TimePicker.FontAttributesProperty.PropertyName || e.PropertyName == TimePicker.FontFamilyProperty.PropertyName || e.PropertyName == TimePicker.FontSizeProperty.PropertyName)
                                UpdateFont();
-
-                       if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
+                       else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateFlowDirection();
                }
 
@@ -125,13 +136,13 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        (Control as UITextField).UpdateTextAlignment(Element);
                }
-               
-               void UpdateFont()
+
+               protected internal virtual void UpdateFont()
                {
                        Control.Font = Element.ToUIFont();
                }
 
-               void UpdateTextColor()
+               protected internal virtual void UpdateTextColor()
                {
                        var textColor = Element.TextColor;