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

[Android] Calling Focus on all Pickers running an API 28 devices no longer opens Picker #5159

Closed
varyamereon opened this issue Feb 7, 2019 · 33 comments

Comments

@varyamereon
Copy link

varyamereon commented Feb 7, 2019

Description

When using API Level 28, calling Focus on a DatePicker with Visible set to false does not show the date picker dialog.

Prior to this PR https://github.com/xamarin/Xamarin.Forms/pull/4344/files#diff-446294d29d78ca8d84e4c0ddc20bbc07L132 the Picker was opened as part of the FocusRequestedEvent. PR #4344 brought DatePicker inline with the other pickers by changing it to trigger a Click on the control which then would trigger the click listener to open the picker.

On API 28 FocusRequestedEvent is still called so there is still an opportunity to react to this. The difference on API 28 is that the Focus request fails so the Click Listener never actually fires.

Possible fix ideas

Steps to Reproduce

  1. Add a date picker and a button on a Forms page. Set the visibility of the date picker to false
  2. Use the button to call Focus on the date picker.
  3. On API 27 and lower the dialog is shown. Not on API 28.

Expected Behavior

DatePicker dialog is shown.

Actual Behavior

Nothing happens.

Basic Information

  • Version with issue: 4.0.0.135214-pre4
  • Last known good version: Unknown
  • IDE: Visual Studio 2019 Windows
  • Platform Target Frameworks:
    • Android: API 28
  • Android Support Library Version:
  • Nuget Packages:
  • Affected Devices: Android

Screenshots

n/a

Reproduction Link

Reproduction

@PureWeen PureWeen self-assigned this Feb 7, 2019
@samhouts
Copy link
Member

@PureWeen Are you looking at this one?

@PureWeen
Copy link
Contributor

@varyamereon
I couldn't get your sample to run.

I've attached a quick sample based on the one you linked that works for me.

Can you try it?

DatePickerPopup.zip

@PureWeen PureWeen added the s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. label Feb 12, 2019
@varyamereon
Copy link
Author

varyamereon commented Feb 13, 2019

@PureWeen
Interesting, I have downloaded your sample and find the same problem on my end. Building against API 28 clicking the button will not show the DatePicker, building against API 27 it works. I am running the sample on an Android Emulator running Android Pie.

EDIT
Same happens when applying to a real device running Android Pie.

@NickCullen
Copy link

Anyone found a solution to this? Currently experiencing the exact same issue when compiling against Android 28 (Pie).

On devices that are 8.1 and lower (using the same APK built against Android 28), the date time pickers work. Only happens when running on an Android 9.0 device.

@NickCullen
Copy link

I just did a bit of testing.

In the start, our xaml looks like this (notice, they start off invisible):

<DatePicker x:Name="accidentDatePicker" Date="{Binding ClaimDate}" IsVisible="False" />
<TimePicker x:Name="accidentTimePicker" Time="{Binding ClaimTime}" IsVisible="False" />

Our code was just calling ".Focus()" on one of these elements (depending on the button you click, of course).

       private void EditDateButton_Clicked(object sender, EventArgs e)
       {
           this.accidentDatePicker.Focus();
       }

