diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml new file mode 100644 index 000000000000..40ae259cda7e --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml.cs new file mode 100644 index 000000000000..cd2fad50de50 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26328.xaml.cs @@ -0,0 +1,99 @@ +using System.Collections.ObjectModel; +using System.Globalization; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 26328, "SwipeView causes Java.Lang.IllegalArgumentException: Cannot add a null child view to a ViewGroup", PlatformAffected.Android)] + public partial class Issue26328 : TestContentPage + { + public Issue26328() + { + InitializeComponent(); + } + + protected override void Init() + { + BindingContext = new Issue26328ViewModel(); + } + } + + public class Issue26328ItemModel + { + public int Id { get; set; } + public string Title { get; set; } + + public Issue26328ItemModel(int i) + { + Id = i; + Title = "Hello"; + } + + public Command SwipeCommand => + new Command(DeleteMessage); + + void DeleteMessage() + { +#pragma warning disable CS0618 // Type or member is obsolete + MessagingCenter.Send(this, "Swipe", new Issue26328RemoveMessage(this)); +#pragma warning disable CS0618 // Type or member is obsolete + } + } + + public class Issue26328RemoveMessage + { + public Issue26328ItemModel Item { get; } + + public Issue26328RemoveMessage(Issue26328ItemModel item) + { + Item = item; + } + } + + public class Issue26328ViewModel + { + public ObservableCollection ItemList { get; set; } + + public Issue26328ViewModel() + { +#pragma warning disable CS0618 // Type or member is obsolete + MessagingCenter.Subscribe(this, "Swipe", (sender, arg) => RemoveItem(arg.Item)); +#pragma warning disable CS0618 // Type or member is obsolete + ItemList = []; + for (var i = 0; i < 200; ++i) + { + ItemList.Add(new Issue26328ItemModel(i)); + } + } + + void RemoveItem(Issue26328ItemModel item) + { + ItemList.Remove(item); + GC.Collect(); + } + } + + [AcceptEmptyServiceProvider] + public class Issue26328TestConverter : IMultiValueConverter, IMarkupExtension + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values[0] == null || values[1] == null) + { + return null; + } + var id = (int)values[0]; + var title = (string)values[1]; + return $"{title} - {id}"; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public object ProvideValue(IServiceProvider serviceProvider) + { + return this; + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26328.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26328.cs new file mode 100644 index 000000000000..9d29a3beccd5 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26328.cs @@ -0,0 +1,31 @@ +#if ANDROID // Crash only happened on Android +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue26328 : _IssuesUITest + { + public override string Issue => "SwipeView causes Java.Lang.IllegalArgumentException: Cannot add a null child view to a ViewGroup"; + + public Issue26328(TestDevice device) + : base(device) + { } + + [Test] + [Category(UITestCategories.SwipeView)] + public void NoCrashRemovingSwipeItems() + { + App.WaitForElement("TestCollectionView"); + + for(int i = 0; i < 10; i++) + { + App.SwipeRightToLeft(); + App.ScrollDown("TestCollectionView", ScrollStrategy.Gesture, swipePercentage: 0.2); + App.ScrollUp("TestCollectionView", ScrollStrategy.Gesture, swipePercentage: 0.2); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Core/src/Handlers/SwipeItemView/SwipeItemViewHandler.Android.cs b/src/Core/src/Handlers/SwipeItemView/SwipeItemViewHandler.Android.cs index bb1f25098087..99d49ecc0053 100644 --- a/src/Core/src/Handlers/SwipeItemView/SwipeItemViewHandler.Android.cs +++ b/src/Core/src/Handlers/SwipeItemView/SwipeItemViewHandler.Android.cs @@ -57,6 +57,7 @@ public static void MapVisibility(ISwipeItemViewHandler handler, ISwipeItemView v protected override void DisconnectHandler(ContentViewGroup platformView) { // If we're being disconnected from the xplat element, then we should no longer be managing its children + platformView.CrossPlatformLayout = null; platformView.RemoveAllViews(); base.DisconnectHandler(platformView); } diff --git a/src/Core/src/Platform/Android/MauiSwipeView.cs b/src/Core/src/Platform/Android/MauiSwipeView.cs index bc1ce01f761a..605bb66f7106 100644 --- a/src/Core/src/Platform/Android/MauiSwipeView.cs +++ b/src/Core/src/Platform/Android/MauiSwipeView.cs @@ -526,7 +526,7 @@ float GetSwipeContentOffset() void UpdateSwipeItems() { - if (_contentView == null || _actionView != null) + if (_contentView == null || _contentView.IsDisposed() || _actionView != null) return; ISwipeItems? items = GetSwipeItemsByDirection(); @@ -545,22 +545,25 @@ void UpdateSwipeItems() foreach (var item in items) { - AView swipeItem = item.ToPlatform(MauiContext); + AView? swipeItem = item?.ToPlatform(MauiContext); - if (item is ISwipeItemView formsSwipeItemView) + if (swipeItem is not null) { - _actionView.AddView(swipeItem); - UpdateSwipeItemViewLayout(formsSwipeItemView); - _swipeItems.Add(formsSwipeItemView, swipeItem); - } - else if (item is ISwipeItemMenuItem menuItem) - { - _actionView.AddView(swipeItem); - _swipeItems.Add(item, swipeItem); - } + if (item is ISwipeItemView formsSwipeItemView) + { + _actionView.AddView(swipeItem); + UpdateSwipeItemViewLayout(formsSwipeItemView); + _swipeItems.Add(formsSwipeItemView, swipeItem); + } + else if (item is ISwipeItemMenuItem menuItem) + { + _actionView.AddView(swipeItem); + _swipeItems.Add(item, swipeItem); + } - if (swipeItem != null) - swipeItems.Add(swipeItem); + if (swipeItem != null) + swipeItems.Add(swipeItem); + } } AddView(_actionView);