[Material] [Android, iOS] Added Slider and ProgressBar (#5209)
* [Android Material] Linear Progress Indicator (#5079) Merging into the material "slider" / "progress" bar branch so that we can share some code as they are the same control * [Material] [Slider, ProgressBar] Updated the progress bard and added the slider * Renamed the gallery fixes #5008 fixes #5079 fixes #5018
This commit is contained in:
Родитель
c1ae194404
Коммит
9ab1fff85f
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.Controls
|
||||
{
|
||||
public class ColorPicker : ContentView
|
||||
{
|
||||
public static readonly BindableProperty UseDefaultProperty = BindableProperty.Create(nameof(UseDefault), typeof(bool), typeof(ColorPicker), false,
|
||||
propertyChanged: OnColorChanged);
|
||||
|
||||
public static readonly BindableProperty ColorProperty = BindableProperty.Create(nameof(Color), typeof(Color), typeof(ColorPicker), Color.Default,
|
||||
propertyChanged: OnColorChanged);
|
||||
|
||||
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(ColorPicker), "Pick a color:",
|
||||
propertyChanged: OnTitleChanged);
|
||||
|
||||
static readonly string[] _components = { "R", "G", "B", "A" };
|
||||
|
||||
Label _titleLabel;
|
||||
Slider[] _sliders;
|
||||
BoxView _box;
|
||||
Label _hexLabel;
|
||||
Switch _useDefault;
|
||||
|
||||
public ColorPicker()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
Padding = 0,
|
||||
RowSpacing = 3,
|
||||
ColumnSpacing = 3,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = 20 },
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = 60 },
|
||||
},
|
||||
};
|
||||
|
||||
_titleLabel = new Label { Text = (string)TitleProperty.DefaultValue };
|
||||
grid.AddChild(_titleLabel, 0, 0, 2);
|
||||
|
||||
_useDefault = new Switch
|
||||
{
|
||||
IsToggled = true,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
VerticalOptions = LayoutOptions.Center
|
||||
};
|
||||
_useDefault.Toggled += OnUseDefaultToggled;
|
||||
grid.AddChild(_useDefault, 2, 0);
|
||||
|
||||
_sliders = new Slider[_components.Length];
|
||||
for (var i = 0; i < _components.Length; i++)
|
||||
{
|
||||
_sliders[i] = new Slider
|
||||
{
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
Minimum = 0,
|
||||
Maximum = 255,
|
||||
Value = 255
|
||||
};
|
||||
_sliders[i].ValueChanged += OnColorSliderChanged;
|
||||
var label = new Label
|
||||
{
|
||||
Text = _components[i],
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
VerticalOptions = LayoutOptions.Center
|
||||
};
|
||||
grid.AddChild(label, 0, i + 1);
|
||||
grid.AddChild(_sliders[i], 1, i + 1);
|
||||
}
|
||||
|
||||
_box = new BoxView
|
||||
{
|
||||
Color = Color,
|
||||
HorizontalOptions = LayoutOptions.Fill,
|
||||
VerticalOptions = LayoutOptions.Fill,
|
||||
};
|
||||
grid.AddChild(_box, 2, 1, 1, 3);
|
||||
|
||||
_hexLabel = new Label
|
||||
{
|
||||
Text = ColorToHex(Color),
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
FontSize = 10,
|
||||
};
|
||||
grid.AddChild(_hexLabel, 2, 4);
|
||||
|
||||
Content = grid;
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => (string)GetValue(TitleProperty);
|
||||
set => SetValue(TitleProperty, value);
|
||||
}
|
||||
|
||||
public bool UseDefault
|
||||
{
|
||||
get => (bool)GetValue(UseDefaultProperty);
|
||||
set => SetValue(UseDefaultProperty, value);
|
||||
}
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get => (Color)GetValue(ColorProperty);
|
||||
set => SetValue(ColorProperty, value);
|
||||
}
|
||||
|
||||
public event EventHandler<ColorPickedEventArgs> ColorPicked;
|
||||
|
||||
void OnColorSliderChanged(object sender, ValueChangedEventArgs e)
|
||||
{
|
||||
var color = Color.FromRgba(
|
||||
(int)_sliders[0].Value,
|
||||
(int)_sliders[1].Value,
|
||||
(int)_sliders[2].Value,
|
||||
(int)_sliders[3].Value);
|
||||
Color = color;
|
||||
}
|
||||
|
||||
private void OnUseDefaultToggled(object sender, ToggledEventArgs e)
|
||||
{
|
||||
UseDefault = !e.Value;
|
||||
|
||||
foreach (var slider in _sliders)
|
||||
slider.IsEnabled = e.Value;
|
||||
}
|
||||
|
||||
static void OnColorChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
if (bindable is ColorPicker picker)
|
||||
{
|
||||
var color = picker.UseDefault ? Color.Default : picker.Color;
|
||||
picker._hexLabel.Text = color.IsDefault ? "<default>" : ColorToHex(color);
|
||||
picker._box.Color = color;
|
||||
picker.ColorPicked?.Invoke(picker, new ColorPickedEventArgs(color));
|
||||
}
|
||||
}
|
||||
|
||||
static void OnTitleChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
if (bindable is ColorPicker picker)
|
||||
{
|
||||
picker._titleLabel.Text = picker.Title;
|
||||
}
|
||||
}
|
||||
|
||||
static string ColorToHex(Color color)
|
||||
{
|
||||
var a = (int)(color.A * 255);
|
||||
var r = (int)(color.R * 255);
|
||||
var g = (int)(color.G * 255);
|
||||
var b = (int)(color.B * 255);
|
||||
|
||||
var value = a << 24 | r << 16 | g << 8 | b;
|
||||
|
||||
return "#" + value.ToString("X");
|
||||
}
|
||||
}
|
||||
|
||||
public class ColorPickedEventArgs : EventArgs
|
||||
{
|
||||
public ColorPickedEventArgs(Color color)
|
||||
{
|
||||
Color = color;
|
||||
}
|
||||
|
||||
public Color Color { get; }
|
||||
}
|
||||
}
|
|
@ -321,6 +321,7 @@ namespace Xamarin.Forms.Controls
|
|||
new GalleryPageFactory(() => new OpenGLViewCoreGalleryPage(), "OpenGLView Gallery"),
|
||||
new GalleryPageFactory(() => new PickerCoreGalleryPage(), "Picker Gallery"),
|
||||
new GalleryPageFactory(() => new ProgressBarCoreGalleryPage(), "ProgressBar Gallery"),
|
||||
new GalleryPageFactory(() => new MaterialProgressBarGallery(), "[Material] ProgressBar & Slider Gallery"),
|
||||
new GalleryPageFactory(() => new ScrollGallery(), "ScrollView Gallery"),
|
||||
new GalleryPageFactory(() => new ScrollGallery(ScrollOrientation.Horizontal), "ScrollView Gallery Horizontal"),
|
||||
new GalleryPageFactory(() => new ScrollGallery(ScrollOrientation.Both), "ScrollView Gallery 2D"),
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
|
||||
namespace Xamarin.Forms.Controls
|
||||
{
|
||||
public class MaterialProgressBarGallery : ContentPage
|
||||
{
|
||||
public MaterialProgressBarGallery()
|
||||
{
|
||||
Visual = VisualMarker.Material;
|
||||
|
||||
var progressBar = new ProgressBar { Progress = 0.5 };
|
||||
var slider = new Slider { Value = 0.5 };
|
||||
|
||||
var primaryPicker = new ColorPicker { Title = "Primary Color" };
|
||||
primaryPicker.ColorPicked += (_, e) =>
|
||||
{
|
||||
progressBar.ProgressColor = e.Color;
|
||||
slider.MinimumTrackColor = e.Color;
|
||||
};
|
||||
var backgroundPicker = new ColorPicker { Title = "Background Color" };
|
||||
backgroundPicker.ColorPicked += (_, e) =>
|
||||
{
|
||||
progressBar.BackgroundColor = e.Color;
|
||||
slider.MaximumTrackColor = e.Color;
|
||||
};
|
||||
var thumbPicker = new ColorPicker { Title = "Thumb Color" };
|
||||
thumbPicker.ColorPicked += (_, e) =>
|
||||
{
|
||||
slider.ThumbColor = e.Color;
|
||||
};
|
||||
|
||||
var valuePicker = CreateValuePicker("Value / Progress", value =>
|
||||
{
|
||||
progressBar.Progress = value / 100.0;
|
||||
slider.Value = value / 100.0;
|
||||
});
|
||||
var heightPicker = CreateValuePicker("Height", value =>
|
||||
{
|
||||
progressBar.HeightRequest = value;
|
||||
slider.HeightRequest = value;
|
||||
});
|
||||
|
||||
Content = new StackLayout
|
||||
{
|
||||
Padding = 10,
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new ScrollView
|
||||
{
|
||||
Margin = new Thickness(-10, 0),
|
||||
Content = new StackLayout
|
||||
{
|
||||
Padding = 10,
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
primaryPicker,
|
||||
backgroundPicker,
|
||||
thumbPicker,
|
||||
valuePicker,
|
||||
heightPicker,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new BoxView
|
||||
{
|
||||
HeightRequest = 1,
|
||||
Margin = new Thickness(-10, 0),
|
||||
Color = Color.Black
|
||||
},
|
||||
|
||||
progressBar,
|
||||
slider
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Grid CreateValuePicker(string title, Action<double> changed)
|
||||
{
|
||||
// 50%
|
||||
Slider slider = new Slider(0, 100, 50);
|
||||
|
||||
var actions = new Grid
|
||||
{
|
||||
Padding = 0,
|
||||
ColumnSpacing = 6,
|
||||
RowSpacing = 6,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = 10 },
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = 30 }
|
||||
}
|
||||
};
|
||||
|
||||
actions.AddChild(new Label { Text = title }, 0, 0, 3);
|
||||
|
||||
var valueLabel = new Label
|
||||
{
|
||||
Text = slider.Value.ToString(),
|
||||
HorizontalOptions = LayoutOptions.End
|
||||
};
|
||||
|
||||
slider.ValueChanged += (_, e) =>
|
||||
{
|
||||
changed?.Invoke(slider.Value);
|
||||
valueLabel.Text = e.NewValue.ToString("0");
|
||||
};
|
||||
actions.AddChild(new Label { Text = "V" }, 0, 1);
|
||||
actions.AddChild(slider, 1, 1);
|
||||
actions.AddChild(valueLabel, 2, 1);
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,9 +38,7 @@ namespace Xamarin.Forms
|
|||
}, propertyChanged: (bindable, oldValue, newValue) =>
|
||||
{
|
||||
var slider = (Slider)bindable;
|
||||
EventHandler<ValueChangedEventArgs> eh = slider.ValueChanged;
|
||||
if (eh != null)
|
||||
eh(slider, new ValueChangedEventArgs((double)oldValue, (double)newValue));
|
||||
slider.ValueChanged?.Invoke(slider, new ValueChangedEventArgs((double)oldValue, (double)newValue));
|
||||
});
|
||||
|
||||
public static readonly BindableProperty MinimumTrackColorProperty = BindableProperty.Create(nameof(MinimumTrackColor), typeof(Color), typeof(Slider), Color.Default);
|
||||
|
|
|
@ -10,8 +10,6 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
{
|
||||
public class MaterialProgressBarRenderer : ViewRenderer<ProgressBar, MProgressView>
|
||||
{
|
||||
const float BackgroundAlpha = 0.3f;
|
||||
|
||||
BasicColorScheme _defaultColorScheme;
|
||||
BasicColorScheme _colorScheme;
|
||||
|
||||
|
@ -51,7 +49,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
var cs = MaterialColors.Light.CreateColorScheme();
|
||||
return new BasicColorScheme(
|
||||
cs.PrimaryColor,
|
||||
cs.PrimaryColor.ColorWithAlpha(BackgroundAlpha),
|
||||
cs.PrimaryColor.ColorWithAlpha(MaterialColors.SliderTrackAlpha),
|
||||
cs.PrimaryColor);
|
||||
}
|
||||
|
||||
|
@ -123,9 +121,6 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
|
||||
void UpdateAllColors()
|
||||
{
|
||||
// TODO: Fix this once Google implements the new way.
|
||||
// Right now, copy what is done with the activity indicator.
|
||||
|
||||
Color progressColor = Element.ProgressColor;
|
||||
Color backgroundColor = Element.BackgroundColor;
|
||||
|
||||
|
@ -144,8 +139,6 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
// handle the case where only the background is set
|
||||
var background = backgroundColor.ToUIColor();
|
||||
|
||||
// TODO: Potentially override background alpha to match material design.
|
||||
// TODO: Potentially override primary color to match material design.
|
||||
_colorScheme = new BasicColorScheme(
|
||||
_defaultColorScheme.PrimaryColor,
|
||||
background,
|
||||
|
@ -160,9 +153,10 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
// handle the case where only the progress is set
|
||||
var progress = progressColor.ToUIColor();
|
||||
|
||||
progress.GetRGBA(out _, out _, out _, out var alpha);
|
||||
_colorScheme = new BasicColorScheme(
|
||||
progress,
|
||||
progress.ColorWithAlpha(BackgroundAlpha),
|
||||
progress.ColorWithAlpha(alpha * MaterialColors.SliderTrackAlpha),
|
||||
progress);
|
||||
}
|
||||
else
|
||||
|
@ -171,7 +165,6 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
var background = backgroundColor.ToUIColor();
|
||||
var progress = progressColor.ToUIColor();
|
||||
|
||||
// TODO: Potentially override alpha to match material design.
|
||||
_colorScheme = new BasicColorScheme(
|
||||
progress,
|
||||
background,
|
||||
|
|
|
@ -141,15 +141,19 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
if (minColor.IsDefault && maxColor.IsDefault && thumbColor.IsDefault)
|
||||
return;
|
||||
|
||||
// TODO: Potentially override alpha to match material design.
|
||||
|
||||
if (!minColor.IsDefault)
|
||||
{
|
||||
Control.SetTrackFillColor(minColor.ToUIColor(), UIControlState.Normal);
|
||||
|
||||
// if no max color was specified, then use a shade of the min
|
||||
if (maxColor.IsDefault)
|
||||
Control.SetTrackBackgroundColor(MatchAlpha(minColor, Control.GetTrackBackgroundColor(UIControlState.Normal)), UIControlState.Normal);
|
||||
{
|
||||
Control.GetTrackBackgroundColor(UIControlState.Normal).GetRGBA(out _, out _, out _, out var baseAlpha);
|
||||
Control.SetTrackBackgroundColor(minColor.MultiplyAlpha(baseAlpha).ToUIColor(), UIControlState.Normal);
|
||||
}
|
||||
|
||||
if (thumbColor.IsDefault)
|
||||
Control.SetThumbColor(minColor.ToUIColor(), UIControlState.Normal);
|
||||
}
|
||||
|
||||
if (!maxColor.IsDefault)
|
||||
|
@ -157,12 +161,6 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
|
||||
if (!thumbColor.IsDefault)
|
||||
Control.SetThumbColor(thumbColor.ToUIColor(), UIControlState.Normal);
|
||||
|
||||
UIColor MatchAlpha(Color color, UIColor alphaColor)
|
||||
{
|
||||
alphaColor.GetRGBA(out _, out _, out _, out var a);
|
||||
return color.ToUIColor().ColorWithAlpha(a);
|
||||
}
|
||||
}
|
||||
|
||||
void OnControlValueChanged(object sender, EventArgs eventArgs)
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
#if __ANDROID__
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using AColor = Android.Graphics.Color;
|
||||
using AProgressBar = Android.Widget.ProgressBar;
|
||||
using ASeekBar = Android.Widget.AbsSeekBar;
|
||||
#else
|
||||
using MaterialComponents;
|
||||
using AColor = UIKit.UIColor;
|
||||
|
@ -19,6 +22,15 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
// https://github.com/material-components/material-components-android/blob/3637c23078afc909e42833fd1c5fd47bb3271b5f/lib/java/com/google/android/material/color/res/values/colors.xml
|
||||
internal static class MaterialColors
|
||||
{
|
||||
// https://github.com/material-components/material-components-ios/blob/v76.0.0/components/Slider/src/ColorThemer/MDCSliderColorThemer.m#L21
|
||||
const float kSliderBaselineDisabledFillAlpha = 0.32f;
|
||||
const float kSliderBaselineEnabledBackgroundAlpha = 0.24f;
|
||||
const float kSliderBaselineDisabledBackgroundAlpha = 0.12f;
|
||||
const float kSliderBaselineEnabledTicksAlpha = 0.54f;
|
||||
const float kSliderBaselineDisabledTicksAlpha = 0.12f;
|
||||
|
||||
public const float SliderTrackAlpha = kSliderBaselineEnabledBackgroundAlpha;
|
||||
|
||||
// values based on
|
||||
// copying to match iOS
|
||||
// TODO generalize into xplat classes
|
||||
|
@ -96,7 +108,7 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
};
|
||||
|
||||
public static readonly int[][] EntryUnderlineStates =
|
||||
{
|
||||
{
|
||||
new []{ global::Android.Resource.Attribute.StateFocused },
|
||||
new []{ -global::Android.Resource.Attribute.StateFocused },
|
||||
};
|
||||
|
@ -137,6 +149,71 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
|
||||
internal static AColor WithAlpha(this AColor color, double alpha) =>
|
||||
new AColor(color.R, color.G, color.B, (byte)(alpha * 255));
|
||||
|
||||
internal static void ApplySeekBarColors(this ASeekBar seekBar, Color progressColor, Color backgroundColor, Color thumbColor)
|
||||
{
|
||||
seekBar.ApplyProgressBarColors(progressColor, backgroundColor);
|
||||
|
||||
if (thumbColor.IsDefault)
|
||||
{
|
||||
// reset everything to defaults
|
||||
seekBar.ThumbTintList = seekBar.ProgressTintList;
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle the case where the thumb is set
|
||||
var thumb = thumbColor.ToAndroid();
|
||||
|
||||
seekBar.ThumbTintList = ColorStateList.ValueOf(thumb);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ApplyProgressBarColors(this AProgressBar progressBar, Color progressColor, Color backgroundColor)
|
||||
{
|
||||
AColor defaultProgress = Dark.PrimaryColor;
|
||||
|
||||
if (progressColor.IsDefault)
|
||||
{
|
||||
if (backgroundColor.IsDefault)
|
||||
{
|
||||
// reset everything to defaults
|
||||
progressBar.ProgressTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
progressBar.ProgressBackgroundTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
progressBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle the case where only the background is set
|
||||
var background = backgroundColor.ToAndroid();
|
||||
|
||||
progressBar.ProgressTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
progressBar.ProgressBackgroundTintList = ColorStateList.ValueOf(background);
|
||||
progressBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcOver;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (backgroundColor.IsDefault)
|
||||
{
|
||||
// handle the case where only the progress is set
|
||||
var progress = progressColor.ToAndroid();
|
||||
|
||||
progressBar.ProgressTintList = ColorStateList.ValueOf(progress);
|
||||
progressBar.ProgressBackgroundTintList = ColorStateList.ValueOf(progress);
|
||||
progressBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle the case where both are set
|
||||
var background = backgroundColor.ToAndroid();
|
||||
var progress = progressColor.ToAndroid();
|
||||
|
||||
progressBar.ProgressTintList = ColorStateList.ValueOf(progress);
|
||||
progressBar.ProgressBackgroundTintList = ColorStateList.ValueOf(background);
|
||||
progressBar.ProgressBackgroundTintMode = PorterDuff.Mode.SrcOver;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -232,6 +309,15 @@ namespace Xamarin.Forms.Platform.iOS.Material
|
|||
|
||||
|
||||
|
||||
static AColor WithMultipliedAlpha(AColor color, float alpha)
|
||||
{
|
||||
#if __ANDROID__
|
||||
return color.WithAlpha(color.A / 255f * alpha);
|
||||
#else
|
||||
return color.ColorWithAlpha(color.CGColor.Alpha / 255f * alpha);
|
||||
#endif
|
||||
}
|
||||
|
||||
static AColor WithAlpha(AColor color, float alpha)
|
||||
{
|
||||
#if __ANDROID__
|
||||
|
|
|
@ -2,18 +2,15 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using Android.Content;
|
||||
using Android.Content.Res;
|
||||
using Android.Graphics;
|
||||
using Android.Support.V4.View;
|
||||
using Android.Views;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android.FastRenderers;
|
||||
using Xamarin.Forms.Platform.Android.Material;
|
||||
using AColor = Android.Graphics.Color;
|
||||
using AProgressBar = Android.Widget.ProgressBar;
|
||||
using AView = Android.Views.View;
|
||||
|
||||
[assembly: ExportRenderer(typeof(Xamarin.Forms.ProgressBar), typeof(MaterialProgressBarRenderer), new[] { typeof(VisualRendererMarker.Material) })]
|
||||
[assembly: ExportRenderer(typeof(ProgressBar), typeof(MaterialProgressBarRenderer), new[] { typeof(VisualRendererMarker.Material) })]
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android.Material
|
||||
{
|
||||
|
@ -122,7 +119,6 @@ namespace Xamarin.Forms.Platform.Android.Material
|
|||
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
ElementPropertyChanged?.Invoke(this, e);
|
||||
|
||||
if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
|
||||
UpdateProgress();
|
||||
else if (e.PropertyName == ProgressBar.ProgressColorProperty.PropertyName || e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
|
||||
|
@ -142,51 +138,7 @@ namespace Xamarin.Forms.Platform.Android.Material
|
|||
if (Element == null || Control == null)
|
||||
return;
|
||||
|
||||
Color progressColor = Element.ProgressColor;
|
||||
Color backgroundColor = Element.BackgroundColor;
|
||||
|
||||
var defaultProgress = MaterialColors.Light.PrimaryColor;
|
||||
|
||||
if (progressColor.IsDefault && backgroundColor.IsDefault)
|
||||
{
|
||||
// reset everything to defaults
|
||||
ProgressTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
ProgressBackgroundTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
|
||||
}
|
||||
else if (progressColor.IsDefault && !backgroundColor.IsDefault)
|
||||
{
|
||||
// handle the case where only the background is set
|
||||
var background = backgroundColor.ToAndroid();
|
||||
|
||||
// TODO: Potentially override primary color to match material design.
|
||||
ProgressTintList = ColorStateList.ValueOf(defaultProgress);
|
||||
ProgressBackgroundTintList = ColorStateList.ValueOf(background);
|
||||
|
||||
// TODO: Potentially override background alpha to match material design.
|
||||
ProgressBackgroundTintMode = PorterDuff.Mode.SrcOver;
|
||||
}
|
||||
else if (!progressColor.IsDefault && backgroundColor.IsDefault)
|
||||
{
|
||||
// handle the case where only the progress is set
|
||||
var progress = progressColor.ToAndroid();
|
||||
|
||||
ProgressTintList = ColorStateList.ValueOf(progress);
|
||||
ProgressBackgroundTintList = ColorStateList.ValueOf(progress);
|
||||
ProgressBackgroundTintMode = PorterDuff.Mode.SrcIn;
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle the case where both are set
|
||||
var background = backgroundColor.ToAndroid();
|
||||
var progress = progressColor.ToAndroid();
|
||||
|
||||
ProgressTintList = ColorStateList.ValueOf(progress);
|
||||
ProgressBackgroundTintList = ColorStateList.ValueOf(background);
|
||||
|
||||
// TODO: Potentially override alpha to match material design.
|
||||
ProgressBackgroundTintMode = PorterDuff.Mode.SrcOver;
|
||||
}
|
||||
this.ApplyProgressBarColors(Element.ProgressColor, Element.BackgroundColor);
|
||||
}
|
||||
|
||||
void UpdateProgress()
|
||||
|
@ -207,7 +159,7 @@ namespace Xamarin.Forms.Platform.Android.Material
|
|||
SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
|
||||
{
|
||||
Measure(widthConstraint, heightConstraint);
|
||||
return new SizeRequest(new Size(Control.MeasuredWidth, Control.MeasuredHeight), new Size());
|
||||
return new SizeRequest(new Size(Control.MeasuredWidth, Context.ToPixels(4)), new Size(Context.ToPixels(4), Context.ToPixels(4)));
|
||||
}
|
||||
|
||||
void IVisualElementRenderer.SetElement(VisualElement element) =>
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
#if __ANDROID_28__
|
||||
using System;
|
||||
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.FastRenderers;
|
||||
using Xamarin.Forms.Platform.Android.Material;
|
||||
using AView = Android.Views.View;
|
||||
|
||||
[assembly: ExportRenderer(typeof(Xamarin.Forms.Slider), typeof(MaterialSliderRenderer), new[] { typeof(VisualRendererMarker.Material) })]
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android.Material
|
||||
{
|
||||
public class MaterialSliderRenderer : SeekBar,
|
||||
SeekBar.IOnSeekBarChangeListener,
|
||||
IVisualElementRenderer, IViewRenderer, ITabStop
|
||||
{
|
||||
const double MaximumValue = 10000.0;
|
||||
|
||||
int? _defaultLabelFor;
|
||||
|
||||
bool _disposed;
|
||||
|
||||
Slider _element;
|
||||
|
||||
VisualElementTracker _visualElementTracker;
|
||||
VisualElementRenderer _visualElementRenderer;
|
||||
MotionEventHelper _motionEventHelper;
|
||||
|
||||
double _max = 0.0;
|
||||
double _min = 0.0;
|
||||
|
||||
public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
|
||||
public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
|
||||
|
||||
public MaterialSliderRenderer(Context context)
|
||||
: base(new ContextThemeWrapper(context, Resource.Style.XamarinFormsMaterialSlider), null, Resource.Style.XamarinFormsMaterialSlider)
|
||||
{
|
||||
VisualElement.VerifyVisualFlagEnabled();
|
||||
|
||||
SetOnSeekBarChangeListener(this);
|
||||
Max = (int)MaximumValue;
|
||||
|
||||
_visualElementRenderer = new VisualElementRenderer(this);
|
||||
_motionEventHelper = new MotionEventHelper();
|
||||
}
|
||||
|
||||
protected SeekBar Control => this;
|
||||
|
||||
protected Slider Element
|
||||
{
|
||||
get { return _element; }
|
||||
set
|
||||
{
|
||||
if (_element == value)
|
||||
return;
|
||||
|
||||
var oldElement = _element;
|
||||
_element = value;
|
||||
|
||||
OnElementChanged(new ElementChangedEventArgs<Slider>(oldElement, _element));
|
||||
|
||||
_element?.SendViewInitialized(this);
|
||||
|
||||
_motionEventHelper.UpdateElement(_element);
|
||||
}
|
||||
}
|
||||
|
||||
double Value
|
||||
{
|
||||
get { return _min + (_max - _min) * (Control.Progress / MaximumValue); }
|
||||
set { Control.Progress = (int)((value - _min) / (_max - _min) * MaximumValue); }
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_visualElementTracker?.Dispose();
|
||||
_visualElementTracker = null;
|
||||
|
||||
_visualElementRenderer?.Dispose();
|
||||
_visualElementRenderer = null;
|
||||
|
||||
if (Element != null)
|
||||
{
|
||||
Element.PropertyChanged -= OnElementPropertyChanged;
|
||||
|
||||
if (Platform.GetRenderer(Element) == this)
|
||||
Element.ClearValue(Platform.RendererProperty);
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected virtual void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
||||
{
|
||||
ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
|
||||
|
||||
if (e.OldElement != null)
|
||||
{
|
||||
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
|
||||
}
|
||||
|
||||
if (e.NewElement != null)
|
||||
{
|
||||
this.EnsureId();
|
||||
|
||||
if (_visualElementTracker == null)
|
||||
_visualElementTracker = new VisualElementTracker(this);
|
||||
|
||||
e.NewElement.PropertyChanged += OnElementPropertyChanged;
|
||||
|
||||
UpdateValue();
|
||||
UpdateColors();
|
||||
|
||||
ElevationHelper.SetElevation(this, e.NewElement);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
ElementPropertyChanged?.Invoke(this, e);
|
||||
|
||||
if (e.IsOneOf(Slider.ValueProperty, Slider.MinimumProperty, Slider.MaximumProperty))
|
||||
UpdateValue();
|
||||
else if (e.IsOneOf(VisualElement.BackgroundColorProperty, Slider.MaximumTrackColorProperty, Slider.MinimumTrackColorProperty, Slider.ThumbColorProperty))
|
||||
UpdateColors();
|
||||
}
|
||||
|
||||
public override bool OnTouchEvent(MotionEvent e)
|
||||
{
|
||||
if (_visualElementRenderer.OnTouchEvent(e) || base.OnTouchEvent(e))
|
||||
return true;
|
||||
|
||||
return _motionEventHelper.HandleMotionEvent(Parent, e);
|
||||
}
|
||||
|
||||
void UpdateColors()
|
||||
{
|
||||
if (Element == null || Control == null)
|
||||
return;
|
||||
|
||||
Color backgroundColor = Element.MaximumTrackColor;
|
||||
if (backgroundColor == Color.Default)
|
||||
backgroundColor = Element.BackgroundColor;
|
||||
Color progressColor = Element.MinimumTrackColor;
|
||||
Color thumbColor = Element.ThumbColor;
|
||||
|
||||
this.ApplySeekBarColors(progressColor, backgroundColor, thumbColor);
|
||||
}
|
||||
|
||||
void UpdateValue()
|
||||
{
|
||||
_min = Element.Minimum;
|
||||
_max = Element.Maximum;
|
||||
Value = Element.Value;
|
||||
}
|
||||
|
||||
// SeekBar.IOnSeekBarChangeListener
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnProgressChanged(SeekBar seekBar, int progress, bool fromUser)
|
||||
{
|
||||
if (fromUser)
|
||||
((IElementController)Element).SetValueFromRenderer(Slider.ValueProperty, Value);
|
||||
}
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnStartTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
((ISliderController)Element)?.SendDragStarted();
|
||||
}
|
||||
|
||||
void SeekBar.IOnSeekBarChangeListener.OnStopTrackingTouch(SeekBar seekBar)
|
||||
{
|
||||
((ISliderController)Element)?.SendDragCompleted();
|
||||
}
|
||||
|
||||
// IVisualElementRenderer
|
||||
|
||||
VisualElement IVisualElementRenderer.Element => Element;
|
||||
|
||||
VisualElementTracker IVisualElementRenderer.Tracker => _visualElementTracker;
|
||||
|
||||
ViewGroup IVisualElementRenderer.ViewGroup => null;
|
||||
|
||||
AView IVisualElementRenderer.View => this;
|
||||
|
||||
SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
|
||||
{
|
||||
Measure(widthConstraint, heightConstraint);
|
||||
return new SizeRequest(new Size(Control.MeasuredWidth, Control.MeasuredHeight), new Size());
|
||||
}
|
||||
|
||||
void IVisualElementRenderer.SetElement(VisualElement element) =>
|
||||
Element = (element as Slider) ?? throw new ArgumentException($"Element must be of type {nameof(Slider)}.");
|
||||
|
||||
void IVisualElementRenderer.SetLabelFor(int? id)
|
||||
{
|
||||
if (_defaultLabelFor == null)
|
||||
_defaultLabelFor = ViewCompat.GetLabelFor(this);
|
||||
|
||||
ViewCompat.SetLabelFor(this, (int)(id ?? _defaultLabelFor));
|
||||
}
|
||||
|
||||
void IVisualElementRenderer.UpdateLayout() =>
|
||||
_visualElementTracker?.UpdateLayout();
|
||||
|
||||
// IViewRenderer
|
||||
|
||||
void IViewRenderer.MeasureExactly() =>
|
||||
ViewRenderer.MeasureExactly(this, Element, Context);
|
||||
|
||||
// ITabStop
|
||||
|
||||
AView ITabStop.TabStop => this;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/white"
|
||||
android:alpha="?android:attr/disabledAlpha" />
|
||||
</selector>
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
From original drawable https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/res/drawable/progress_horizontal_material.xml
|
||||
with removed rounded corners. And, following some of the drawable/color links.
|
||||
-->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background"
|
||||
android:gravity="center_vertical|fill_horizontal">
|
||||
<shape android:shape="rectangle"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<corners android:radius="0dp" />
|
||||
<solid android:color="@color/white_disabled_material" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/secondaryProgress"
|
||||
android:gravity="center_vertical|fill_horizontal">
|
||||
<scale android:scaleWidth="100%">
|
||||
<shape android:shape="rectangle"
|
||||
android:tint="?android:attr/colorControlActivated">
|
||||
<corners android:radius="0dp" />
|
||||
<solid android:color="@color/white_disabled_material" />
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
<item android:id="@android:id/progress"
|
||||
android:gravity="center_vertical|fill_horizontal">
|
||||
<scale android:scaleWidth="100%">
|
||||
<shape android:shape="rectangle"
|
||||
android:tint="?android:attr/colorControlActivated">
|
||||
<corners android:radius="0dp" />
|
||||
<solid android:color="@android:color/white" />
|
||||
</shape>
|
||||
</scale>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -6,12 +6,19 @@
|
|||
<item name="materialButtonStyle">@style/XamarinFormsMaterialButton</item>
|
||||
</style>
|
||||
|
||||
<!-- Material Sliders -->
|
||||
<style name="XamarinFormsMaterialSlider" parent="Widget.AppCompat.SeekBar">
|
||||
</style>
|
||||
|
||||
<!-- Material Progress Bars -->
|
||||
<style name="XamarinFormsMaterialProgressBarHorizontal" parent="Widget.AppCompat.ProgressBar.Horizontal">
|
||||
<item name="android:indeterminateOnly">false</item>
|
||||
<item name="android:progressDrawable">@drawable/MaterialProgressBar</item>
|
||||
</style>
|
||||
<style name="XamarinFormsMaterialProgressBarCircular" parent="Widget.AppCompat.ProgressBar">
|
||||
<item name="android:background">@drawable/MaterialActivityIndicatorBackground</item>
|
||||
</style>
|
||||
|
||||
<!-- Material Buttons (All Styles) -->
|
||||
<style name="XamarinFormsMaterialButton" parent="Widget.MaterialComponents.Button">
|
||||
<item name="android:insetTop">0dp</item>
|
||||
|
@ -23,4 +30,5 @@
|
|||
<style name="XamarinFormsMaterialEntryFilled" parent="Widget.MaterialComponents.TextInputLayout.FilledBox">
|
||||
<item name="boxCollapsedPaddingTop">8dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -315,6 +315,8 @@
|
|||
<Compile Include="ButtonElementManager.cs" />
|
||||
<Compile Include="IButtonLayoutRenderer.cs" />
|
||||
<Compile Include="ButtonLayoutManager.cs" />
|
||||
<Compile Include="Material\MaterialSliderRenderer.cs" />
|
||||
<AndroidResource Include="Resources\color\white_disabled_material.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@ -403,6 +405,14 @@
|
|||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\MaterialProgressBar.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\color\" />
|
||||
</ItemGroup>
|
||||
<Target Name="BeforeBuild" Condition=" '$(CreateAllAndroidTargets)' == 'true' ">
|
||||
<!-- create 8.1 binaries-->
|
||||
<MSBuild Targets="Restore" Projects="@(ProjectToBuild)">
|
||||
|
|
Загрузка…
Ссылка в новой задаче