If I change my code to the following, the date picker input box appears (we don't want this to happen - all we want to happen is to bring up the date/time picker dialog). BUT along with the input field appearing, the date/timer picker dialog DOES appear.

        private void EditDateButton_Clicked(object sender, EventArgs e)
        {
            accidentDatePicker.IsVisible = true;
            this.accidentDatePicker.Focus();
        }

@PureWeen PureWeen added e/3 🕒 3 and removed s/needs-info ❓ A question has been asked that requires an answer before work can continue on this issue. labels Feb 28, 2019
@filmar25
Copy link

The picker not working too, not only DatePicker.
The solution .IsVisible = true not working for me.

@PureWeen
Copy link
Contributor

https://developer.android.com/about/versions/pie/android-9.0-changes-28

Views with 0 area (either a width or a height is 0) are no longer focusable.

Pickers probably should have a ShowDialog method instead of just having to go via focus
Either way we'll have to figure out a way to make Focus() work on API 28 to trigger dialoge

@TobiasRoeddiger
Copy link

Facing the same issue. Any quick workarounds for this at the moment?

@jmbowman1107
Copy link

jmbowman1107 commented Apr 18, 2019

Facing the same issue. Any quick workarounds for this at the moment?

I put the button I am using to activate it, and the picker itself in a grid in the same column and did:

HeightRequest="1" WidthRequest="1" Margin="0" HorizontalOptions="Center" on the picker.

So basically hide the picker behind the button, and it seems to work.

@jmjohnson05
Copy link

Hopefully this can help somebody else. Full disclosure -- it's a hack, but it works. I was able to work around this issue with a custom picker renderer bypassing the default implementation of the OnFocusChangeRequested method. I basically just copied the code that is used to show the dialog from the XF picker renderer. It's not exactly a quick fix if you don't already have a custom renderer for the picker, but it works with API Level 28 and 27.

For reference, take a look at the IPickerRenderer.OnClick method for the Android PickerRenderer:

Here is the relevant code:

IElementController ElementController => Element as IElementController;

protected override void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e)
{
	if (e.Focus)
	{
		SetupPickerDialog();
	}
	else if (pickerDialog != null)
	{
		pickerDialog.Hide();
		ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
		pickerDialog = null;
	}
}

void SetupPickerDialog()
{
	Picker model = Element;

	if (pickerDialog != null)
	{
		return;
	}

	var picker = new NumberPicker(Context);

	if (model.Items != null && model.Items.Any())
	{
		picker.MaxValue = model.Items.Count - 1;
		picker.MinValue = 0;
		picker.SetDisplayedValues(model.Items.ToArray());
		picker.WrapSelectorWheel = false;
		picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
		picker.Value = model.SelectedIndex;
	}

	var layout = new LinearLayout(Context)
	{
		Orientation = Orientation.Vertical
	};

	layout.AddView(picker);

	ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);

	var builder = new AlertDialog.Builder(Context);
	builder.SetView(layout);

	if (!Element.IsSet(Picker.TitleColorProperty))
	{
		builder.SetTitle(model.Title ?? "");
	}

	builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) =>
	{
		ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
		pickerDialog = null;
	});

	builder.SetPositiveButton(global::Android.Resource.String.Ok, (s, a) =>
	{
		ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);

		if (Element != null)
		{
			if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
			{
				Control.Text = model.Items[Element.SelectedIndex];
			}

			ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
		}

		pickerDialog = null;
	});

	pickerDialog = builder.Create();

	pickerDialog.DismissEvent += (sender, args) =>
	{
		ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
	};

	pickerDialog.Show();
}

@Ws16
Copy link

Ws16 commented May 14, 2019

check if the IsEnable property is set to false then the focus() event won't work on api 28, setting it to true or removing it will fix the issue and make the datepicker focus works.

@chrisfoulds
Copy link

chrisfoulds commented Jun 3, 2019

Tried all the workarounds, some work but only once and the custom renderer is overkill for my purposes.
So I basically removed the datepicker from my xaml and make a new one on each button press now. Works on Droid, iOS and Windows on all versions tested and is simpler than making new renderer.


 private void Date_Clicked(object sender, EventArgs e)
        {
            currentPicker = new DatePicker
            {
                IsEnabled=true,
                HeightRequest=1,
                BackgroundColor=Color.Transparent,
                TextColor =Color.White,
                FontSize=26,
                Margin = new Thickness(5)
            };
            currentPicker.Date = selectedDate;

            currentPicker.DateSelected += DatePicker_DateSelected;
            mainGrid.Children.Insert(0, currentPicker);
            currentPicker.Focus();
        }

        private void DatePicker_DateSelected(object sender, DateChangedEventArgs e)
        {
            selectedDate = currentPicker.Date;
            mainGrid.Children.Remove(currentPicker);
            currentPicker = null;
            UpdateDisplay();
        }

@jonathanantoine
Copy link

@chrisfoulds sorry it did not work for me either. The only workaround I've found is using a custom renderer (quite easy to write in fact) following these instructions
: #5433 (comment)

@chrisfoulds
Copy link

The solution I posted above works great for me, in a product app now with 20k DAU's and no complaints so surprised you had to go down the custom renderer route.
Thanks for sharing though.

@jonathanantoine
Copy link

