Entry cell loses focus on button press in android but not iOS , work around?

VasbiVasbi USUniversity ✭✭

I have some code in iOS where I have buttons that act like a calculator keyboard when pressed on it fills a entry field that is focused.

Works great for iOS but on android the button press even causes the entry to lose focus. I have tried to set a custom renderer for buttons and focus to false however it doesn't fix the problem.

The reason I am using this method of custom number entry is because iOS and android doesn't have a decent numeric keyboard I need on all device types and sizes.

Can anyone give me a hint of a work around?

Best Answers

Answers

  • adamkempadamkemp USInsider, Developer Group Leader mod

    It wasn't clear from your post, but did you set the Focusable property to false?

  • VasbiVasbi USUniversity ✭✭

    Yes I sent the focusable buttons to false.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Could you attach an example project showing exactly what you're trying?

  • VasbiVasbi USUniversity ✭✭
    edited November 2015

    Thanks for taking your time to look at my issue

    I created a quick example and attached it.
    Middle of the screen has a Entry with a Button below it, both don't do anything besides demonstrate my problem.

    Entry has a custom renderer that disables the soft keyboard from appearing, Button has a Custom Renderer that sets focusable and focusableOnTouch to false.

    Select the Entry, then press the button and you can see the Entry loses focus, I want the EntryCell to keep focus.

    In my actual project the work around of attaching to the Entry UnFocused event and setting it back again isn't an option due to there being two Entrys on the page, and when testing this type of method the keyboard kept appearing even though the CustomRenderer is set to not allow this.

  • VasbiVasbi USUniversity ✭✭

    @adamkemp

    Wow, Thanks for you help you have really good knowledge of this stuff and I appreciate the time you took to look into it.

    For anyone that comes across this thread the answer I marked work perfectly, I agree on using the more restrictive version though.

    The only issue I am having now is when I force text on the entry with Entry.Text = "1" for example the soft Keyboard appears, which is weird since it doesn't appear on focus. If I find a solution to this I will repost in case anyone else wanted this solution for the same reason as I did.

  • VasbiVasbi USUniversity ✭✭

    @adamkemp

    Perfect... thanks again you saved me a day of frustation as the problem was in their code.

    How do you get access to the code? I would prefer being able to fix these issues myself then posting on the forums. Are you using a decompiler? or is this layer of their code open to the community?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Xamarin Studio has a built in Assembly Browser that decompiles the IL. Just double click the assembly reference or jump to definition on something that comes from the Forms assembly. Then change the drop downs at the top of the tab to show all members (not just public) and change the language to C#.

  • Jeff451Jeff451 ITMember ✭✭

    @adamkemp This is great, thanks!

    Is it possible in your opinion to understand what is the element being touched/clicked in DispatchTouchEvent?

    I'd like to keep the keyboard open only when certain buttons are clicked, but keep the default behaviour (hiding the keyboard) in all the other cases.

    Thanks again

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Is it possible in your opinion to understand what is the element being touched/clicked in DispatchTouchEvent?

    There is no well-defined concept of a hit test in the Android framework so you can't easily answer the question "what is the view that would receive a touch at this location?". You can try to implement your own kind of hit testing by just recursively walking through the view hierarchy looking for the innermost view that contains that point, but that's not easy to do correctly, and it's not entirely accurate. For instance, a circular button may only respond to touches within the visible circle, but the view itself is always rectangular. There's no way for you to know in which areas that view will respond to touches without actually sending it a touch and seeing how it responds.

    This gesture to clear focus is IMO one of the dumbest "features" of Xamarin.Forms, and I've lost count of how many posts on the forums I answered that ended up being explained by this behavior. Now that it's open source you could try submitting a pull request to disable the feature (optionally?) or improve it. I'm no longer in a position to do that. The code you're looking for is here.

  • FrancoisMFrancoisM FRUniversity ✭✭

    Thanks Adam!
    Here is my version. I add my entry/editor and button into a custom StackLayout called EntryStackLayout.

    private bool _ignoreNewFocus;
            public override bool DispatchTouchEvent(MotionEvent e)
            {
                var currentView = CurrentFocus;
                var parent = currentView?.Parent?.Parent;
                var entryStackLayout = parent as EntryStackLayout;
                if (entryStackLayout != null)
                {
                    var entryLayoutLocation = new int[2];
                    entryStackLayout.GetLocationOnScreen(entryLayoutLocation);
                    var x = e.RawX + entryStackLayout.Left - entryLayoutLocation[0];
                    var y = e.RawY + entryStackLayout.Top - entryLayoutLocation[1];
                    var entryStackLayoutRect = new Rectangle(entryStackLayout.Left, entryStackLayout.Top, entryStackLayout.Width, entryStackLayout.Height);
                    _ignoreNewFocus = entryStackLayoutRect.Contains(x, y);
                }
                var result = base.DispatchTouchEvent(e);
                _ignoreNewFocus = false;
                return result;
            }
    
            public override Android.Views.View CurrentFocus => _ignoreNewFocus ? null : base.CurrentFocus;
    
  • VinayakGawasVinayakGawas USMember ✭✭✭

    @Vasbi Heyyy.
    How did u do that in iOS can you please share your code.

  • BhaurajBiradar.9064BhaurajBiradar.9064 USMember ✭✭✭

    Hi, Any solution for iOS?

  • BhaurajBiradar.9064BhaurajBiradar.9064 USMember ✭✭✭

    Hi, I fixed the issue with HitTest in iOS.. Everything working fine :)

  • AwodamAwodam ATMember ✭✭

    @BhaurajBiradar.9064 can you share your iOS HitTest ?

  • BhaurajBiradar.9064BhaurajBiradar.9064 USMember ✭✭✭
    edited August 2017

    Please remember 3 steps to achieve this
    1. Entry or Editor on focus out
    2. Identifying Button Click
    3. Event fire on Button Click
    Unfortunately we can't achieve above 3 because of Xamarin Forms issues.

    We can achieve all 3 in below 2 steps

    Step 1. Entry or Editor on focus out - with OnElementPropertyChanged method

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
        if (e.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
        {
            if (Control != null)
            {
                Control.ShouldEndEditing =
                (UITextField textField) =>
                {
                    Control.ResignFirstResponder();
                    // first check button is clicked? from step 2
                    //do coding here for button click fire event
                    return true;
                };
            }
        }
        base.OnElementPropertyChanged(sender, e);
        }
    

    Step 2. Identifying Button Click
    Create custom stack layout and render it from iOS project. Keep all elements(Your button and entry or editor) in custom layout.

    In custom stack layout override method HitTest() --> check here button clicked or not.
    If button clicked set a flag

  • abdullahtahan.7433abdullahtahan.7433 SAMember ✭✭
    edited November 2017

    thank you guys

  • DerProgrammiererDerProgrammierer DEMember ✭✭✭

    @adamkemp Thanks for your code. I tried your solution, but it doesn't work for me. It doesn't even jump into DispatchTouchEvent() method if I set a breakpoint.

    Do you have any idea why this could be the case?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    This post is 2 years old. I haven't done any Android programming at all since then, nor have I used any Xamarin products since almost that long ago. If this hack doesn't work anymore then unfortunately I can't help you. The good news is that Xamarin.Forms is open source now so you can go look at exactly what they're doing and try to figure it out and fix it or come up with a better hack. I'm afraid I don't remember enough to teach you how touch event handling works on Android so you're on your own to figure that out. Sorry. Good luck!

  • @BhaurajBiradar.9064 Could you please explain your solution a bit more? how am I supposed to check if my button is clicked in the customEntryRendere??!
    have the same problem on iOS
    has anybody come up with another solution?

  • DerProgrammiererDerProgrammierer DEMember ✭✭✭
    edited January 2018

    @DerProgrammierer said:
    @adamkemp Thanks for your code. I tried your solution, but it doesn't work for me. It doesn't even jump into DispatchTouchEvent() method if I set a breakpoint.

    Do you have any idea why this could be the case?

    I solved the problem. It was one of the few occasions where the code I wrote didn't end up being in my app until I manually uninstall the app from the device and build it again.

    Thanks adamkemp!

  • pythpyth Member ✭✭

    I can confirm the DispatchTouchEvent trick works perfectly.
    I used it for a chat application where the keyboard was hiding after the user clicked the send button.

    To restrict that behaviour to just the chat entry, I applied a "AutomationId" to the Entry into my Xamarin.forms XAML.
    Then my code of DispatchTouchEvent is the following :

    private bool _ignoreNewFocus;
    public override bool DispatchTouchEvent(MotionEvent ev)
    {
        var focused = CurrentFocus;
        _ignoreNewFocus = (focused?.Parent is EntryRenderer entryRenderer && entryRenderer.Element.AutomationId == "ChatMessageEntry");
        var result = base.DispatchTouchEvent(ev);
        _ignoreNewFocus = false;
        return result;
    }
    

    Thanks a lot.

  • pythpyth Member ✭✭
    edited November 2018

    For iOS, this is what I've done, it works great. Thanks @BhaurajBiradar.9064 !

    public class IosEntryRenderer : EntryRenderer
        {
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if( e.PropertyName == VisualElement.IsFocusedProperty.PropertyName )
                {
                    if( sender is Entry entry && entry.AutomationId == "ChatMessageEntry" )
                    {
                    if( IsOnChatView )
                    {
                        Control.BecomeFirstResponder();
                        return false;
                    }
                    else return true;
                    }
                }
                base.OnElementPropertyChanged(sender, e);
            }
        }
    
  • UnreachableCodeUnreachableCode USMember ✭✭✭

    @Jeff451 said:
    @adamkemp This is great, thanks!

    Is it possible in your opinion to understand what is the element being touched/clicked in DispatchTouchEvent?

    I'd like to keep the keyboard open only when certain buttons are clicked, but keep the default behaviour (hiding the keyboard) in all the other cases.

    Thanks again

    I'm trying to work this out myself, right now. There must be an easy way.

Sign In or Register to comment.