Merge branch '4.6.0' into 4.7.0

This commit is contained in:
Rui Marinho 2020-05-28 15:19:06 +01:00
Родитель d80e38ae40 6f0fabeba8
Коммит 9bae2b34e8
7 изменённых файлов: 307 добавлений и 8 удалений

Просмотреть файл

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue9827"
BackgroundColor="Blue">
<controls:TestContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyTemplate">
<Grid Margin="10" BackgroundColor="Blue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Image Source="https://placekitten.com/g/100/200" BackgroundColor="#5833c2" WidthRequest="100" HeightRequest="200" />
<StackLayout Grid.Column="1">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Title}" FontSize="Subtitle" LineBreakMode="WordWrap" />
</StackLayout>
<Label Text="{Binding Title}" FontSize="Micro" />
<Label Text="{Binding Title}" FontSize="Micro" />
<Label Text="{Binding Title}" FontSize="Micro" />
<Label Text="{Binding Title}" FontSize="Micro" />
</StackLayout>
</Grid>
</DataTemplate>
</ResourceDictionary>
</controls:TestContentPage.Resources>
<Grid Padding="40" BackgroundColor="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="20" />
<RowDefinition Height="200" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Label Text="Swipe to the next item in the CarouselView. The SearchBar text should be updated to match the current item, and so should the indicator view. However, with PeekAreaInserts set to a non-0 value, the CurrentItem lags behind, so the IndicatorView and the SearchBar don't get updated. It looks like this is related to being inside of a Grid with a * row pushing the CarouselView down. O_O"></Label>
<StackLayout HorizontalOptions="Center" Grid.Row="1" Orientation="Horizontal">
<Button AutomationId="btnPrev" Command="{Binding GoPrevCommand}" Text="Back"/>
<Label AutomationId="lblPosition" Text="{ Binding Path=Position, Source={x:Reference cv}, StringFormat='Pos:{0}'}"/>
<Button AutomationId="btnNext" Command="{Binding GoNextCommand}" Text="Next"/>
</StackLayout>
<SearchBar AutomationId="searchBar" Grid.Row="2" x:Name="matchSearch" BindingContext="{Binding CarouselCurrentItem}" Text="{Binding Title, Mode=OneWay}" Placeholder="Title" WidthRequest="300" />
<CarouselView Grid.Row="3" x:Name="cv"
ItemsSource="{Binding Items}"
CurrentItem="{Binding CarouselCurrentItem}"
IsSwipeEnabled="true"
PeekAreaInsets="20"
IndicatorView="indicators"
ItemTemplate="{StaticResource MyTemplate}"
BackgroundColor="Pink"
AutomationId="carousel"
/>
<IndicatorView Grid.Row="4" x:Name="indicators" IndicatorColor="Orange" SelectedIndicatorColor="Purple" IndicatorsShape="Circle" MaximumVisible="10"/>
</Grid>
</controls:TestContentPage>

