Merge branch '4.6.0' into 4.7.0
This commit is contained in:
Коммит
9bae2b34e8
|
@ -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)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче