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

Fix 4143: improved Span region calculation #13348

Merged
merged 4 commits into from
Jul 7, 2021
Merged

Conversation

Braini01
Copy link
Contributor

@Braini01 Braini01 commented Jan 8, 2021

Description of Change

There are a lot of problems that are connected to the incorrect calculation of the region of a Span, especially on Android. This causes diffferent effects like calling two commands with one tap due to overlapping regions or the reacting area for the tap is not where it is supposed to be.

This PR is an attempt to fix those problems currently for Android only. To completely fix #4143 some problems on iOS need to be fixed to and I am willing to fix them too, but I would like to discuss my Android approach here first with this PR.

Issues Resolved

API Changes

Added:

  • static Region Region.FromRectangles(IEnumerable rectangles)

Platforms Affected

  • Android
  • iOS (would add it later, depending on the discussion)

Behavioral/Visual Changes

Some regions now might be shorter, because certain lines were tappable as a whole. Now, it should only go as far as there is some visible character.

Before/After Screenshots

There are no visual changes but in order to verify my code I added code to the LabelRenderer that would draw rectangles around the span regions. Here are the drawn regions before and after my code changes and with and without region inflation:

Current State:
unfixed_inflated

Fixed State:
fixed_inflated

Current State without inflation:
unfixed_not_inflated

Fixed State without inflation:
fixed_not_inflated

I am not sure if the exisiting region inflation is working as it should, so that's why I put uninflated examples here for better comparison.

Testing Procedure

I created a Test Case in the Android ControlGallery (Issue4143). There are three Labels with text in it and some texts are colored blue to determine a tappable Span. If such a Span is tapped, its background color should change. Make sure that only the tapped Span changes its background color. Also make sure that tapping outside of blue text areas does not trigger any color changes.

PR Checklist

  • Targets the correct branch
  • Tests are passing (or failures are unrelated)

@Braini01
Copy link
Contributor Author

Hi there, just would like to know what the state of this PR is. Is it unusable, is it complex and discussed internally or is the review scheduled for a certain point in the future? Just so I know if it is still in the pipe and I will just wait or if I could improve anything or if I should forget about it :)

@adamped
Copy link
Contributor

adamped commented Jan 22, 2021

Having originally wrote the span gesture recognizer code long ago (though to be clear, I don't work for MS, just a contributor), having a look at this PR, it looks great to me. It was never designed with RTL in mind originally either.

Regarding the inflation, the reason it was added, is because it we wanted to give a wider area to tap. While we can see an issue if they are all crammed together, it was designed with just a single link in a large body of text.

But creating the regions only adds them to a list. When the label is tapped, it checks the regions, if it finds 2, it deflates them and only selects the one that it touches when deflated.

You can check out the original PR here: https://github.com/xamarin/Xamarin.Forms/pull/2173/files#diff-567ae32d5aada76aa0f492e11c0f4188f293e2186636f2fd5835db95a3ee2725R305
And look at GetChildElements, you'll see the Deflate.

Also don't be discouraged if they don't respond to you for months. Some of my PRs sit there for months on end. I have one fixing a minor issue for spans in iOS - #11321

@Braini01
Copy link
Contributor Author

@adamped Thank you very much for the insights. Very much appreciated and helpful :)

I was not discouraged but a little lost. It's my first PR here, so I did not know how things are rollin. So thanks for your information.

@cabal95
Copy link

cabal95 commented Mar 5, 2021

For anybody who needs this ASAP, I was able to manually integrate this into my custom Label Renderer without too much trouble. Had to use a few Reflection lookups to access private fields and constructors, but other than that it worked great to just take the main method that was fixed and drop it straight into my custom renderer.

Thanks @Braini01 for tracking this down and fixing it!

Copy link
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Captura de pantalla 2021-05-03 a las 10 35 57

@rachelkang
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

@rachelkang rachelkang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thank you for your contribution!

Are you planning on adding the iOS implementation to this PR, or to a separate PR?

@Braini01
Copy link
Contributor Author

