Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Correction of CollectionView to work properly with selected item color (
Browse files Browse the repository at this point in the history
#12651) fixes #7790 fixes #9590 fixes #11421

* Correction of CollectionView to work properly with selected item color when using VisualState on Android and iOS

* Update TemplatedCell.cs

* Update TemplatedItemViewHolder.cs

* Add scaling to make the bug more obvious

* Make fix work for on-template visual states on iOS

* Make fix for for on-template visual states on Android

* Minor performance optimization

* Make method name more explicit

* Spaces -> Tabs

* Fix UI test failure (item 3 doesn't fit on the screen anymore)

Co-authored-by: Rony Mesquita <ronymesquitadasilva@gmail.com>
  • Loading branch information
hartez and ronymesquita committed Nov 1, 2020
1 parent 75ca48d commit 6355a1d
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 39 deletions.
Expand Up @@ -36,8 +36,8 @@ public void SelectionShouldUpdateBinding()
RunningApp.WaitForElement("Selected: Item: 2");

// Tapping Item 3 should select it and updating the binding
RunningApp.Tap("Item 3");
RunningApp.WaitForElement("Selected: Item: 3");
RunningApp.Tap("Item 1");
RunningApp.WaitForElement("Selected: Item: 1");
}
#endif
}
Expand Down
@@ -1,33 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.SelectionGalleries.SingleBoundSelection">
<ContentPage.Content>
<StackLayout Spacing="5">

<Label Text="The selected item in the CollectionView should match the 'Selected' Label below. If it does not, this test has failed."
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />

<Label Text="{Binding SelectedItem, StringFormat='{}Selected: {0}'}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />

<Button AutomationId="Reset" Text="Reset Selection to Item 0" Clicked="ResetClicked" />

<Button AutomationId="Clear" Text="Clear Selection" Clicked="ClearClicked" />

<CollectionView ItemsSource="{Binding Items}" SelectionMode="Single" SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Image Source="{Binding Image}" HeightRequest="50" />
<Label Text="{Binding Caption}"></Label>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

</StackLayout>
</ContentPage.Content>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.SelectionGalleries.SingleBoundSelection">

<ContentPage.Content>
<StackLayout
Spacing="5">

<Label
Text="The selected item in the CollectionView should match the 'Selected' Label below. If it does not, this test has failed."
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />

<Label
Text="{Binding SelectedItem, StringFormat='{}Selected: {0}'}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />

<Button
AutomationId="Reset"
Text="Reset Selection to Item 0"
Clicked="ResetClicked" />

<Button
AutomationId="Clear"
Text="Clear Selection"
Clicked="ClearClicked" />

<CollectionView
ItemsSource="{Binding Items}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
HasShadow="False"
Margin="10"
BackgroundColor="LightBlue"
CornerRadius="20">
<StackLayout>
<Image
Source="{Binding Image}"
HeightRequest="50" />
<Label
Text="{Binding Caption}"></Label>
</StackLayout>

<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="RoyalBlue" />
<Setter
Property="Scale"
Value="0.9" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>

</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

</StackLayout>
</ContentPage.Content>
</ContentPage>
Expand Up @@ -34,6 +34,8 @@ public bool IsSelected
}
}

protected virtual bool UseDefaultSelectionColor => true;

