feature: segments control

This commit is contained in:
Tsanie Lily 2020-05-08 08:47:38 +08:00
parent cae3692140
commit e2ecabc224
11 changed files with 325 additions and 30 deletions

View File

@ -78,6 +78,7 @@
<Compile Include="Services\FileStore.cs" />
<Compile Include="Renderers\AppShellRenderer.cs" />
<Compile Include="Renderers\AppShellSection\AppShellSectionRootHeader.cs" />
<Compile Include="Renderers\SegmentedControlRenderer.cs" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />

View File

@ -35,7 +35,7 @@ namespace Pixiview.iOS.Renderers
if (renderer.ViewController is UITabBarController controller)
{
var tabBar = controller.TabBar;
tabBar.TintColor = UIColor.SecondaryLabelColor.ColorWithAlpha(1);
tabBar.TintColor = UIColor.LabelColor;
tabBar.UnselectedItemTintColor = UIColor.SecondaryLabelColor;
}
}

View File

@ -0,0 +1,155 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Pixiview.iOS.Renderers;
using Pixiview.UI;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(SegmentedControl), typeof(SegmentedControlRenderer))]
namespace Pixiview.iOS.Renderers
{
[SuppressMessage("Code Notifications", "XI0002:Notifies you from using newer Apple APIs when targeting an older OS version", Justification = "<Pending>")]
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, UISegmentedControl>
{
private UISegmentedControl nativeControl;
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> 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();
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.QuaternaryLabelColor;
}
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);
}
}
}

View File

@ -67,18 +67,9 @@
<ShellContent ContentTemplate="{DataTemplate i:MainPage}"/>
</Tab>
<Tab Icon="{DynamicResource FontIconSparkles}"
Title="{r:Text Recommends}">
<ShellContent Title="{r:Text Recommends}"
ContentTemplate="{DataTemplate i:RecommendsPage}"
Route="{x:Static util:Routes.Recommends}"/>
<ShellContent Title="{r:Text ByUser}"
Route="{x:Static util:Routes.ByUser}">
<ShellContent.ContentTemplate>
<DataTemplate>
<i:RecommendsPage ByUser="True"/>
</DataTemplate>
</ShellContent.ContentTemplate>
</ShellContent>
Title="{r:Text Recommends}"
Route="{x:Static util:Routes.Recommends}">
<ShellContent ContentTemplate="{DataTemplate i:RecommendsPage}"/>
</Tab>
<Tab Icon="{DynamicResource FontIconOrder}"
Title="{r:Text Ranking}"

View File

@ -67,6 +67,8 @@ namespace Pixiview.Illust
#endregion
protected Thickness totalBarOffset;
protected Thickness navigationBarOffset;
protected bool loaded;
private T illustData;
@ -297,7 +299,7 @@ namespace Pixiview.Illust
#region - Illust Tasks -
void DoLoadIllusts(bool force = false)
protected void DoLoadIllusts(bool force = false, bool skipImage = false)
{
illustData = DoLoadIllustData(force);
if (illustData == null)
@ -321,7 +323,10 @@ namespace Pixiview.Illust
Illusts = collection;
Loading = false;
DoLoadImages(collection);
if (!skipImage)
{
DoLoadImages(collection);
}
}
void DoLoadImages(IllustCollection collection)

View File

@ -23,8 +23,7 @@
CancelButtonColor="{DynamicResource SubTextColor}"
Text="{Binding Keywords, Mode=TwoWay}"
SearchButtonPressed="SearchBar_SearchButtonPressed"
Unfocused="SearchBar_Unfocused"
/>
Unfocused="SearchBar_Unfocused"/>
<Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8"
IsVisible="{Binding Loading}"
HorizontalOptions="Center" VerticalOptions="Center"

View File

@ -19,9 +19,6 @@ namespace Pixiview.Illust
set => SetValue(KeywordsProperty, value);
}
private readonly Thickness totalBarOffset;
private readonly Thickness navigationBarOffset;
public RankingPage()
{
totalBarOffset = new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0);

View File

@ -11,13 +11,26 @@
<ToolbarItem Order="Primary" Clicked="Refresh_Clicked"
IconImageSource="{DynamicResource FontIconRefresh}"/>
</ContentPage.ToolbarItems>
<Grid>
<Grid Padding="{Binding PageTopMargin}">
<ScrollView HorizontalOptions="Fill">
<u:FlowLayout ItemsSource="{Binding Illusts}"
HorizontalOptions="Fill" Column="{Binding Columns}"
Margin="16" RowSpacing="16" ColumnSpacing="16"
Margin="16, 6, 16, 16" RowSpacing="16" ColumnSpacing="16"
ItemTemplate="{StaticResource cardView}"/>
</ScrollView>
<Grid Margin="0, -40, 0, 0" VerticalOptions="Start" HeightRequest="40">
<u:SegmentedControl VerticalOptions="Center" HorizontalOptions="Center"
HeightRequest="30"
BackgroundColor="{DynamicResource WindowColor}"
TintColor="{DynamicResource SubTextColor}"
SelectedTextColor="{DynamicResource TextColor}"
SelectedSegmentIndex="{Binding SegmentIndex, Mode=TwoWay}">
<u:SegmentedControl.Children>
<u:SegmentedControlOption Text="{r:Text Recommends}"/>
<u:SegmentedControlOption Text="{r:Text ByUser}"/>
</u:SegmentedControl.Children>
</u:SegmentedControl>
</Grid>
<Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8"
IsVisible="{Binding Loading}"
HorizontalOptions="Center" VerticalOptions="Center"

