diff --git a/Billing.Shared/Billing.Shared.projitems b/Billing.Shared/Billing.Shared.projitems
index 7d6eba9..5566fd7 100644
--- a/Billing.Shared/Billing.Shared.projitems
+++ b/Billing.Shared/Billing.Shared.projitems
@@ -77,6 +77,7 @@
+
diff --git a/Billing.Shared/UI/CustomControl.cs b/Billing.Shared/UI/CustomControl.cs
index d32550f..17baaab 100644
--- a/Billing.Shared/UI/CustomControl.cs
+++ b/Billing.Shared/UI/CustomControl.cs
@@ -62,4 +62,6 @@ namespace Billing.UI
}
}
}
+
+ public class BlurryPanel : ContentView { }
}
\ No newline at end of file
diff --git a/Billing.Shared/UI/SegmentedControl.cs b/Billing.Shared/UI/SegmentedControl.cs
new file mode 100644
index 0000000..b4e310d
--- /dev/null
+++ b/Billing.Shared/UI/SegmentedControl.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms;
+
+namespace Billing.UI
+{
+ public class SegmentedControl : View, IViewContainer
+ {
+ public IList Children { get; set; }
+
+ public SegmentedControl()
+ {
+ Children = new List();
+ }
+
+ public static readonly BindableProperty TintColorProperty = Helper.Create(nameof(TintColor));
+ public static readonly BindableProperty DisabledColorProperty = Helper.Create(nameof(DisabledColor));
+ public static readonly BindableProperty SelectedTextColorProperty = Helper.Create(nameof(SelectedTextColor));
+ public static readonly BindableProperty SelectedSegmentIndexProperty = Helper.Create(nameof(SelectedSegmentIndex));
+
+ public Color TintColor
+ {
+ get => (Color)GetValue(TintColorProperty);
+ set => SetValue(TintColorProperty, value);
+ }
+ public Color DisabledColor
+ {
+ get => (Color)GetValue(DisabledColorProperty);
+ set => SetValue(DisabledColorProperty, value);
+ }
+ public Color SelectedTextColor
+ {
+ get => (Color)GetValue(SelectedTextColorProperty);
+ set => SetValue(SelectedTextColorProperty, value);
+ }
+ public int SelectedSegmentIndex
+ {
+ get => (int)GetValue(SelectedSegmentIndexProperty);
+ set => SetValue(SelectedSegmentIndexProperty, value);
+ }
+
+ public SegmentedControlOption SelectedSegment => Children[SelectedSegmentIndex];
+
+ public event EventHandler ValueChanged;
+
+ //[EditorBrowsable(EditorBrowsableState.Never)]
+ public void SendValueChanged()
+ {
+ ValueChanged?.Invoke(this, new ValueChangedEventArgs { NewValue = SelectedSegmentIndex });
+ }
+ }
+
+ public class SegmentedControlOption : View
+ {
+ public static readonly BindableProperty TextProperty = Helper.Create(nameof(Text));
+
+ public string Text
+ {
+ get => (string)GetValue(TextProperty);
+ set => SetValue(TextProperty, value);
+ }
+
+ public object Value { get; set; }
+ }
+
+ public class ValueChangedEventArgs : EventArgs
+ {
+ public int NewValue { get; set; }
+ }
+}
diff --git a/Billing.Shared/Views/RankPage.xaml b/Billing.Shared/Views/RankPage.xaml
index 4806653..ebf656b 100644
--- a/Billing.Shared/Views/RankPage.xaml
+++ b/Billing.Shared/Views/RankPage.xaml
@@ -41,6 +41,7 @@
+
@@ -59,52 +60,89 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Billing.Shared/Views/RankPage.xaml.cs b/Billing.Shared/Views/RankPage.xaml.cs
index 20fba3a..edd6bca 100644
--- a/Billing.Shared/Views/RankPage.xaml.cs
+++ b/Billing.Shared/Views/RankPage.xaml.cs
@@ -16,13 +16,33 @@ namespace Billing.Views
{
public partial class RankPage : BillingPage
{
+ private static readonly BindableProperty SegmentTypeProperty = Helper.Create(nameof(SegmentType), defaultValue: 0, propertyChanged: OnSegmentTypeChanged);
private static readonly BindableProperty ChartProperty = Helper.Create(nameof(Chart));
private static readonly BindableProperty CategoryChartProperty = Helper.Create(nameof(CategoryChart));
private static readonly BindableProperty TopBillsProperty = Helper.Create, RankPage>(nameof(TopBills));
private static readonly BindableProperty NoResultChartProperty = Helper.Create(nameof(NoResultChart));
private static readonly BindableProperty NoResultCategoryChartProperty = Helper.Create(nameof(NoResultCategoryChart));
private static readonly BindableProperty NoResultTopBillsProperty = Helper.Create(nameof(NoResultTopBills));
+ private static readonly BindableProperty IncomeProperty = Helper.Create(nameof(Income));
+ private static readonly BindableProperty SpendingProperty = Helper.Create(nameof(Spending));
+ private static readonly BindableProperty BalanceProperty = Helper.Create(nameof(Balance));
+ private static void OnSegmentTypeChanged(RankPage page, int old, int @new)
+ {
+ page.type = @new switch
+ {
+ 1 => CategoryType.Income,
+ _ => CategoryType.Spending
+ };
+ page.OnFilterCommand(false);
+ page.SetMonth(page.current);
+ }
+
+ public int SegmentType
+ {
+ get => (int)GetValue(SegmentTypeProperty);
+ set => SetValue(SegmentTypeProperty, value);
+ }
public Chart Chart
{
get => (Chart)GetValue(ChartProperty);
@@ -53,6 +73,9 @@ namespace Billing.Views
get => (bool)GetValue(NoResultTopBillsProperty);
set => SetValue(NoResultTopBillsProperty, value);
}
+ public decimal Income => (decimal)GetValue(IncomeProperty);
+ public decimal Spending => (decimal)GetValue(SpendingProperty);
+ public decimal Balance => (decimal)GetValue(BalanceProperty);
public Command LeftCommand { get; }
public Command RightCommand { get; }
@@ -63,6 +86,7 @@ namespace Billing.Views
private DateTime end;
private IEnumerable bills;
private CategoryType type = CategoryType.Spending;
+ private bool isFilterToggled;
private readonly SKTypeface font;
@@ -70,6 +94,7 @@ namespace Billing.Views
{
LeftCommand = new Command(OnLeftCommand);
RightCommand = new Command(OnRightCommand);
+ FilterCommand = new Command(OnFilterCommand);
EditBilling = new Command(OnEditBilling);
var style = SKFontManager.Default.GetFontStyles("PingFang SC");
@@ -79,6 +104,9 @@ namespace Billing.Views
}
InitializeComponent();
+
+ gridFilter.TranslationY = -60;
+ panelFilter.TranslationY = -60;
}
public override void OnLoaded()
@@ -104,6 +132,36 @@ namespace Billing.Views
SetMonth(current.AddMonths(1));
}
+ private async void OnFilterCommand(object o)
+ {
+ if (o is bool flag)
+ {
+ isFilterToggled = flag;
+ }
+ else
+ {
+ isFilterToggled = !isFilterToggled;
+ }
+ ViewExtensions.CancelAnimations(gridFilter);
+ ViewExtensions.CancelAnimations(panelFilter);
+ if (isFilterToggled)
+ {
+ await Task.WhenAll(
+ gridFilter.TranslateTo(0, 0, easing: Easing.CubicOut),
+ gridFilter.FadeTo(1, easing: Easing.CubicOut),
+ panelFilter.TranslateTo(0, 0, easing: Easing.CubicOut),
+ panelFilter.FadeTo(1, easing: Easing.CubicOut));
+ }
+ else
+ {
+ await Task.WhenAll(
+ gridFilter.TranslateTo(0, -60, easing: Easing.CubicIn),
+ gridFilter.FadeTo(0, easing: Easing.CubicIn),
+ panelFilter.TranslateTo(0, -60, easing: Easing.CubicIn),
+ panelFilter.FadeTo(0, easing: Easing.CubicIn));
+ }
+ }
+
private async void OnEditBilling(object o)
{
if (Tap.IsBusy)
@@ -130,6 +188,16 @@ namespace Billing.Views
bill.Wallet = App.Accounts.FirstOrDefault(a => a.Id == bill.Bill.WalletId)?.Name;
}
+ private async void RefreshBalance()
+ {
+ var bills = await Task.Run(() => App.Bills.Where(b => b.CreateTime >= current && b.CreateTime <= end));
+ var income = bills.Where(b => b.Amount > 0).Sum(b => b.Amount);
+ var spending = -bills.Where(b => b.Amount < 0).Sum(b => b.Amount);
+ SetValue(IncomeProperty, income);
+ SetValue(SpendingProperty, spending);
+ SetValue(BalanceProperty, income - spending);
+ }
+
private void OnBillChecked(object sender, Bill e)
{
var bill = TopBills.FirstOrDefault(b => b.Bill == e);
@@ -137,6 +205,7 @@ namespace Billing.Views
{
UpdateBill(bill);
}
+ RefreshBalance();
Task.Run(App.WriteBills);
}
@@ -158,6 +227,8 @@ namespace Billing.Views
_ = Task.Run(() => LoadReportChart(primaryColor, textColor));
_ = Task.Run(() => LoadCategoryChart(primaryColor, textColor));
_ = Task.Run(LoadTopBills);
+
+ RefreshBalance();
}
private void LoadReportChart(SKColor primaryColor, SKColor textColor)
diff --git a/Billing/Billing.Android/Billing.Android.csproj b/Billing/Billing.Android/Billing.Android.csproj
index c30730e..70a2386 100644
--- a/Billing/Billing.Android/Billing.Android.csproj
+++ b/Billing/Billing.Android/Billing.Android.csproj
@@ -89,6 +89,7 @@
+
diff --git a/Billing/Billing.Android/Properties/AndroidManifest.xml b/Billing/Billing.Android/Properties/AndroidManifest.xml
index 8c5af44..59f68b9 100644
--- a/Billing/Billing.Android/Properties/AndroidManifest.xml
+++ b/Billing/Billing.Android/Properties/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/Billing/Billing.Android/Renderers/BlurryPanelRenderer.cs b/Billing/Billing.Android/Renderers/BlurryPanelRenderer.cs
new file mode 100644
index 0000000..259da23
--- /dev/null
+++ b/Billing/Billing.Android/Renderers/BlurryPanelRenderer.cs
@@ -0,0 +1,30 @@
+using Android.Content;
+using Billing.Droid.Renderers;
+using Billing.UI;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.Android;
+
+[assembly: ExportRenderer(typeof(BlurryPanel), typeof(BlurryPanelRenderer))]
+namespace Billing.Droid.Renderers
+{
+ public class BlurryPanelRenderer : ViewRenderer
+ {
+ public BlurryPanelRenderer(Context context) : base(context)
+ {
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ var color = e.NewElement.BackgroundColor;
+ if (!color.IsDefault)
+ {
+ SetBackgroundColor(color.MultiplyAlpha(.94).ToAndroid());
+ }
+ }
+ }
+ }
+}
diff --git a/Billing/Billing.iOS/Billing.iOS.csproj b/Billing/Billing.iOS/Billing.iOS.csproj
index 16d4a37..a65f362 100644
--- a/Billing/Billing.iOS/Billing.iOS.csproj
+++ b/Billing/Billing.iOS/Billing.iOS.csproj
@@ -89,6 +89,8 @@
+
+
diff --git a/Billing/Billing.iOS/Info.plist b/Billing/Billing.iOS/Info.plist
index 684fd37..e2f0733 100644
--- a/Billing/Billing.iOS/Info.plist
+++ b/Billing/Billing.iOS/Info.plist
@@ -44,8 +44,8 @@
UIFileSharingEnabled
CFBundleVersion
- 7
+ 8
CFBundleShortVersionString
- 0.7.308
+ 0.8.309
diff --git a/Billing/Billing.iOS/Renderers/BlurryPanelRenderer.cs b/Billing/Billing.iOS/Renderers/BlurryPanelRenderer.cs
new file mode 100644
index 0000000..5e5d7a9
--- /dev/null
+++ b/Billing/Billing.iOS/Renderers/BlurryPanelRenderer.cs
@@ -0,0 +1,87 @@
+using Billing.iOS.Renderers;
+using Billing.UI;
+using CoreAnimation;
+using UIKit;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.iOS;
+
+[assembly: ExportRenderer(typeof(BlurryPanel), typeof(BlurryPanelRenderer))]
+namespace Billing.iOS.Renderers
+{
+ public class BlurryPanelRenderer : ViewRenderer
+ {
+ private UIVisualEffectView nativeControl;
+ private CALayer bottom;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ if (bottom != null)
+ {
+ if (bottom.SuperLayer != null)
+ {
+ bottom.RemoveFromSuperLayer();
+ }
+ bottom.Dispose();
+ bottom = null;
+ }
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.BackgroundColor = Color.Default;
+ if (Control == null)
+ {
+ var blur = UIBlurEffect.FromStyle(UIBlurEffectStyle.SystemMaterial);
+ nativeControl = new UIVisualEffectView(blur)
+ {
+ Frame = Frame
+ };
+ SetNativeControl(nativeControl);
+ }
+ }
+ }
+
+ public override void LayoutSubviews()
+ {
+ base.LayoutSubviews();
+
+ if (nativeControl != null)
+ {
+ if (bottom == null)
+ {
+ bottom = new CALayer
+ {
+ BackgroundColor = UIColor.White.CGColor,
+ ShadowColor = UIColor.Black.CGColor,
+ ShadowOpacity = 1.0f
+ };
+ }
+ if (bottom.SuperLayer == null)
+ {
+ nativeControl.Layer.InsertSublayer(bottom, 0);
+ }
+ bottom.Frame = new CoreGraphics.CGRect(0, Frame.Height - 5, Frame.Width, 5);
+ nativeControl.Frame = Frame;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (bottom != null)
+ {
+ if (bottom.SuperLayer != null)
+ {
+ bottom.RemoveFromSuperLayer();
+ }
+ bottom.Dispose();
+ bottom = null;
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/Billing/Billing.iOS/Renderers/SegmentedControlRenderer.cs b/Billing/Billing.iOS/Renderers/SegmentedControlRenderer.cs
new file mode 100644
index 0000000..6ff1208
--- /dev/null
+++ b/Billing/Billing.iOS/Renderers/SegmentedControlRenderer.cs
@@ -0,0 +1,153 @@
+using System;
+using System.ComponentModel;
+using Billing.iOS.Renderers;
+using Billing.UI;
+using UIKit;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.iOS;
+
+[assembly: ExportRenderer(typeof(SegmentedControl), typeof(SegmentedControlRenderer))]
+namespace Billing.iOS.Renderers
+{
+ public class SegmentedControlRenderer : ViewRenderer
+ {
+ private UISegmentedControl nativeControl;
+
+ protected override void OnElementChanged(ElementChangedEventArgs e)
+ {
+ base.OnElementChanged(e);
+
+ var element = Element;
+ if (Control == null && element != null)
+ {
+ nativeControl = new UISegmentedControl();
+
+ for (var i = 0; i < element.Children.Count; i++)
+ {
+ nativeControl.InsertSegment(element.Children[i].Text, i, false);
+ }
+
+ nativeControl.Enabled = element.IsEnabled;
+ //nativeControl.BackgroundColor = element.BackgroundColor.ToUIColor();
+ nativeControl.SelectedSegmentTintColor = GetTintColor(element);
+ SetTextColor();
+ nativeControl.SelectedSegment = element.SelectedSegmentIndex;
+
+ SetNativeControl(nativeControl);
+ }
+
+ if (e.OldElement != null)
+ {
+ if (nativeControl != null)
+ {
+ nativeControl.ValueChanged -= NativeControl_ValueChanged;
+ }
+ }
+
+ if (e.NewElement != null)
+ {
+ nativeControl.ValueChanged += NativeControl_ValueChanged;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ var element = Element;
+ if (nativeControl == null || element == null)
+ {
+ return;
+ }
+
+ switch (e.PropertyName)
+ {
+ case "Renderer":
+ element.SendValueChanged();
+ break;
+
+ //case nameof(element.BackgroundColor):
+ // nativeControl.BackgroundColor = element.BackgroundColor.ToUIColor();
+ // break;
+ case nameof(element.SelectedSegmentIndex):
+ nativeControl.SelectedSegment = element.SelectedSegmentIndex;
+ break;
+
+ case nameof(element.TintColor):
+ nativeControl.SelectedSegmentTintColor = GetTintColor(element);
+ break;
+
+ case nameof(element.IsEnabled):
+ nativeControl.Enabled = element.IsEnabled;
+ nativeControl.SelectedSegmentTintColor = GetTintColor(element);
+ break;
+
+ case nameof(element.SelectedTextColor):
+ SetTextColor();
+ break;
+ }
+ }
+
+ private void SetTextColor()
+ {
+ //var color = Element.SelectedTextColor;
+ //UIColor c = color == default ? UIColor.LabelColor : color.ToUIColor();
+ UIColor c = UIColor.LabelColor;
+ var attribute = new UITextAttributes
+ {
+ TextColor = c
+ };
+ nativeControl.SetTitleTextAttributes(attribute, UIControlState.Selected);
+ attribute = new UITextAttributes
+ {
+ TextColor = c.ColorWithAlpha(.6f)
+ };
+ nativeControl.SetTitleTextAttributes(attribute, UIControlState.Normal);
+ }
+
+ private UIColor GetTintColor(SegmentedControl element)
+ {
+ if (element.IsEnabled)
+ {
+ //var tintColor = element.TintColor;
+ //if (tintColor == default)
+ //{
+ return UIColor.SystemGray6Color;
+ //}
+ //else
+ //{
+ // return tintColor.ToUIColor().ColorWithAlpha(.3f);
+ //}
+ }
+ else
+ {
+ //var disabledColor = element.DisabledColor;
+ //if (disabledColor == default)
+ //{
+ return UIColor.SecondaryLabelColor;
+ //}
+ //else
+ //{
+ // return disabledColor.ToUIColor();
+ //}
+ }
+ }
+
+ private void NativeControl_ValueChanged(object sender, EventArgs e)
+ {
+ Element.SelectedSegmentIndex = (int)nativeControl.SelectedSegment;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (nativeControl != null)
+ {
+ nativeControl.ValueChanged -= NativeControl_ValueChanged;
+ nativeControl.Dispose();
+ nativeControl = null;
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}