* Pixiview/UI/OptionCell.cs:

* Pixiview/UI/AdaptedPage.cs:
* Pixiview/Utils/FileStore.cs:
* Pixiview/Utils/IllustData.cs:
* Pixiview/Illust/MainPage.xaml:
* Pixiview/UI/Theme/LightTheme.cs:
* Pixiview/Illust/MainPage.xaml.cs:
* Pixiview.iOS/GlobalSuppressions.cs:
* Pixiview/Illust/ViewIllustPage.xaml:
* Pixiview.Android/GlobalSuppressions.cs:
* Pixiview/Illust/ViewIllustPage.xaml.cs:
* Pixiview/Illust/IllustCollectionPage.cs:
* Pixiview.iOS/Renderers/SegmentedControlRenderer.cs:

* Pixiview/Illust/RankingPage.xaml:
* Pixiview/Illust/RankingPage.xaml.cs:
* Pixiview.Android/Pixiview.Android.csproj:
* Pixiview.Android/Resources/values/colors.xml:
* Pixiview.Android/Resources/Resource.designer.cs:
* Pixiview.Android/Resources/layout/RadioGroup.xml:
* Pixiview.Android/Renderers/BlurryPanelRenderer.cs:
* Pixiview.Android/Resources/layout/RadioButton.xml:
* Pixiview.Android/Renderers/SegmentedControlRenderer.cs:
* Pixiview.Android/Resources/color/segmented_control_text.xml:
* Pixiview.Android/Resources/drawable/segmented_control_background.xml:
* Pixiview.Android/Resources/drawable/segmented_control_last_background.xml:
* Pixiview.Android/Resources/drawable/segmented_control_first_background.xml:
  segmented control for Android
This commit is contained in:
2020-05-13 18:09:16 +08:00
parent ba46ba02c4
commit d32c5eb69a
26 changed files with 913 additions and 424 deletions

View File

@ -6,4 +6,6 @@
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0056:Use index operator", Justification = "<Pending>")]
[assembly: SuppressMessage("Style", "IDE0057:Use range operator", Justification = "<Pending>")]
[assembly: SuppressMessage("Style", "IDE0063:Use simple 'using' statement", Justification = "<Pending>")]
[assembly: SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "<Pending>")]

View File

@ -68,6 +68,8 @@
<Compile Include="Effects\LongPressEffectImplement.cs" />
<Compile Include="Renderers\CircleImageRenderer.cs" />
<Compile Include="Renderers\CardViewRenderer.cs" />
<Compile Include="Renderers\BlurryPanelRenderer.cs" />
<Compile Include="Renderers\SegmentedControlRenderer.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
@ -155,6 +157,30 @@
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\color\segmented_control_text.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\segmented_control_background.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\segmented_control_first_background.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\drawable\segmented_control_last_background.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\layout\RadioButton.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\layout\RadioGroup.xml">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\drawable\" />
@ -166,6 +192,7 @@
<Folder Include="Resources\mipmap-xxhdpi\" />
<Folder Include="Resources\mipmap-xxxhdpi\" />
<Folder Include="Renderers\" />
<Folder Include="Resources\color\" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\fa-light-300.ttf" />

View File

@ -0,0 +1,23 @@
using Android.Content;
using Pixiview.Droid.Renderers;
using Pixiview.UI;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(BlurryPanel), typeof(BlurryPanelRenderer))]
namespace Pixiview.Droid.Renderers
{
public class BlurryPanelRenderer : ViewRenderer
{
public BlurryPanelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
SetBackgroundColor(Color.Black.MultiplyAlpha(.92).ToAndroid());
}
}
}

View File

@ -0,0 +1,234 @@
using Android.Content;
using Android.Graphics.Drawables;
using Android.Views;
using Android.Widget;
using Pixiview.Droid.Renderers;
using Pixiview.UI;
using Xamarin.Forms.Platform.Android;
[assembly: Xamarin.Forms.ExportRenderer(typeof(SegmentedControl), typeof(SegmentedControlRenderer))]
namespace Pixiview.Droid.Renderers
{
public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, RadioGroup>
{
RadioGroup nativeControl;
RadioButton _rb;
readonly Context context;
public SegmentedControlRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
{
base.OnElementChanged(e);
if (Control == null)
{
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
var layoutInflater = LayoutInflater.From(context);
var view = layoutInflater.Inflate(Resource.Layout.RadioGroup, null);
nativeControl = (RadioGroup)layoutInflater.Inflate(Resource.Layout.RadioGroup, null);
var density = context.Resources.DisplayMetrics.Density;
for (var i = 0; i < Element.Children.Count; i++)
{
var o = Element.Children[i];
var rb = (RadioButton)layoutInflater.Inflate(Resource.Layout.RadioButton, null);
var width = rb.Paint.MeasureText(o.Text) * density + 0.5f;
rb.LayoutParameters = new RadioGroup.LayoutParams((int)width, LayoutParams.WrapContent, 1f);
rb.Text = o.Text;
if (i == 0)
rb.SetBackgroundResource(Resource.Drawable.segmented_control_first_background);
else if (i == Element.Children.Count - 1)
rb.SetBackgroundResource(Resource.Drawable.segmented_control_last_background);
else
rb.SetBackgroundResource(Resource.Drawable.segmented_control_background);
ConfigureRadioButton(i, rb);
nativeControl.AddView(rb);
}
var option = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex);
if (option != null)
option.Checked = true;
SetNativeControl(nativeControl);
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
if (nativeControl != null)
nativeControl.CheckedChange -= NativeControl_ValueChanged;
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
nativeControl.CheckedChange += NativeControl_ValueChanged;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (nativeControl == null || Element == null) return;
switch (e.PropertyName)
{
case "Renderer":
Element?.SendValueChanged();
break;
case nameof(SegmentedControl.SelectedSegmentIndex):
var option = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex);
if (option != null)
option.Checked = true;
if (Element.SelectedSegmentIndex < 0)
{
var layoutInflater = LayoutInflater.From(context);
nativeControl = (RadioGroup)layoutInflater.Inflate(Resource.Layout.RadioGroup, null);
for (var i = 0; i < Element.Children.Count; i++)
{
var o = Element.Children[i];
var rb = (RadioButton)layoutInflater.Inflate(Resource.Layout.RadioButton, null);
var width = rb.Paint.MeasureText(o.Text);
rb.LayoutParameters = new RadioGroup.LayoutParams((int)width, LayoutParams.WrapContent, 1f);
rb.Text = o.Text;
if (i == 0)
rb.SetBackgroundResource(Resource.Drawable.segmented_control_first_background);
else if (i == Element.Children.Count - 1)
rb.SetBackgroundResource(Resource.Drawable.segmented_control_last_background);
else
rb.SetBackgroundResource(Resource.Drawable.segmented_control_background);
ConfigureRadioButton(i, rb);
nativeControl.AddView(rb);
}
nativeControl.CheckedChange += NativeControl_ValueChanged;
SetNativeControl(nativeControl);
}
Element.SendValueChanged();
break;
case nameof(SegmentedControl.TintColor):
OnPropertyChanged();
break;
case nameof(SegmentedControl.IsEnabled):
OnPropertyChanged();
break;
case nameof(SegmentedControl.SelectedTextColor):
var v = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex);
v.SetTextColor(Element.SelectedTextColor.ToAndroid());
break;
}
}
void OnPropertyChanged()
{
if (nativeControl != null && Element != null)
{
for (var i = 0; i < Element.Children.Count; i++)
{
var rb = (RadioButton)nativeControl.GetChildAt(i);
ConfigureRadioButton(i, rb);
}
}
}
void ConfigureRadioButton(int i, RadioButton rb)
{
if (i == Element.SelectedSegmentIndex)
{
rb.SetTextColor(Element.SelectedTextColor.ToAndroid());
_rb = rb;
}
else
{
var textColor = Element.IsEnabled ? Element.TintColor.ToAndroid() : Element.DisabledColor.ToAndroid();
rb.SetTextColor(textColor);
}
GradientDrawable selectedShape;
GradientDrawable unselectedShape;
var gradientDrawable = (StateListDrawable)rb.Background;
var drawableContainerState = (DrawableContainer.DrawableContainerState)gradientDrawable.GetConstantState();
var children = drawableContainerState.GetChildren();
// Doesnt works on API < 18
selectedShape = children[0] is GradientDrawable ? (GradientDrawable)children[0] : (GradientDrawable)((InsetDrawable)children[0]).Drawable;
unselectedShape = children[1] is GradientDrawable ? (GradientDrawable)children[1] : (GradientDrawable)((InsetDrawable)children[1]).Drawable;
var color = Element.IsEnabled ? Element.TintColor.ToAndroid() : Element.DisabledColor.ToAndroid();
selectedShape.SetStroke(3, color);
selectedShape.SetColor(color);
unselectedShape.SetStroke(3, color);
rb.Enabled = Element.IsEnabled;
}
void NativeControl_ValueChanged(object sender, RadioGroup.CheckedChangeEventArgs e)
{
var rg = (RadioGroup)sender;
if (rg.CheckedRadioButtonId != -1)
{
var id = rg.CheckedRadioButtonId;
var radioButton = rg.FindViewById(id);
var radioId = rg.IndexOfChild(radioButton);
var rb = (RadioButton)rg.GetChildAt(radioId);
var color = Element.IsEnabled ? Element.TintColor.ToAndroid() : Element.DisabledColor.ToAndroid();
_rb?.SetTextColor(color);
rb.SetTextColor(Element.SelectedTextColor.ToAndroid());
_rb = rb;
Element.SelectedSegmentIndex = radioId;
}
}
protected override void Dispose(bool disposing)
{
if (nativeControl != null)
{
nativeControl.CheckedChange -= NativeControl_ValueChanged;
nativeControl.Dispose();
nativeControl = null;
_rb = null;
}
try
{
base.Dispose(disposing);
}
catch
{
return;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="@color/normal"/>
<item android:color="@color/selected" />
</selector>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<inset android:insetRight="-1dp">
<shape android:id="@+id/shape_id" xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/selected" />
<stroke android:width="1dp" android:color="@color/selected" />
</shape>
</inset>
</item>
<item android:state_checked="false">
<inset android:insetRight="-1dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/normal" />
<stroke android:width="1dp" android:color="@color/selected" />
</shape>
</inset>
</item>
</selector>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<inset android:insetRight="-1dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/selected" />
<stroke android:width="1dp" android:color="@color/selected" />
<corners android:topLeftRadius="2sp" android:bottomLeftRadius="2sp" />
</shape>
</inset>
</item>
<item android:state_checked="false">
<inset android:insetRight="-1dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/normal" />
<stroke android:width="1dp" android:color="@color/selected" />
<corners android:topLeftRadius="2sp" android:bottomLeftRadius="2sp" />
</shape>
</inset>
</item>
</selector>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/selected" />
<stroke android:width="1dp" android:color="@color/selected" />
<corners android:topRightRadius="2sp" android:bottomRightRadius="2sp" />
</shape>
</item>
<item android:state_checked="false">
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/normal" />
<stroke android:width="1dp" android:color="@color/selected" />
<corners android:topRightRadius="2sp" android:bottomRightRadius="2sp" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:button="@null"
android:gravity="center"
android:background="@drawable/segmented_control_background"
android:textColor="@color/segmented_control_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:minHeight="30dp"
android:textSize="12sp" />

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="@+id/SegControl" />

View File

@ -4,4 +4,6 @@
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="normal">@android:color/transparent</color>
<color name="selected">#007AFF</color>
</resources>