dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22k stars 1.72k forks source link

MAUI Picker always open up in Android 8 and below #15394

Open sujith2ss opened 1 year ago

sujith2ss commented 1 year ago

Description

Navigate from one page to another page, MAUI Picker always open up, with out selecting it on Android version 8 and below. Easily reproducible in Android Emulator.

Steps to Reproduce

  1. Clone the repo https://github.com/sujith2ss/test
  2. Build and run it on Android 8 device or emulator
  3. Click on the Next Button on App
  4. See the picker is opened already.

Link to public reproduction project repository

https://github.com/sujith2ss/test

Version with bug

7.0.49

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 8 and below

Did you find any workaround?

No response

Relevant log output

No response

sujith2ss commented 1 year ago

Demo video attached https://github.com/sujith2ss/test/blob/main/demo.mp4

https://github.com/dotnet/maui/assets/135240873/9ffdcd6a-8859-4d3c-9e03-9d9e81ea62f2

gktval commented 1 year ago

I am experiencing this with Android 12+, API versions 32/33. The only difference is that it happens with a TabView and occurs when the tab containing the picker becomes visible on an animation that switches between the tabs. I was able to fix it by removing a line that makes the tab item focused.

sujith2ss commented 1 year ago

@gktval I didn't notice this issue in andorid version higher than 8. In android 8.0 or below if we try focus or unfocus the result are same. In the example code you can see only page navigation to another page, but the picker is getting opened automatically.

AndieH68 commented 1 year ago

Also experiencing this on Android 13 API Version 33.

This occurs if the Picker is the first visible control on a page and the page regains focus from another page or a popup disappearing.

thomasgalliker commented 11 months ago

Yeah, there is some code in the PickerHandler.android.cs that says 'if focused, run click method'. I have no idea why this code is there. For most of the picker related handlers, I had to rewrite them from scratch because they were either not working properly or having memory leaks.

Andy-Donegan commented 10 months ago

Just to add same here as @AndieH68 Android 13 API version 33.

XamlTest commented 10 months ago

Verified this on Visual Studio Enterprise 17.9.0 Preview 1(8.0.3). Repro on Android 8.0-API26, not repro on Android 13.0-API33, Windows 11 with below Project: Sample.zip

ViktorArbuzov commented 8 months ago

any updates?

donatellijl12 commented 7 months ago

I have this issue as well. Whenever navigating to a page with a picker on Android, the picker automatically opens. Is there a workaround other than building a custom control?

Andy-Donegan commented 7 months ago

automatically

Yes there is a work around, I am away for the weekend but will post the work around I used and try to find the link for the fix so others can see it within here. Sorry but can not update until Monday.

donatellijl12 commented 7 months ago

Placing the picker in a scroll view fixed it for me for on initial page navigation, but not whenever I navigate back to the page. I'm interested to see your work around when you are able to share. Thanks!

P.S. I also tried calling unfocus() in OnAppearing and listing the items in the xaml instead of using ItemsSource, neither which fixed the issue.

Andy-Donegan commented 7 months ago

I could not find the reference for how I came to this fix, as I did not create this somebody else figured this out and I have just used it. In your .xaml page add this as the top element so it is target by the bug as the first element.

<!-- element added her for .Net 8 fix to stop popups auto opening on refocus to page.-->                
<Entry 
    x:Name="FakeEntry"
    Margin="0,0,0,0"
    HeightRequest="1"
    MaximumHeightRequest="1"
    TextColor="Black"
    Placeholder=""/>

in your code behind. You need to add the following because the Android Underline is present on all entry elements so the invisible element will still have an underline, below will remove it.


    // Complete below function is required to fix .Net 8 upgrade which breaks the picker class
    // Had to add invisible text input element also and then hide it (not invisible) to stop page focus causing
    // pickers to be opened all the time.
    protected override void OnHandlerChanged()
    {
        base.OnHandlerChanged();
#if ANDROID
        Android.Graphics.Drawables.GradientDrawable gd = new Android.Graphics.Drawables.GradientDrawable();
        gd.SetStroke(0, Android.Graphics.Color.Transparent);
        gd.SetColor(Android.Graphics.Color.Transparent);

        // Invisible Text Entry box for .Net 8 fix for popups auto being called on return to page focus.
        var view = FakeEntry.Handler.PlatformView as AndroidX.AppCompat.Widget.AppCompatEditText;
        view.Background = null;
        view.SetBackgroundColor(Android.Graphics.Color.Transparent);
        view.SetPadding(0, 0, 0, 0);
        view.SetCursorVisible(false);
        view.SetBackground(gd);
#endif
    }

All credit goes to somebody else.
mchiz commented 5 months ago

I created a more generic fix. I have been testing it and it seems to work.

First, you have to create this handler:

    public PickerHandlerFixAndroidFocus( ) {
        _onFocusChangeMethod = typeof( PickerHandler ).GetMethod( "OnFocusChange", BindingFlags.Instance | BindingFlags.NonPublic );
        _onClickMethod = typeof( PickerHandler ).GetMethod( "OnClick", BindingFlags.Instance | BindingFlags.NonPublic );

        System.Diagnostics.Debug.Assert( _onFocusChangeMethod != null && _onClickMethod != null );
    }

    protected override void ConnectHandler( MauiPicker platformView ) {
        base.ConnectHandler( platformView );

        var focusChangeDelegate = ( System.EventHandler< Android.Views.View.FocusChangeEventArgs > )Delegate.CreateDelegate( typeof( System.EventHandler<Android.Views.View.FocusChangeEventArgs> ), this, _onFocusChangeMethod );
        var clickDelegate = ( System.EventHandler )Delegate.CreateDelegate( typeof( System.EventHandler ), this, _onClickMethod );

        platformView.Click -= clickDelegate;
        platformView.FocusChange -= focusChangeDelegate;

        platformView.Click += OnClick;
        platformView.FocusChange += OnFocusChange;
    }

    void OnClick( object? sender, EventArgs e ) {
        var diff = DateTime.Now - _lastFocusTimeStamp;

        if( diff <= _timeToIgnoreClickAfterFocus ) {
            _lastFocusTimeStamp = DateTime.MinValue;
            return;
        }

        _onClickMethod!.Invoke( this, [ sender, e ] );
    }

    void OnFocusChange( object? sender, global::Android.Views.View.FocusChangeEventArgs e ) {
        if( e.HasFocus ) {
            _lastFocusTimeStamp = DateTime.Now;
            return;
        }

        _lastFocusTimeStamp = DateTime.MinValue;

        _onFocusChangeMethod!.Invoke( this, [ sender, e ] );
    }

    DateTime _lastFocusTimeStamp = DateTime.MinValue;
    MethodInfo? _onClickMethod;
    MethodInfo? _onFocusChangeMethod;

    readonly TimeSpan _timeToIgnoreClickAfterFocus = TimeSpan.FromSeconds( 0.1f );
}

And then you just register it in your MauiProgram.cs (Android only):



builder.ConfigureMauiHandlers( cf => {
                #if ANDROID
                cf.AddHandler( typeof( Picker ), typeof( PickerHandlerFixAndroidFocus ) );
                #endif
            } )```
RichardMarsh commented 5 months ago

Also experiencing this on Android 13 API Version 33.

This occurs if the Picker is the first visible control on a page and the page regains focus from another page or a popup disappearing.

I had the same problem when closing a popup. The first control that is focusable would get the focus automatically triggering the OnFocus event.

In my case, I could solve this by adding a positive ZIndex value to the control I didn't want to have autofocus on. Now if I open and close the popup, the focus does not go back to control with the ZIndex.

Focus goes to whichever control has a lower zindex.

No idea why this would work or what the side effects are, but I'm putting this out there in case it can help someone else.

RichardMarsh commented 5 months ago

Also experiencing this on Android 13 API Version 33. This occurs if the Picker is the first visible control on a page and the page regains focus from another page or a popup disappearing.

I had the same problem when closing a popup. The first control that is focusable would get the focus automatically triggering the OnFocus event.

In my case, I could solve this by adding a positive ZIndex value to the control I didn't want to have autofocus on. Now if I open and close the popup, the focus does not go back to control with the ZIndex.

Focus goes to whichever control has a lower zindex.

No idea why this would work or what the side effects are, but I'm putting this out there in case it can help someone else.

Found this work does not work when controls are inside of a ScrollView

mchiz commented 5 months ago

@RichardMarsh, did you try my fix? It is working great for me.

RichardMarsh commented 5 months ago

@RichardMarsh, did you try my fix? It is working great for me.

Going to give it a bash soon

smalgin commented 5 months ago

@mchiz - many thanks, works like a charm!

TKwakernaak commented 3 months ago

@mchiz Thanks for your handler, it's working great

akhilvswoodplc commented 3 months ago

@RichardMarsh, did you try my fix? It is working great for me.

Not working for me. Any changes required?

corne-ac commented 2 months ago

This is still a problem and workarounds are very inconsistent in working.

uhbtl commented 1 month ago

Same problem here. With the workaround the picker doesn´t automatically open anymore. But at the same time I cannot open the picker manually in code with the Focus method anymore.

Andy-Donegan commented 1 month ago

Same problem here. With the workaround the picker doesn´t automatically open anymore. But at the same time I cannot open the picker manually in code with the Focus method anymore.

Read my suggestion before the work around, the original author Deserves Credit but I can not find the link to where I got it from. It is extremely basic but a fake picker invisible removes all of the problems and allows all of your other pickers to operate as normal. Not perfect but resolves your issues hopefully.

thomasgalliker commented 1 month ago

This seems to happen even on latest Android operating system. The steps I was able to reproduce it are:

  1. Create a MainPage and DetailPage. Set MainPage of App to new NavigationPage(new MainPage).
  2. Add an Entry to DetailPage.
  3. On MainPage, place a Picker with some items... Make sure the Picker is the first focussable element in the visual tree.
  4. Navigation to DetailPage.
  5. Set focus to Entry by putting your finger into it.
  6. Navigate back to MainPage.

Actual outcome The Picker is automatically focussed (opened) when we navigate back from DetailPage to MainPage.

Expected outcome The Picker is not opened when we navigate back to MainPage.

uhbtl commented 4 weeks ago

Read my suggestion before the work around, the original author Deserves Credit but I can not find the link to where I got it from. It is extremely basic but a fake picker invisible removes all of the problems and allows all of your other pickers to operate as normal. Not perfect but resolves your issues hopefully.

You´re right, that works. Thank you. Since I dont want to do that on every possible page, do you know a more generic way to implement this workaround?