Просмотреть файл

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.ManualReview)]
#endif
#if APP
[XamlCompilation(XamlCompilationOptions.Compile)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 9827, "CarouselView doesn't update the CurrentItem on Swipe under strange condition", PlatformAffected.Android)]
public partial class Issue9827 : TestContentPage
{
ViewModelIssue9827 ViewModel => BindingContext as ViewModelIssue9827;
public Issue9827()
{
#if APP
Device.SetFlags(new List<string>(Device.Flags ?? new List<string>()) { ExperimentalFlags.IndicatorViewExperimental, ExperimentalFlags.CarouselViewExperimental });
InitializeComponent();
#endif
}
protected override void Init()
{
BindingContext = new ViewModelIssue9827();
}
protected override void OnAppearing()
{
if (ViewModel.Items.Count == 0)
ViewModel.LoadItemsCommand.Execute(null);
base.OnAppearing();
}
#if UITEST
[Test]
public void Issue9827Test()
{
RunningApp.WaitForElement("Pos:0");
RunningApp.Tap(c => c.Marked("btnNext"));
RunningApp.WaitForElement("Item 1 with some additional text");
RunningApp.WaitForElement("Pos:1");
}
#endif
}
[Preserve(AllMembers = true)]
public class ViewModelIssue9827 : System.ComponentModel.INotifyPropertyChanged
{
public ViewModelIssue9827()
{
Items = new ObservableCollection<ModelIssue9827>();
LoadItemsCommand = new Command(ExecuteLoadItemsCommand);
GoNextCommand = new Command(GoNext);
GoPrevCommand = new Command(GoPrev);
}
ModelIssue9827 carouselCurrentItem;
void GoNext()
{
var index = Items.IndexOf(carouselCurrentItem);
var newItem = Items[Math.Min(index + 1, Items.Count - 1)];
CarouselCurrentItem = newItem;
}
void GoPrev()
{
var index = Items.IndexOf(carouselCurrentItem);
var newItem = Items[Math.Max(0, index - 1)];
CarouselCurrentItem = newItem;
}
void ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try
{
Items.Clear();
var items = Enumerable.Range(0, 10).Select(i => new ModelIssue9827() { Title = $"Item {i} with some additional text" });
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public ModelIssue9827 CarouselCurrentItem
{
get { return carouselCurrentItem; }
set { SetProperty(ref carouselCurrentItem, value); }
}
public ObservableCollection<ModelIssue9827> Items { get; set; }
public Command LoadItemsCommand { get; set; }
public Command GoNextCommand { get; set; }
public Command GoPrevCommand { get; set; }
bool isBusy = false;
string title = string.Empty;
protected bool SetProperty<T>(ref T backingStore,
T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
public bool IsBusy { get { return isBusy; } set { SetProperty(ref isBusy, value); } }
public string Title { get { return title; } set { SetProperty(ref title, value); } }
#region INotifyPropertyChanged
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion
}
[Preserve(AllMembers = true)]
public class ModelIssue9827 : System.ComponentModel.INotifyPropertyChanged
{
string _title;
protected bool SetProperty<T>(ref T backingStore,
T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
public Guid Id { get; set; }
public string Title
{
get { return _title; }
set
{
if (_title == value)
{
return;
}
_title = value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged
}
}

Просмотреть файл

@ -796,6 +796,10 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue8145.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue8145.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10222.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue10222.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue4714.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue4714.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue9827.xaml.cs">
<SubType>Code</SubType>
<DependentUpon>Issue9827.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)_TemplateMarkup.xaml.cs"> <Compile Include="$(MSBuildThisFileDirectory)_TemplateMarkup.xaml.cs">
<DependentUpon>_TemplateMarkup.xaml</DependentUpon> <DependentUpon>_TemplateMarkup.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
@ -1559,6 +1563,10 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8958.xaml"> <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8958.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue9827.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla27417Xaml.xaml"> <EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla27417Xaml.xaml">
@ -2016,4 +2024,4 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

Просмотреть файл

@ -369,6 +369,7 @@ namespace Xamarin.Forms.Platform.Android
void CarouselViewScrolled(object sender, ItemsViewScrolledEventArgs e) void CarouselViewScrolled(object sender, ItemsViewScrolledEventArgs e)
{ {
_noNeedForScroll = false;
UpdatePosition(e.CenterItemIndex); UpdatePosition(e.CenterItemIndex);
UpdateVisualStates(); UpdateVisualStates();
} }
@ -474,7 +475,7 @@ namespace Xamarin.Forms.Platform.Android
class CarouselViewOnScrollListener : RecyclerViewScrollListener<ItemsView, IItemsViewSource> class CarouselViewOnScrollListener : RecyclerViewScrollListener<ItemsView, IItemsViewSource>
{ {
public CarouselViewOnScrollListener(ItemsView itemsView, ItemsViewAdapter<ItemsView, IItemsViewSource> itemsViewAdapter) : base(itemsView, itemsViewAdapter) public CarouselViewOnScrollListener(ItemsView itemsView, ItemsViewAdapter<ItemsView, IItemsViewSource> itemsViewAdapter) : base(itemsView, itemsViewAdapter, true)
{ {
} }

Просмотреть файл

@ -212,7 +212,7 @@ namespace Xamarin.Forms.Platform.Android
{ {
var maxVisible = GetMaximumVisible(); var maxVisible = GetMaximumVisible();
var position = IndicatorView.Position; var position = IndicatorView.Position;
_selectedIndex = position >= maxVisible ? maxVisible - 1 : position; _selectedIndex = Math.Max(0, position >= maxVisible ? maxVisible - 1 : position);
UpdateIndicators(); UpdateIndicators();
} }

Просмотреть файл

@ -16,11 +16,18 @@ namespace Xamarin.Forms.Platform.Android.CollectionView
int _horizontalOffset, _verticalOffset; int _horizontalOffset, _verticalOffset;
TItemsView _itemsView; TItemsView _itemsView;
ItemsViewAdapter<TItemsView, TItemsViewSource> _itemsViewAdapter; ItemsViewAdapter<TItemsView, TItemsViewSource> _itemsViewAdapter;
bool _getCenteredItemOnXAndY = false;
public RecyclerViewScrollListener(TItemsView itemsView, ItemsViewAdapter<TItemsView, TItemsViewSource> itemsViewAdapter) public RecyclerViewScrollListener(TItemsView itemsView, ItemsViewAdapter<TItemsView, TItemsViewSource> itemsViewAdapter) : this(itemsView, itemsViewAdapter, false)
{
}
public RecyclerViewScrollListener(TItemsView itemsView, ItemsViewAdapter<TItemsView, TItemsViewSource> itemsViewAdapter, bool getCenteredItemOnXAndY)
{ {
_itemsView = itemsView; _itemsView = itemsView;
_itemsViewAdapter = itemsViewAdapter; _itemsViewAdapter = itemsViewAdapter;
_getCenteredItemOnXAndY = getCenteredItemOnXAndY;
} }
public override void OnScrolled(RecyclerView recyclerView, int dx, int dy) public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
@ -42,7 +49,7 @@ namespace Xamarin.Forms.Platform.Android.CollectionView
{ {
firstVisibleItemIndex = linearLayoutManager.FindFirstVisibleItemPosition(); firstVisibleItemIndex = linearLayoutManager.FindFirstVisibleItemPosition();
lastVisibleItemIndex = linearLayoutManager.FindLastVisibleItemPosition(); lastVisibleItemIndex = linearLayoutManager.FindLastVisibleItemPosition();
centerItemIndex = recyclerView.CalculateCenterItemIndex(firstVisibleItemIndex, linearLayoutManager); centerItemIndex = recyclerView.CalculateCenterItemIndex(firstVisibleItemIndex, linearLayoutManager, _getCenteredItemOnXAndY);
} }
var context = recyclerView.Context; var context = recyclerView.Context;

Просмотреть файл

@ -10,7 +10,7 @@ namespace Xamarin.Forms.Platform.Android
{ {
internal static class RecyclerExtensions internal static class RecyclerExtensions
{ {
public static int CalculateCenterItemIndex(this RecyclerView recyclerView, int firstVisibleItemIndex, LinearLayoutManager linearLayoutManager) public static int CalculateCenterItemIndex(this RecyclerView recyclerView, int firstVisibleItemIndex, LinearLayoutManager linearLayoutManager, bool lookCenteredOnXAndY)
{ {
// This can happen if a layout pass has not happened yet // This can happen if a layout pass has not happened yet
if (firstVisibleItemIndex == -1) if (firstVisibleItemIndex == -1)
@ -21,12 +21,22 @@ namespace Xamarin.Forms.Platform.Android
if (linearLayoutManager.Orientation == LinearLayoutManager.Horizontal) if (linearLayoutManager.Orientation == LinearLayoutManager.Horizontal)
{ {
float centerX = recyclerView.Width / 2; float centerX = recyclerView.Width / 2;
centerView = recyclerView.FindChildViewUnder(centerX, recyclerView.Top); float centerY = recyclerView.Top;
if (lookCenteredOnXAndY)
centerY = recyclerView.Height / 2;
centerView = recyclerView.FindChildViewUnder(centerX, centerY);
} }
else else
{ {
float centerY = recyclerView.Height / 2; float centerY = recyclerView.Height / 2;
centerView = recyclerView.FindChildViewUnder(recyclerView.Left, centerY); float centerX = recyclerView.Left;
if (lookCenteredOnXAndY)
centerX = recyclerView.Width / 2;
centerView = recyclerView.FindChildViewUnder(centerX, centerY);
} }
if (centerView != null) if (centerView != null)