diff --git a/Billing.Shared/Billing.Shared.projitems b/Billing.Shared/Billing.Shared.projitems
index 7572722..ddb5bab 100644
--- a/Billing.Shared/Billing.Shared.projitems
+++ b/Billing.Shared/Billing.Shared.projitems
@@ -51,6 +51,10 @@
BillPage.xaml
+
+ IconSelectPage.xaml
+ Code
+
SettingPage.xaml
@@ -86,4 +90,9 @@
MSBuild:UpdateDesignTimeXaml
+
+
+ MSBuild:UpdateDesignTimeXaml
+
+
\ No newline at end of file
diff --git a/Billing.Shared/Languages/en.xml b/Billing.Shared/Languages/en.xml
index 8d63e7d..bbabe70 100644
--- a/Billing.Shared/Languages/en.xml
+++ b/Billing.Shared/Languages/en.xml
@@ -31,4 +31,5 @@
Credit Card
Debit Card
Electronic Account
+ Icon Selection
\ No newline at end of file
diff --git a/Billing.Shared/Languages/zh-CN.xml b/Billing.Shared/Languages/zh-CN.xml
index f0ce9f2..5441ff3 100644
--- a/Billing.Shared/Languages/zh-CN.xml
+++ b/Billing.Shared/Languages/zh-CN.xml
@@ -31,4 +31,5 @@
信用卡
储蓄卡
电子账户
+ 图标选择
\ No newline at end of file
diff --git a/Billing.Shared/Themes/BaseTheme.cs b/Billing.Shared/Themes/BaseTheme.cs
index c82218b..f19568f 100644
--- a/Billing.Shared/Themes/BaseTheme.cs
+++ b/Billing.Shared/Themes/BaseTheme.cs
@@ -9,6 +9,7 @@ namespace Billing.Themes
public const string CascadiaFontBold = nameof(CascadiaFontBold);
public const string RobotoCondensedFontRegular = nameof(RobotoCondensedFontRegular);
public const string RobotoCondensedFontBold = nameof(RobotoCondensedFontBold);
+ public const string BrandsFontRegular = nameof(BrandsFontRegular);
public const string WindowBackgroundColor = nameof(WindowBackgroundColor);
public const string OptionTintColor = nameof(OptionTintColor);
@@ -33,6 +34,7 @@ namespace Billing.Themes
Add(CascadiaFontBold, Definition.GetCascadiaBoldFontFamily());
Add(RobotoCondensedFontRegular, Definition.GetRobotoCondensedRegularFontFamily());
Add(RobotoCondensedFontBold, Definition.GetRobotoCondensedBoldFontFamily());
+ Add(BrandsFontRegular, Definition.GetBrandsFontFamily());
Add(PrimaryColor, PrimaryMauiColor);
Add(SecondaryColor, SecondaryMauiColor);
diff --git a/Billing.Shared/UI/Converters.cs b/Billing.Shared/UI/Converters.cs
index 0712086..6202e8b 100644
--- a/Billing.Shared/UI/Converters.cs
+++ b/Billing.Shared/UI/Converters.cs
@@ -1,6 +1,7 @@
using Billing.Languages;
using Billing.Models;
using System;
+using System.Collections.Generic;
using System.Globalization;
using Xamarin.Forms;
@@ -95,4 +96,62 @@ namespace Billing.UI
return value;
}
}
+
+ public class IconConverter : IValueConverter
+ {
+ public static readonly Dictionary IconPreset = new()
+ {
+ { "alipay", "\uf642" },
+ { "appstore", "\uf370" },
+ { "apple-pay", "\uf416" },
+ { "btc", "\uf15a" },
+ { "jcb", "\uf24b" },
+ { "master-card", "\uf1f1" },
+ { "visa", "\uf1f0" },
+ { "g-pay", "\ue079" },
+ { "paypal", "\uf1ed" },
+ { "qq", "\uf1d6" },
+ { "steam", "\uf1b6" },
+ { "uber", "\uf402" },
+ { "weixin", "\uf1d7" },
+ { "youtube", "\uf167" }
+ };
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ImageSource source)
+ {
+ return source;
+ }
+ if (value is string name)
+ {
+ if (name.StartsWith("#brand#"))
+ {
+ var key = name[7..];
+ if (!IconPreset.TryGetValue(key, out var glyph))
+ {
+ if (!int.TryParse(key, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int i))
+ {
+ return ImageSource.FromFile(BaseModel.ICON_DEFAULT);
+ }
+ glyph = char.ConvertFromUtf32(i);
+ }
+ return new FontImageSource
+ {
+ FontFamily = Definition.GetBrandsFontFamily(),
+ Size = 20,
+ Glyph = glyph,
+ Color = Color.Black
+ };
+ }
+ return ImageSource.FromFile(name);
+ }
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value;
+ }
+ }
}
\ No newline at end of file
diff --git a/Billing.Shared/UI/Definition.cs b/Billing.Shared/UI/Definition.cs
index 29bc40a..2c5695b 100644
--- a/Billing.Shared/UI/Definition.cs
+++ b/Billing.Shared/UI/Definition.cs
@@ -9,6 +9,7 @@ namespace Billing.UI
public static partial string GetCascadiaBoldFontFamily();
public static partial string GetRobotoCondensedRegularFontFamily();
public static partial string GetRobotoCondensedBoldFontFamily();
+ public static partial string GetBrandsFontFamily();
}
public static class ExtensionHelper
diff --git a/Billing.Shared/UI/GroupStackLayout.cs b/Billing.Shared/UI/GroupStackLayout.cs
index 0ae2020..90e4eaf 100644
--- a/Billing.Shared/UI/GroupStackLayout.cs
+++ b/Billing.Shared/UI/GroupStackLayout.cs
@@ -12,6 +12,8 @@ namespace Billing.UI
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(GroupStackLayout));
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(GroupStackLayout), propertyChanged: OnItemsSourcePropertyChanged);
public static readonly BindableProperty SpacingProperty = BindableProperty.Create(nameof(Spacing), typeof(double), typeof(GroupStackLayout), defaultValue: 4d);
+ public static readonly BindableProperty RowHeightProperty = BindableProperty.Create(nameof(RowHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 32d);
+ public static readonly BindableProperty GroupHeightProperty = BindableProperty.Create(nameof(GroupHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 24d);
public DataTemplate GroupHeaderTemplate
{
@@ -33,6 +35,16 @@ namespace Billing.UI
get => (double)GetValue(SpacingProperty);
set => SetValue(SpacingProperty, value);
}
+ public double RowHeight
+ {
+ get => (double)GetValue(RowHeightProperty);
+ set => SetValue(RowHeightProperty, value);
+ }
+ public double GroupHeight
+ {
+ get => (double)GetValue(GroupHeightProperty);
+ set => SetValue(GroupHeightProperty, value);
+ }
private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new)
{
@@ -40,14 +52,14 @@ namespace Billing.UI
stack.lastWidth = -1;
if (@new == null)
{
- stack.cachedLayout.Clear();
+ //stack.cachedLayout.Clear();
stack.Children.Clear();
stack.InvalidateLayout();
}
else if (@new is IList list)
{
stack.freezed = true;
- stack.cachedLayout.Clear();
+ //stack.cachedLayout.Clear();
stack.Children.Clear();
var groupTemplate = stack.GroupHeaderTemplate;
var itemTemplate = stack.ItemTemplate;
@@ -91,7 +103,7 @@ namespace Billing.UI
}
}
- private readonly Dictionary cachedLayout = new();
+ //private readonly Dictionary cachedLayout = new();
private bool freezed;
private double lastWidth = -1;
@@ -114,27 +126,40 @@ namespace Billing.UI
return;
}
var spacing = Spacing;
- var lastHeight = 0.0;
+ var lastHeight = 0d;
+ var rowHeight = RowHeight;
+ var groupHeight = GroupHeight;
foreach (var item in Children)
{
- var measured = item.Measure(width, height, MeasureFlags.IncludeMargins);
- var rect = new Rectangle(
- 0, lastHeight, width,
- measured.Request.Height);
- if (cachedLayout.TryGetValue(item, out var v))
+ //var measured = item.Measure(width, height, MeasureFlags.IncludeMargins);
+ //var rect = new Rectangle(
+ // 0, lastHeight, width,
+ // measured.Request.Height);
+ //if (cachedLayout.TryGetValue(item, out var v))
+ //{
+ // if (v != rect)
+ // {
+ // cachedLayout[item] = rect;
+ // item.Layout(rect);
+ // }
+ //}
+ //else
+ //{
+ // cachedLayout.Add(item, rect);
+ // item.Layout(rect);
+ //}
+ double itemHeight;
+ if (item.BindingContext is IList)
{
- if (v != rect)
- {
- cachedLayout[item] = rect;
- item.Layout(rect);
- }
+ itemHeight = groupHeight;
}
else
{
- cachedLayout.Add(item, rect);
- item.Layout(rect);
+ itemHeight = rowHeight;
}
- lastHeight += rect.Height + spacing;
+ var rect = new Rectangle(0, lastHeight, width, itemHeight);
+ item.Layout(rect);
+ lastHeight += itemHeight + spacing;
}
}
@@ -146,11 +171,21 @@ namespace Billing.UI
}
lastWidth = widthConstraint;
var spacing = Spacing;
- var lastHeight = 0.0;
+ var lastHeight = 0d;
+ var rowHeight = RowHeight;
+ var groupHeight = GroupHeight;
foreach (var item in Children)
{
- var measured = item.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
- lastHeight += measured.Request.Height + spacing;
+ //var measured = item.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
+ //lastHeight += measured.Request.Height + spacing;
+ if (item.BindingContext is IList)
+ {
+ lastHeight += groupHeight + spacing;
+ }
+ else
+ {
+ lastHeight = rowHeight + spacing;
+ }
}
lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight));
return lastSizeRequest;
diff --git a/Billing.Shared/UI/ItemSelectPage.cs b/Billing.Shared/UI/ItemSelectPage.cs
index 52d86f3..32ba843 100644
--- a/Billing.Shared/UI/ItemSelectPage.cs
+++ b/Billing.Shared/UI/ItemSelectPage.cs
@@ -1,6 +1,5 @@
using Billing.Themes;
using System;
-using System.Collections;
using System.Collections.Generic;
using Xamarin.Forms;
@@ -24,7 +23,7 @@ namespace Billing.UI
Spacing = 10,
Children =
{
- new Image
+ new TintImage
{
WidthRequest = 22,
HeightRequest = 22,
diff --git a/Billing.Shared/UI/OptionsCells.cs b/Billing.Shared/UI/OptionsCells.cs
index 10a664c..b9f1a1a 100644
--- a/Billing.Shared/UI/OptionsCells.cs
+++ b/Billing.Shared/UI/OptionsCells.cs
@@ -203,8 +203,9 @@ namespace Billing.UI
HorizontalOptions = LayoutOptions.End,
Children =
{
- new Image
+ new TintImage
{
+ WidthRequest = 26,
HeightRequest = 20,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center,
diff --git a/Billing.Shared/UI/WrapLayout.cs b/Billing.Shared/UI/WrapLayout.cs
index 20ef995..fa51728 100644
--- a/Billing.Shared/UI/WrapLayout.cs
+++ b/Billing.Shared/UI/WrapLayout.cs
@@ -8,8 +8,8 @@ namespace Billing.UI
public class WrapLayout : Layout
{
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(WrapLayout), propertyChanged: OnItemsSourcePropertyChanged);
- public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(nameof(ColumnSpacing), typeof(double), typeof(WrapLayout), defaultValue: 5.0, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
- public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(nameof(RowSpacing), typeof(double), typeof(WrapLayout), defaultValue: 5.0, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
+ public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(nameof(ColumnSpacing), typeof(double), typeof(WrapLayout), defaultValue: 4d, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
+ public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(nameof(RowSpacing), typeof(double), typeof(WrapLayout), defaultValue: 4d, propertyChanged: (obj, _, _) => ((WrapLayout)obj).InvalidateLayout());
private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new)
{
@@ -177,14 +177,14 @@ namespace Billing.UI
{
cellSize.Width = (width - columnSpacing * (columns - 1)) / columns;
}
- if (double.IsPositiveInfinity(height))
+ //if (double.IsPositiveInfinity(height))
{
cellSize.Height = maxChildSize.Height;
}
- else
- {
- cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;
- }
+ //else
+ //{
+ // cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;
+ //}
layoutData = new LayoutData(visibleChildrenCount, cellSize, rows, columns);
}
else
diff --git a/Billing.Shared/Views/AccountPage.xaml b/Billing.Shared/Views/AccountPage.xaml
index 268e29a..c615b84 100644
--- a/Billing.Shared/Views/AccountPage.xaml
+++ b/Billing.Shared/Views/AccountPage.xaml
@@ -14,6 +14,7 @@
+
@@ -55,7 +56,8 @@
-
+
diff --git a/Billing.Shared/Views/AddAccountPage.xaml b/Billing.Shared/Views/AddAccountPage.xaml
index a55f2d6..b77cc25 100644
--- a/Billing.Shared/Views/AddAccountPage.xaml
+++ b/Billing.Shared/Views/AddAccountPage.xaml
@@ -13,6 +13,7 @@
+
@@ -28,7 +29,7 @@
Placeholder="{r:Text AccountNamePlaceholder}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Billing.Shared/Views/IconSelectPage.xaml.cs b/Billing.Shared/Views/IconSelectPage.xaml.cs
new file mode 100644
index 0000000..0a2440a
--- /dev/null
+++ b/Billing.Shared/Views/IconSelectPage.xaml.cs
@@ -0,0 +1,80 @@
+using Billing.Models;
+using Billing.UI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xamarin.Forms;
+
+namespace Billing.Views
+{
+ public partial class IconSelectPage : BillingPage
+ {
+ public static readonly BindableProperty IconsSourceProperty = BindableProperty.Create(nameof(IconsSource), typeof(IList), typeof(IconSelectPage));
+
+ public IList IconsSource
+ {
+ get => (IList)GetValue(IconsSourceProperty);
+ set => SetValue(IconsSourceProperty, value);
+ }
+
+ public Command IconCheck { get; }
+
+ public event EventHandler IconChecked;
+
+ private string iconChecked;
+
+ public IconSelectPage(string icon)
+ {
+ iconChecked = icon;
+ IconCheck = new Command(OnIconCheck);
+
+ InitializeComponent();
+
+ InitIcons();
+ }
+
+ private void InitIcons()
+ {
+ var source = new List
+ {
+ new() { Icon = BaseModel.ICON_DEFAULT },
+ new() { Icon = "wallet" }
+ };
+ source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" }));
+ foreach (var icon in source)
+ {
+ if (icon.Icon == iconChecked)
+ {
+ icon.IsChecked = true;
+ }
+ }
+ IconsSource = source;
+ }
+
+ private async void OnIconCheck(object o)
+ {
+ if (o is string icon)
+ {
+ foreach (var ic in IconsSource)
+ {
+ ic.IsChecked = ic.Icon == icon;
+ }
+ iconChecked = icon;
+ IconChecked?.Invoke(this, icon);
+ await Navigation.PopAsync();
+ }
+ }
+ }
+
+ public class BillingIcon : BindableObject
+ {
+ public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create(nameof(IsChecked), typeof(bool), typeof(BillingIcon));
+
+ public bool IsChecked
+ {
+ get => (bool)GetValue(IsCheckedProperty);
+ set => SetValue(IsCheckedProperty, value);
+ }
+ public string Icon { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Billing/Billing.Android/Assets/fa-brands-400.ttf b/Billing/Billing.Android/Assets/fa-brands-400.ttf
new file mode 100644
index 0000000..afee608
Binary files /dev/null and b/Billing/Billing.Android/Assets/fa-brands-400.ttf differ
diff --git a/Billing/Billing.Android/Billing.Android.csproj b/Billing/Billing.Android/Billing.Android.csproj
index 54c310c..3cd7920 100644
--- a/Billing/Billing.Android/Billing.Android.csproj
+++ b/Billing/Billing.Android/Billing.Android.csproj
@@ -86,6 +86,7 @@
+
diff --git a/Billing/Billing.Android/Definition.cs b/Billing/Billing.Android/Definition.cs
index 219fbe1..a8294bf 100644
--- a/Billing/Billing.Android/Definition.cs
+++ b/Billing/Billing.Android/Definition.cs
@@ -6,5 +6,6 @@
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold.ttf#CascadiaCode-Bold";
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular.ttf#RobotoCondensed-Regular";
public static partial string GetRobotoCondensedBoldFontFamily() => "RobotoCondensed-Bold.ttf#RobotoCondensed-Bold";
+ public static partial string GetBrandsFontFamily() => "fa-brands-400.ttf#FontAwesome6Brands-Regular";
}
}
\ No newline at end of file
diff --git a/Billing/Billing.Android/Renderers/TintImageRenderer.cs b/Billing/Billing.Android/Renderers/TintImageRenderer.cs
index 6f5f9e2..298472a 100644
--- a/Billing/Billing.Android/Renderers/TintImageRenderer.cs
+++ b/Billing/Billing.Android/Renderers/TintImageRenderer.cs
@@ -18,9 +18,13 @@ namespace Billing.Droid.Renderers
{
base.OnElementPropertyChanged(sender, e);
- if (e.PropertyName == nameof(TintImage.PrimaryColor) && Control?.Drawable != null && Element is TintImage image)
+ if (e.PropertyName == nameof(TintImage.PrimaryColor) ||
+ e.PropertyName == nameof(Image.Source))
{
- Control.Drawable.SetTint(image.PrimaryColor?.ToAndroid() ?? 0);
+ if (Control?.Drawable != null && Element is TintImage image)
+ {
+ Control.Drawable.SetTint(image.PrimaryColor?.ToAndroid() ?? 0);
+ }
}
}
diff --git a/Billing/Billing.iOS/Billing.iOS.csproj b/Billing/Billing.iOS/Billing.iOS.csproj
index 1236863..035fe62 100644
--- a/Billing/Billing.iOS/Billing.iOS.csproj
+++ b/Billing/Billing.iOS/Billing.iOS.csproj
@@ -79,6 +79,7 @@
+
diff --git a/Billing/Billing.iOS/Definition.cs b/Billing/Billing.iOS/Definition.cs
index e162438..d345ea9 100644
--- a/Billing/Billing.iOS/Definition.cs
+++ b/Billing/Billing.iOS/Definition.cs
@@ -6,5 +6,6 @@
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold";
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular";
public static partial string GetRobotoCondensedBoldFontFamily() => "RobotoCondensed-Bold";
+ public static partial string GetBrandsFontFamily() => "FontAwesome6Brands-Regular";
}
}
\ No newline at end of file
diff --git a/Billing/Billing.iOS/Info.plist b/Billing/Billing.iOS/Info.plist
index a61e781..2c2c75b 100644
--- a/Billing/Billing.iOS/Info.plist
+++ b/Billing/Billing.iOS/Info.plist
@@ -44,6 +44,7 @@
RobotoCondensed-Bold.ttf
CascadiaCode-Bold.ttf
CascadiaCode-Regular.ttf
+ fa-brands-400.ttf
UIFileSharingEnabled
diff --git a/Billing/Billing.iOS/Resources/fa-brands-400.ttf b/Billing/Billing.iOS/Resources/fa-brands-400.ttf
new file mode 100644
index 0000000..afee608
Binary files /dev/null and b/Billing/Billing.iOS/Resources/fa-brands-400.ttf differ