public void OnClick(global::Android.Views.View view)
{
if (_isSelectionEnabled)
Expand All @@ -55,6 +57,11 @@ protected virtual void OnViewHolderClicked(int adapterPosition)

void SetSelectionStates(bool isSelected)
{
if (!UseDefaultSelectionColor)
{
return;
}

if (Forms.IsMarshmallowOrNewer)
{
// We're looking for the foreground ripple effect, which is not available on older APIs
Expand Down
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms.Platform.Android
Expand Down Expand Up @@ -81,5 +83,46 @@ public void Recycle(ItemsView itemsView)

itemsView.AddLogicalChild(View);
}

protected override bool UseDefaultSelectionColor
{
get
{
if (View != null)
{
return !IsUsingVSMForSelectionColor(View);
}

return base.UseDefaultSelectionColor;
}
}

bool IsUsingVSMForSelectionColor(View view)
{
var groups = VisualStateManager.GetVisualStateGroups(view);
for (var groupIndex = 0; groupIndex < groups.Count; groupIndex++)
{
var group = groups[groupIndex];
for (var stateIndex = 0; stateIndex < group.States.Count; stateIndex++)
{
var state = group.States[stateIndex];
if (state.Name != VisualStateManager.CommonStates.Selected)
{
continue;
}

for (var setterIndex = 0; setterIndex < state.Setters.Count; setterIndex++)
{
var setter = state.Setters[setterIndex];
if (setter.Property.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
{
return true;
}
}
}
}

return false;
}
}
}
}
49 changes: 44 additions & 5 deletions Xamarin.Forms.Platform.iOS/CollectionView/TemplatedCell.cs
@@ -1,4 +1,4 @@
using System;
using System;
using CoreGraphics;
using Foundation;
using UIKit;
Expand Down Expand Up @@ -39,7 +39,7 @@ public override void ConstrainTo(nfloat constant)
ClearConstraints();
ConstrainedDimension = constant;
}

protected void ClearConstraints()
{
ConstrainedSize = default;
Expand All @@ -52,7 +52,7 @@ protected void ClearConstraints()
var preferredAttributes = base.PreferredLayoutAttributesFittingAttributes(layoutAttributes);

// Measure this cell (including the Forms element) if there is no constrained size
var size = ConstrainedSize == default(CGSize) ? Measure() : ConstrainedSize;
var size = ConstrainedSize == default(CGSize) ? Measure() : ConstrainedSize;

// Update the size of the root view to accommodate the Forms element
var nativeView = VisualElementRenderer.NativeView;
Expand Down Expand Up @@ -90,6 +90,17 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
// Create the content and renderer for the view
var view = itemTemplate.CreateContent() as View;

// Prevents the use of default color when there are VisualStateManager with Selected state setting the background color
// First we check whether the cell has the default selected background color; if it does, then we should check
// to see if the cell content is the VSM to set a selected color
if (SelectedBackgroundView.BackgroundColor == ColorExtensions.Gray && IsUsingVSMForSelectionColor(view))
{
SelectedBackgroundView = new UIView
{
BackgroundColor = UIColor.Clear
};
}

// Set the binding context _before_ we create the renderer; that way, it's available during OnElementChanged
view.BindingContext = bindingContext;

Expand All @@ -102,7 +113,7 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi
// emit a bunch of needless binding errors
itemsView.AddLogicalChild(view);
}
else
else
{
// Same template, different data
var currentElement = VisualElementRenderer?.Element;
Expand Down Expand Up @@ -146,6 +157,34 @@ void ClearSubviews()
}
}

bool IsUsingVSMForSelectionColor(View view)
{
var groups = VisualStateManager.GetVisualStateGroups(view);
for (var groupIndex = 0; groupIndex < groups.Count; groupIndex++)
{
var group = groups[groupIndex];
for (var stateIndex = 0; stateIndex < group.States.Count; stateIndex++)
{
var state = group.States[stateIndex];
if (state.Name != VisualStateManager.CommonStates.Selected)
{
continue;
}

for (var setterIndex = 0; setterIndex < state.Setters.Count; setterIndex++)
{
var setter = state.Setters[setterIndex];
if (setter.Property.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
{
return true;
}
}
}
}

return false;
}

public override bool Selected
{
get => base.Selected;
Expand Down Expand Up @@ -187,4 +226,4 @@ protected void OnContentSizeChanged()
ContentSizeChanged?.Invoke(this, EventArgs.Empty);
}
}
}
}

0 comments on commit 6355a1d

Please sign in to comment.