Braini01 commented May 5, 2021

If I come around to do it, I would make a new PR for iOS.

@IeuanWalker
Copy link

IeuanWalker commented May 11, 2021

For anybody who needs this ASAP, I was able to manually integrate this into my custom Label Renderer without too much trouble. Had to use a few Reflection lookups to access private fields and constructors, but other than that it worked great to just take the main method that was fixed and drop it straight into my custom renderer.

Thanks @Braini01 for tracking this down and fixing it!

@cabal95 could you share your code

@cabal95
Copy link

cabal95 commented May 12, 2021

@IeuanWalker I had to rip out a few unrelated things as we already had some other custom stuff in our renderer, but this should be the related bits. Note that this is built against Xamarin 5.0.0.1931. If you try to target any other version you might need to make sure the reflected items still exist.

Expand for code
    /// <summary>
    /// Custom <see cref="Label"/> renderer to standardize the rendering of labels.
    /// </summary>
    /// <seealso cref="Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer" />
    public class LabelRenderer : Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LabelRenderer"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public LabelRenderer( Context context )
            : base( context )
        {
        }

        #region Temporary Fix for issue #11657

        // This code can be removed when https://github.com/xamarin/Xamarin.Forms/pull/13348 is merged.

        FieldInfo _spannableStringField = typeof( Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer )
            .GetField( "_spannableString", BindingFlags.NonPublic | BindingFlags.Instance );

        private static readonly ConstructorInfo _regionCtor = typeof( Region )
            .GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[1] { typeof( IList<Rectangle> ) }, null );

        protected override void OnLayout( bool changed, int left, int top, int right, int bottom )
        {
            base.OnLayout( changed, left, top, right, bottom );
            RecalculateSpanPositionsFixed( this, Element, ( SpannableString ) _spannableStringField.GetValue( this ), new SizeRequest( new Size( right - left, bottom - top ) ) );
        }

        private static void RecalculateSpanPositionsFixed( TextView textView, Label element, SpannableString spannableString, SizeRequest finalSize )
        {
            if ( element?.FormattedText?.Spans == null || element.FormattedText.Spans.Count == 0 )
                return;

            var labelWidth = finalSize.Request.Width;
            if ( labelWidth <= 0 || finalSize.Request.Height <= 0 )
                return;

            if ( spannableString == null || spannableString.Handle == IntPtr.Zero/*spannableString.IsDisposed()*/ )
                return;

            var layout = textView.Layout;
            if ( layout == null )
                return;

            int next = 0;
            int count = 0;

            var padding = element.Padding;
            var padLeft = ( int ) textView.Context.ToPixels( padding.Left );
            var padTop = ( int ) textView.Context.ToPixels( padding.Top );

            for ( int i = 0; i < spannableString.Length(); i = next )
            {
                var type = Java.Lang.Class.FromType( typeof( Java.Lang.Object ) );

                var span = element.FormattedText.Spans[count];

                count++;

                if ( string.IsNullOrEmpty( span.Text ) )
                    continue;

                // find the next span
                next = spannableString.NextSpanTransition( i, spannableString.Length(), type );

                // get all spans in the range - Android can have overlapping spans				
                var spans = spannableString.GetSpans( i, next, type );

                var startSpan = spans[0];
                var endSpan = spans[spans.Length - 1];

                var spanStartOffset = spannableString.GetSpanStart( startSpan );
                var spanEndOffset = spannableString.GetSpanEnd( endSpan );

                var spanStartLine = layout.GetLineForOffset( spanStartOffset );
                var spanEndLine = layout.GetLineForOffset( spanEndOffset );

                //go through all lines that are affected by the span and calculate an rectangle for each
                var spanRectangles = new List<Rectangle>();
                for ( var curLine = spanStartLine; curLine <= spanEndLine; curLine++ )
                {
                    global::Android.Graphics.Rect bounds = new global::Android.Graphics.Rect();
                    layout.GetLineBounds( curLine, bounds );
                    var lineHeight = bounds.Height();
                    var lineStartOffset = layout.GetLineStart( curLine );
                    var lineVisibleEndOffset = layout.GetLineVisibleEnd( curLine );

                    var startOffset = ( curLine == spanStartLine ) ? spanStartOffset : lineStartOffset;
                    var spanStartX = ( int ) layout.GetPrimaryHorizontal( startOffset );

                    var endOffset = ( curLine == spanEndLine ) ? spanEndOffset : lineVisibleEndOffset; ;
                    var spanEndX = ( int ) layout.GetSecondaryHorizontal( endOffset );

                    var spanWidth = spanEndX - spanStartX;
                    var spanLeftX = spanStartX;
                    //if rtl is used, startX would be bigger than endX
                    if ( spanStartX > spanEndX )
                    {
                        spanWidth = spanStartX - spanEndX;
                        spanLeftX = spanEndX;
                    }

                    if ( spanWidth > 1 )
                    {
                        var rectangle = new Rectangle( spanLeftX + padLeft, bounds.Top + padTop, spanWidth, lineHeight );
                        spanRectangles.Add( rectangle );
                    }
                }

                ( ( ISpatialElement ) span ).Region = ( ( Region ) _regionCtor.Invoke( new object[] { spanRectangles.ToList() } ) ).Inflate( 10 );
            }
        }

        #endregion
    }