@chrisfoulds I am using it in a "modal page", that's maybe the issue ?

@chrisfoulds
Copy link

@chrisfoulds I am using it in a "modal page", that's maybe the issue ?

@jonathanantoine maybe , I just checked I am using it on a standard navigation page which may make all the difference.

@kingces95 kingces95 removed their assignment Aug 22, 2019
@PureWeen PureWeen assigned jfversluis and unassigned paymicro Aug 23, 2019
@PureWeen PureWeen changed the title [Android] DatePicker Focus event broken when targeting API Level 28 [Android] Calling Focus on all Pickers running an API 28 device no longer opens Picker Aug 23, 2019
@PureWeen PureWeen changed the title [Android] Calling Focus on all Pickers running an API 28 device no longer opens Picker [Android] Calling Focus on all Pickers running an API 28 devices no longer opens Picker Aug 23, 2019
@jfversluis
Copy link
Member

jfversluis commented Aug 27, 2019

So, if I have reproduced this correctly in the Gallery app, it's not so much a problem of receiving focus. That seems to still work, but the TextView is no longer clickable when it's not visible.

I have tried both CallOnClick and PerformClick but both do not seem to execute when the control is not visible. I have created a PR that now checks if the control is Clickable and if not, it will not call the click method, but it will go directly into the logic to show the dialog. Basically the change @PureWeen highlighted with this link a few comments earlier.

This seems to work in all cases I have tried so far, at the time of writing the UI tests are still running.

In any case, if this works, I don't really see the need to keep the IPickerRenderer.OnClick() implementation that was implemented as part of #4344. I'm assuming that was done for a reason. With the logic implemented in #7289 it should now respond the same way as it does today when the control is visible and if the control is not visible, we skip the click and show the dialog directly. The exact same logic is executed for both paths.

I'm curious to hear any of your thoughts about this.

Edit: this fix does not seem to work for Picker for some reason...

@svaldetero-envoc
Copy link

My picker was hidden behind another view so setting the IsVisible workaround didn't work for me. This line in constructor of the Page.xaml.cs worked for me:

if (Device.RuntimePlatform == Device.Android && 
    Xamarin.Essentials.DeviceInfo.Version.Major >= 9.0)
{ 
    picker.HeightRequest = picker.WidthRequest = 1; 
}

@Pastajello
Copy link

I've used the workaround from @truXton222 but now when I press backbutton on the device the datepicker dialog shown again instead of view going back :|

@jfversluis
Copy link
Member

jfversluis commented Sep 17, 2019

@Miksier that is something that is something described in #7289 and also fixed with #7311. I think we're close to merging, thank you for your patience!

@larsduewel
Copy link

@jfversluis would be great!

@samhouts
Copy link
Member

samhouts commented Oct 7, 2019

closed by #7289

@samhouts samhouts closed this as completed Oct 7, 2019
@gilles-leblanc
Copy link

Same issue,
for my work around

 <ContentView Grid.Row="1" Grid.Column="0">
                            <ContentView.GestureRecognizers>
                                <TapGestureRecognizer Tapped="DocDatePickerTapGestureRecognizer_Tapped"/>
                            </ContentView.GestureRecognizers>
                            <Grid>
                                <material:MaterialDatePicker x:Name="DocDate_Entry" AccentColor="{StaticResource Key=AccentColor}" InputTransparent="True"
                                                Placeholder="{language:TranslateExtension Text=DocDateTitle_str}" PropertyChanged="DocDate_Entry_PropertyChanged"/>
                                <DatePicker x:Name="DocDatePicker" HeightRequest="1" WidthRequest="1" Margin="0" HorizontalOptions="Center" PropertyChanged="DocDatePicker_PropertyChanged"
                                            TextColor="Transparent"/>
                            </Grid>
                        </ContentView>

Put the control in the same grid. set datepicker textcolor to transparent

This was the only work around which works for me sadly it only works the first time. After dismissing the picker I can't seem to bring it up anymore unless I switch to another views and later come back to the view with the picker.

@jfversluis
Copy link
Member

jfversluis commented Oct 14, 2019

@gilles-leblanc did you try the latest 4.2 version (or 4.3-pre) the fix for this should be incorporated in there? If you still find that it's not working, please open a new issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests