Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Android.Content;
using Android.Views;
using Microsoft.Maui.Graphics;
using AndroidX.Core.Widget;
using AndroidX.RecyclerView.Widget;
using AView = Android.Views.View;

namespace Microsoft.Maui.Controls.Handlers.Items
Expand Down Expand Up @@ -37,6 +39,75 @@ internal Func<Size?> RetrieveStaticSize
set => _retrieveStaticSize = new WeakReference(value);
}

public override bool DispatchTouchEvent(MotionEvent e)
{
if (IsHeaderOrFooterContent())
{

if (e.Action == MotionEventActions.Up || e.Action == MotionEventActions.Cancel)
{
Parent?.RequestDisallowInterceptTouchEvent(false);
}
}

return base.DispatchTouchEvent(e);
}

public override bool OnInterceptTouchEvent(MotionEvent ev)
{
if (IsHeaderOrFooterContent())
{
if (ev.Action == MotionEventActions.Down)
{
Parent?.RequestDisallowInterceptTouchEvent(true);
return false;
}
}

return base.OnInterceptTouchEvent(ev);
}

/// <summary>
/// Determines if this ItemContentView is being used for header or footer content
/// by checking if the contained View is the same as the ItemsView's Header or Footer
/// </summary>
bool IsHeaderOrFooterContent()
{
if (View == null)
{
return false;
}

// Find the parent ItemsView by traversing up the view hierarchy
var itemsView = FindParentItemsView();
if (itemsView is StructuredItemsView structuredItemsView)
{
// Check if our View is the same object reference as the header or footer
return ReferenceEquals(View, structuredItemsView.Header) ||
ReferenceEquals(View, structuredItemsView.Footer);
}

return false;
}

/// <summary>
/// Finds the parent StructuredItemsView by traversing the logical parent chain
/// </summary>
StructuredItemsView FindParentItemsView()
{
var current = View?.Parent;
while (current != null)
{
if (current is StructuredItemsView itemsView)
{
return itemsView;
}

current = current.Parent;
}
return null;
}

internal void RealizeContent(View view, ItemsView itemsView)
{
Content = CreateHandler(view, itemsView);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
~override Microsoft.Maui.Controls.Handlers.Items.ItemContentView.DispatchTouchEvent(Android.Views.MotionEvent e) -> bool
~override Microsoft.Maui.Controls.Handlers.Items.ItemContentView.OnInterceptTouchEvent(Android.Views.MotionEvent ev) -> bool
86 changes: 86 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue22120.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 22120, "CollectionView.Header is not scrollable in Android platform",
PlatformAffected.Android)]
public class Issue22120 : ContentPage
{
public Issue22120()
{
Title = "Issue 22120";

// Create header items for the ListView inside ScrollView
var headerItems = new List<string>();
for (int i = 1; i <= 15; i++)
{
headerItems.Add($"Header Item {i}");
}

// Create collection items
var collectionItems = new List<string>
{
"Pink", "Green", "Blue", "Yellow", "Orange", "Purple", "SkyBlue", "PaleGreen"
};

// Create the ListView for the header content with ItemTemplate
var headerListView = new ListView
{
AutomationId = "Issue22120HeaderListView",
HorizontalOptions = LayoutOptions.Center,
ItemsSource = headerItems,
HeightRequest = 400,
ItemTemplate = new DataTemplate(() =>
{
var label = new Label
{
Padding = new Thickness(10),
BackgroundColor = Colors.LightBlue
};
label.SetBinding(Label.TextProperty, ".");

return new ViewCell
{
View = label
};
})
};

// Create the ScrollView for the header containing the ListView
var headerScrollView = new ScrollView
{
AutomationId = "Issue22120HeaderScrollView",
HeightRequest = 200,
Content = headerListView
};

// Create the main CollectionView
var collectionView = new CollectionView
{
AutomationId = "Issue22120CollectionView",
Margin = new Thickness(20),
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
ItemsLayout = new GridItemsLayout(3, ItemsLayoutOrientation.Vertical),
Header = headerScrollView,
ItemsSource = collectionItems,
ItemTemplate = new DataTemplate(() =>
{
var button = new Button
{
Margin = new Thickness(5),
Padding = new Thickness(0),
HeightRequest = 60,
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Center
};
button.SetBinding(Button.TextProperty, ".");
return button;
})
};

Content = collectionView;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue22120 : _IssuesUITest
{
public Issue22120(TestDevice device) : base(device) { }

public override string Issue => "CollectionView.Header is not scrollable in Android platform";

[Test]
[Category(UITestCategories.CollectionView)]
public void CollectionViewHeaderScrollViewIsScrollable()
{
App.WaitForElement("Issue22120HeaderScrollView");
App.WaitForElement("Header Item 1");
App.ScrollDown("Issue22120HeaderScrollView", ScrollStrategy.Gesture, swipePercentage: 0.9, swipeSpeed: 1000);
App.WaitForElement("Header Item 7", timeout: TimeSpan.FromSeconds(5));
App.ScrollDown("Issue22120HeaderScrollView", ScrollStrategy.Gesture, swipePercentage: 0.9, swipeSpeed: 1000);
App.WaitForElement("Header Item 12", timeout: TimeSpan.FromSeconds(5));
App.ScrollUp("Issue22120HeaderScrollView", ScrollStrategy.Gesture, swipePercentage: 0.9, swipeSpeed: 1000);
App.ScrollUp("Issue22120HeaderScrollView", ScrollStrategy.Gesture, swipePercentage: 0.9, swipeSpeed: 1000);
App.WaitForElement("Header Item 1", timeout: TimeSpan.FromSeconds(5));
}
}
Loading