@dhanaraj94
Copy link

@IeuanWalker I had to rip out a few unrelated things as we already had some other custom stuff in our renderer, but this should be the related bits. Note that this is built against Xamarin 5.0.0.1931. If you try to target any other version you might need to make sure the reflected items still exist.

Expand for code

    /// <summary>
    /// Custom <see cref="Label"/> renderer to standardize the rendering of labels.
    /// </summary>
    /// <seealso cref="Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer" />
    public class LabelRenderer : Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LabelRenderer"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        public LabelRenderer( Context context )
            : base( context )
        {
        }

        #region Temporary Fix for issue #11657

        // This code can be removed when https://github.com/xamarin/Xamarin.Forms/pull/13348 is merged.

        FieldInfo _spannableStringField = typeof( Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer )
            .GetField( "_spannableString", BindingFlags.NonPublic | BindingFlags.Instance );

        private static readonly ConstructorInfo _regionCtor = typeof( Region )
            .GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[1] { typeof( IList<Rectangle> ) }, null );

        protected override void OnLayout( bool changed, int left, int top, int right, int bottom )
        {
            base.OnLayout( changed, left, top, right, bottom );
            RecalculateSpanPositionsFixed( this, Element, ( SpannableString ) _spannableStringField.GetValue( this ), new SizeRequest( new Size( right - left, bottom - top ) ) );
        }

        private static void RecalculateSpanPositionsFixed( TextView textView, Label element, SpannableString spannableString, SizeRequest finalSize )
        {
            if ( element?.FormattedText?.Spans == null || element.FormattedText.Spans.Count == 0 )
                return;

            var labelWidth = finalSize.Request.Width;
            if ( labelWidth <= 0 || finalSize.Request.Height <= 0 )
                return;

            if ( spannableString == null || spannableString.Handle == IntPtr.Zero/*spannableString.IsDisposed()*/ )
                return;

            var layout = textView.Layout;
            if ( layout == null )
                return;

            int next = 0;
            int count = 0;

            var padding = element.Padding;
            var padLeft = ( int ) textView.Context.ToPixels( padding.Left );
            var padTop = ( int ) textView.Context.ToPixels( padding.Top );

            for ( int i = 0; i < spannableString.Length(); i = next )
            {
                var type = Java.Lang.Class.FromType( typeof( Java.Lang.Object ) );

                var span = element.FormattedText.Spans[count];

                count++;

                if ( string.IsNullOrEmpty( span.Text ) )
                    continue;

                // find the next span
                next = spannableString.NextSpanTransition( i, spannableString.Length(), type );

                // get all spans in the range - Android can have overlapping spans				
                var spans = spannableString.GetSpans( i, next, type );

                var startSpan = spans[0];
                var endSpan = spans[spans.Length - 1];

                var spanStartOffset = spannableString.GetSpanStart( startSpan );
                var spanEndOffset = spannableString.GetSpanEnd( endSpan );

                var spanStartLine = layout.GetLineForOffset( spanStartOffset );
                var spanEndLine = layout.GetLineForOffset( spanEndOffset );

                //go through all lines that are affected by the span and calculate an rectangle for each
                var spanRectangles = new List<Rectangle>();
                for ( var curLine = spanStartLine; curLine <= spanEndLine; curLine++ )
                {
                    global::Android.Graphics.Rect bounds = new global::Android.Graphics.Rect();
                    layout.GetLineBounds( curLine, bounds );
                    var lineHeight = bounds.Height();
                    var lineStartOffset = layout.GetLineStart( curLine );
                    var lineVisibleEndOffset = layout.GetLineVisibleEnd( curLine );

                    var startOffset = ( curLine == spanStartLine ) ? spanStartOffset : lineStartOffset;
                    var spanStartX = ( int ) layout.GetPrimaryHorizontal( startOffset );

                    var endOffset = ( curLine == spanEndLine ) ? spanEndOffset : lineVisibleEndOffset; ;
                    var spanEndX = ( int ) layout.GetSecondaryHorizontal( endOffset );

                    var spanWidth = spanEndX - spanStartX;
                    var spanLeftX = spanStartX;
                    //if rtl is used, startX would be bigger than endX
                    if ( spanStartX > spanEndX )
                    {
                        spanWidth = spanStartX - spanEndX;
                        spanLeftX = spanEndX;
                    }

                    if ( spanWidth > 1 )
                    {
                        var rectangle = new Rectangle( spanLeftX + padLeft, bounds.Top + padTop, spanWidth, lineHeight );
                        spanRectangles.Add( rectangle );
                    }
                }

                ( ( ISpatialElement ) span ).Region = ( ( Region ) _regionCtor.Invoke( new object[] { spanRectangles.ToList() } ) ).Inflate( 10 );
            }
        }

        #endregion
    }