View File

@ -2,16 +2,36 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Pixiview.UI;
using Pixiview.Utils;
using Xamarin.Forms;
namespace Pixiview.Illust
{
public partial class RecommendsPage : IllustDataCollectionPage
{
public bool ByUser { get; set; }
public static readonly BindableProperty SegmentIndexProperty = BindableProperty.Create(
nameof(SegmentIndex), typeof(int), typeof(RecommendsPage), propertyChanged: OnSegmentIndexPropertyChanged);
private static void OnSegmentIndexPropertyChanged(BindableObject obj, object oldValue, object newValue)
{
var page = (RecommendsPage)obj;
page.DoLoadIllusts();
}
public int SegmentIndex
{
get => (int)GetValue(SegmentIndexProperty);
set => SetValue(SegmentIndexProperty, value);
}
private IllustData illustData;
public RecommendsPage()
{
totalBarOffset = new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0);
navigationBarOffset = new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0);
Resources.Add("cardView", GetCardViewTemplate());
InitializeComponent();
}
@ -27,10 +47,39 @@ namespace Pixiview.Illust
loaded = false;
}
public override void OnOrientationChanged(Orientation orientation)
{
int columns;
switch (orientation)
{
case Orientation.Portrait:
columns = 2;
PageTopMargin = totalBarOffset;
break;
case Orientation.PortraitUpsideDown:
columns = isPhone ? 4 : 2;
PageTopMargin = isPhone ? navigationBarOffset : totalBarOffset;
break;
case Orientation.Unknown:
case Orientation.LandscapeLeft:
case Orientation.LandscapeRight:
default:
columns = 4;
PageTopMargin = navigationBarOffset;
break;
}
if (Columns != columns)
{
App.DebugPrint($"ranking page, change columns to {columns}");
Columns = columns;
}
}
protected override IEnumerable<IllustItem> DoGetIllustList(IllustData data, ICommand command)
{
if (ByUser)
if (SegmentIndex == 1)
{
// by user
return data.body.page.recommendUser.SelectMany(i => i.illustIds)
.Select(id =>
{
@ -44,6 +93,7 @@ namespace Pixiview.Illust
}
else
{
// recommends
return data.body.page.recommend.Select(id =>
{
var item = data.body.thumbnails.illust.FirstOrDefault(l => l.illustId == id)?.ConvertToItem();
@ -58,7 +108,12 @@ namespace Pixiview.Illust
protected override IllustData DoLoadIllustData(bool force)
{
return Stores.LoadIllustData(force);
if (illustData != null && !force)
{
return illustData;
}
illustData = Stores.LoadIllustData(force);
return illustData;
}
private void Refresh_Clicked(object sender, EventArgs e)

View File

@ -187,14 +187,14 @@ namespace Pixiview.Illust
item.OriginalUrl = p.urls.original;
}
DoLoadImage(0);
DoLoadImage(0, true);
if (items.Length > 1)
{
DoLoadImage(1);
}
}
private void DoLoadImage(int index)
private void DoLoadImage(int index, bool force = false)
{
var items = Illusts;
if (index < 0 || index >= items.Length)
@ -204,10 +204,13 @@ namespace Pixiview.Illust
}
var item = items[index];
if (item.Loading || (index > 0 && item.Image != null))
if (!force)
{
App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}");
return;
if (item.Loading || (index > 0 && item.Image != null))
{
App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}");
return;
}
}
item.Loading = true;
var image = Stores.LoadPreviewImage(item.PreviewUrl);

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;
namespace Pixiview.UI
{
public class SegmentedControl : View, IViewContainer<SegmentedControlOption>
{
public IList<SegmentedControlOption> Children { get; set; }
public SegmentedControl()
{
Children = new List<SegmentedControlOption>();
}
public static readonly BindableProperty TintColorProperty = BindableProperty.Create(
nameof(TintColor), typeof(Color), typeof(SegmentedControl));
public static readonly BindableProperty DisabledColorProperty = BindableProperty.Create(
nameof(DisabledColor), typeof(Color), typeof(SegmentedControl));
public static readonly BindableProperty SelectedTextColorProperty = BindableProperty.Create(
nameof(SelectedTextColor), typeof(Color), typeof(SegmentedControl));
public static readonly BindableProperty SelectedSegmentIndexProperty = BindableProperty.Create(
nameof(SelectedSegmentIndex), typeof(int), typeof(SegmentedControl));
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<ValueChangedEventArgs> ValueChanged;
[EditorBrowsable(EditorBrowsableState.Never)]
public void SendValueChanged()
{
ValueChanged?.Invoke(this, new ValueChangedEventArgs { NewValue = SelectedSegmentIndex });
}
}
public class SegmentedControlOption : View
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text), typeof(string), typeof(SegmentedControlOption));
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; }
}
}