@cabal95 I have tried to add the custom renderer in my android project not sure why it is not working. I have added the above renderer file and add assembly as below

[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]

@cabal95
Copy link

cabal95 commented May 24, 2021

@cabal95 I have tried to add the custom renderer in my android project not sure why it is not working. I have added the above renderer file and add assembly as below

[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]

Depending on your using statements, it might be targetting the original Xamarin.Forms LabelRenderer. Try fully qualifying the ExportRenderer such as (as a habit I always do it this way because I've had this bite me a few times):

[assembly: ExportRenderer(typeof(Label), typeof(MyProject.Droid.LabelRenderer))]

and see if that makes a difference. If not, try setting a breakpoint in the constructor to make sure it is being called, then also in the other methods to make sure they get called when expected.

@dhanaraj94
Copy link

@cabal95 I have tried to add the custom renderer in my android project not sure why it is not working. I have added the above renderer file and add assembly as below
[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]

Depending on your using statements, it might be targetting the original Xamarin.Forms LabelRenderer. Try fully qualifying the ExportRenderer such as (as a habit I always do it this way because I've had this bite me a few times):

[assembly: ExportRenderer(typeof(Label), typeof(MyProject.Droid.LabelRenderer))]

and see if that makes a difference. If not, try setting a breakpoint in the constructor to make sure it is being called, then also in the other methods to make sure they get called when expected.

Thanks @cabal95 it referred the default renderer. I have changed it.

@rmarinho
Copy link
Member

rmarinho commented Jul 7, 2021

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rmarinho rmarinho merged commit ca19c01 into xamarin:5.0.0 Jul 7, 2021
rmarinho added a commit that referenced this pull request Sep 20, 2021
* Fix 4143: improved Span region calculation (#13348) fixes #4143 fixes #6992 fixes #11650 fixes #7655 fixes #11657 fixes #10520 fixes #4829 

* improved span region calculation on android

* Remove unused code

* Nit: clean up comments

* Wrap in ScrollView for small screen compatibility

Co-authored-by: Tim Dittmar <Tim.Dittmar@Exxeta.com>
Co-authored-by: Rachel Kang <rachel.j.kang@gmail.com>
Co-authored-by: Rachel Kang <rachelkang@microsoft.com>

* Only check for netfx when not in a wapproj (#14402) fixes #13957

* [macOS] Update Switch renderer (#14334) fixes #14313

* [macOS] Update Switch renderer #14313

* [macOS] Update Switch renderer #14313

* [Android] Fix to Issue Java.Lang.IndexOutOfBoundsException: setSpan (#12764) Fix to Issue #12762

* Update EntryRenderer.cs

Fix to Issue #12762
Java.Lang.IndexOutOfBoundsException: setSpan (nn ... nn) ends beyond length n-1

* Fix to EntryRenderer.cs

* Update EntryRenderer.cs

* Update EntryRenderer.cs

Co-authored-by: Rui Marinho <me@ruimarinho.net>

* [Android] Fix ClearButton not working when changing the ClearButtonVisibility while the Entry field is focused (#13820) Fixes #13819

Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com>

* Automated dotnet-format update (#14404)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Automated dotnet-format update (#14405)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix memory leak (and some crashes) in ItemsViewController (iOS) (#14111)

* add unit test

* fix memory leak (and some crashes) in ItemsViewController (iOS)

* Update ItemsViewController.cs

Co-authored-by: Rui Marinho <me@ruimarinho.net>

* Enable ScrollTo tests for Android, UWP; implement ScrollTo with group index for Android (#13007)

* Remove inaccurate category

* Enable ScrollTo tests for Android/UWP; Implement grouped ScrollTo for Android

* Implement missing ScrollTo grouped item by index

* Exempt 3788 test from UWP

* Ignore test on UWP

Co-authored-by: Rui Marinho <me@ruimarinho.net>

* Fix type for case blueviolet (#14434)

The 'v' in this case was uppercase causing this typeconverter to reject BlueViolet as a valid color value. Fixes #AB1286706 [XVS]XLS0431: Invalid value for property 'BackgroundColor': 'BlueViolet'

* [iOS] Fix: CollectionView was not updating when it was invisible (#14384)

* [iOS] Fix: CollectionView was not updating when it was invisible

* [iOS] Fix: NSInternalInconsistencyException Reason: request for number of items in section X when there are only X sections in the collection view when NumberOfItemsInSection is invoked

* Revert "[iOS] Fix: NSInternalInconsistencyException Reason: request for number of items in section X when there are only X sections in the collection view when NumberOfItemsInSection is invoked"

This reverts commit 2343f15.

* [iOS] Fix: Prevent NumberOfItemsInSection invoking if CollectionView is hidded to avoid  "NSInternalInconsistencyException Reason: request for number of items in section X when there are only X sections"

* [iOS] Scroll locked issue using SwipeView (#12758)

* Fixed issue re-enabling the scroll

* Added repro sample

* Fixed also the swipe sensitive issue on iOS

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Fix removed shadow in Thumb setting a custom color (#13166)

* [iOS] Fix crash/closing wrong modal with FormSheet and tap outside (#14527)

* Add repro + fix

* Update Issue12300.cs

* [Core] SwipeItem Parent using SwipeView in DataTemplate (#13385)

* Added repro sample

* Fix the issue

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Fix Frame Background issue with transparent color (#14565)

* Fix broken disabled Button visual state in UWP (#14567)

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Update Device.Idiom when flipping tablet mode state (#13150)

* [Android] Fix occasional wrong touch interception in SwipeView Content  (#13732)

* Fix wrong touch interception in SwipeView Content on Android

* Updated swipe delta

* Fix Entry issue using TextColor and ClearButton in iOS < 13 (#14566)

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Fix issue using FormattedString and LineBreakMode on iOS (#14572)

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* Align BarBackground behavior between Android and iOS. (#13503)

* Automated dotnet-format update (#14570)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [Android] Update gradients based on offset changes in Frame and BoxView (#11812)

* Update gradients based on offset changes in Frame and BoxView on Android

* Updated the sample to test also the BoxView Brush offsets

* Fix NRE in ListView Command validation (#14580)

* [iOS] Fix inability to check an initially disabled RadioButton after enabling it (#14545)

* Listen for updates to IsEnabled on RadioButton to ensure that the tap gesture recogniser is added when the control is enabled

* Update Issue14544.cs

* Update test case

Co-authored-by: Gerald Versluis <gerald@verslu.is>

* Fix crash navigating in Shell (#14577)

* Androidx bumps (#14506)

* Update to Latest Android X Libraries

* - fix android forwarders

* Moar updates

Co-authored-by: Gerald Versluis <gerald@verslu.is>

* Validate issue 14433 (#14576)

* Added repro sample

* Updated sample

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>

* [iOS] Remove usage of System.Drawing types (#14571)

* Run nightly also for 5.0.0 branch

* Automated dotnet-format update (#14581)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Ordering children while adding (#8231)

* Ordering children while adding

* Abstract OrderElement

* Re-order on runtime child addition and Tests

* Runtime addition ensures Z Index

* Stop performance measurement on return

* Update VisualElementPackager.cs

Co-authored-by: Gerald Versluis <gerald@verslu.is>
Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com>

* If stroke is null avoid render a shape stroke (#14587)

* Fix broken Android platform tests (#14590)

* Automated dotnet-format update (#14595)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [macOS] Fix RadioButton activated behaviour (#14139)

* Respond to Activated event

* Set initial control state based on element state

* Revert "Set initial control state based on element state"

This reverts commit 1bd1238.

* CollectionView RemainingItemsThreshold implementation for UWP (#14202)

* [Android] Fix building with Android stable bits (#14608)

* Set java sdk path

* Use class-parse

Co-authored-by: Braini01 <T_Dittmar@web.de>
Co-authored-by: Tim Dittmar <Tim.Dittmar@Exxeta.com>
Co-authored-by: Rachel Kang <rachel.j.kang@gmail.com>
Co-authored-by: Rachel Kang <rachelkang@microsoft.com>
Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
Co-authored-by: MH Rastegari <42671084+mhrastegary77@users.noreply.github.com>
Co-authored-by: Felipe Silveira <felipe@transis.com.br>
Co-authored-by: Rui Marinho <me@ruimarinho.net>
Co-authored-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Thomas Mijieux <tmijieux@users.noreply.github.com>
Co-authored-by: E.Z. Hart <hartez@users.noreply.github.com>
Co-authored-by: Marco Goertz <mgoertz@microsoft.com>
Co-authored-by: Kirill <rotorgames@bk.ru>
Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
Co-authored-by: Lee Millward <lee.millward@gmail.com>
Co-authored-by: Gerald Versluis <gerald@verslu.is>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
Co-authored-by: Filip Navara <filip.navara@gmail.com>
Co-authored-by: Shanmukha Ranganath <shanmukharanganath@gmail.com>
Co-authored-by: Julio César Rocha <JunielKatarn@users.noreply.github.com>
Co-authored-by: nk221 <kirill.n@gmail.com>
rotorgames pushed a commit to rotorgames/Xamarin.Forms that referenced this pull request Aug 19, 2022
…rin#4143 fixes xamarin#6992 fixes xamarin#11650 fixes xamarin#7655 fixes xamarin#11657 fixes xamarin#10520 fixes xamarin#4829

* improved span region calculation on android

* Remove unused code

* Nit: clean up comments

* Wrap in ScrollView for small screen compatibility

Co-authored-by: Tim Dittmar <Tim.Dittmar@Exxeta.com>
Co-authored-by: Rachel Kang <rachel.j.kang@gmail.com>
Co-authored-by: Rachel Kang <rachelkang@microsoft.com>
@maonaoda maonaoda mentioned this pull request Sep 1, 2022
2 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.