How to stop multiple button presses

My app uses a media player that prepares a stream from online. While it is preparing, if the button is pressed again, the app crashes. I know I had seen a method (thought it was where I found the media player information) that would sleep the UI thread so that multiple presses wouldn't affect the button. My question is, would this be the right time to use this method, or is there a better implementation to fix this?

Best Answer

Answers

  • ImfletcherImfletcher USMember

    Is this a custom button? If so why not use a flag in the onclick event to exit if it is the second click.

  • KlutchSCKlutchSC USMember

    Awesome! Disabling the button works fine.

    However since my idea isn't the right way of going about it, I do have the same problem on the start up "Splash" screen. While the splash is being displayed I have my main webservice pull in a long Json string. If the screen is tapped during this loading period, it also has a tendency to freeze up and crash the app.

    Obviously there is no button to disable to stop this crash, but would this be the correct time to implement the Thread.Sleep() method?

  • rmaciasrmacias USBeta, University ✭✭✭✭✭

    Are you sure it is the tapping that is causing the app to crash? That sounds strange. Lately, I've being more convinced that splash screens are pretty much useless. If you have a long web service call that needs to happen before you can render you UI at start up, I would just show a ProgressDialog. That way all touch is disabled and your users know that something is actually happening.

  • rmaciasrmacias USBeta, University ✭✭✭✭✭

    Also, avoid using Thread.Sleep() for these purposes. The next guy/gal who takes over your code will make fun of you and you'll be very embarrassed.

  • KlutchSCKlutchSC USMember

    haha, thank you for the advice!

    I'll look into the progress bar instead.

  • KlutchSCKlutchSC USMember

    oye!

    So disabling the button after pressing is not working out after all. I am able to still break it the same way but only now it requires a bit faster of a second click. Here is my code snippet:

    {

    playButton.Click += delegate {

                playButton.Enabled = false;
    
                if (!mediaPlayer.IsPlaying) {
    
                    playButton.SetBackgroundResource (Resource.Drawable.StopButtonCustom);
    
                    mediaPlayer.PrepareAsync ();
    
                    mediaPlayer.Prepared += (sender, EventArgs) => mediaPlayer.Start ();
    
                }
    
                else {
    
                    playButton.SetBackgroundResource (Resource.Drawable.PlayButtonCustom);
    
                    mediaPlayer.Stop();
    
                }
    
                playButton.Enabled = true;
    
            };
    

    If I remove the playButton.Enabled = true at the bottom, the button will no longer function and the error is gone. However trying to re-enable the button with it is causing it to continue to crash.

  • KlutchSCKlutchSC USMember

    Solved! I moved the enable to be inside the else block as well as with the delegate for after the player has been prepared and it's working fine!

  • rmaciasrmacias USBeta, University ✭✭✭✭✭

    Yeah, that makes sense because you're trying to update the UI from a non-UI thread (the delegate). You could use

    RunOnUIThread(() => playButton.Enabled = true);
    RunOnUIThread(() => playButton.Enabled = false);
    

    and that should work also.

  • subhadipghoshsubhadipghosh INMember

    Just face same kind of issue . I fixed it, may be useful for some users -
    http://technobelities.blogspot.in/2014/08/how-to-stop-double-tap-or-multi-tap.html

    public class SingleClickListener
    {
    public SingleClickListener(Action<object, EventArgs> setOnClick)
    {
    _setOnClick = setOnClick;
    }
    private bool hasClicked;

        private Action<object, EventArgs> _setOnClick;
    
        public void OnClick(object v, EventArgs e)
        {
            if (!hasClicked)
            {
                _setOnClick(v, e);
                hasClicked = true;
            }
            reset();
        }
    
        private void reset()
        {
            Android.OS.Handler mHandler = new Android.OS.Handler();
            mHandler.PostDelayed(new Action(() => { hasClicked = false; }), 500);
        }
    }
    
  • subhadipghoshsubhadipghosh INMember

    Faced same issue. Fixed it in a different way. May be helpful for few users-
    http://technobelities.blogspot.in/2014/08/how-to-stop-double-tap-or-multi-tap.html

    public class SingleClickListener
    {
    public SingleClickListener(Action<object, EventArgs> setOnClick)
    {
    _setOnClick = setOnClick;
    }
    private bool hasClicked;

        private Action<object, EventArgs> _setOnClick;
    
        public void OnClick(object v, EventArgs e)
        {
            if (!hasClicked)
            {
                _setOnClick(v, e);
                hasClicked = true;
            }
            reset();
        }
    
        private void reset()
        {
            Android.OS.Handler mHandler = new Android.OS.Handler();
            mHandler.PostDelayed(new Action(() => { hasClicked = false; }), 500);
        }
    }
    
  • AmerradiAmerradi USMember, University ✭✭
    edited March 2016

    All above solution does not work with real Android device (might work with emulator though) ,they are all about enabling and disabling the button click ,Android queues the clicks (no matter the button is disabled or not),and disabling the button has no effect on the delivery of queued clicks . the best solution I found is saving the last clicked time and avoiding the other clicks within a defined time (like 1 second):

        //should using Android.OS;
        private long mLastClickTime;
        private void OnButtonClick (object sender, EventArgs ev)
        {
            if (SystemClock.ElapsedRealtime() - mLastClickTime < 1000)
            {
                return;
            }
            mLastClickTime = SystemClock.ElapsedRealtime();
            //Add ur code here
          }
    
  • JoshuaLatusiaJoshuaLatusia USMember ✭✭

    I Solved this problem with all of my events by creating a static class which registers when an event is fired.
    What it does is save the event by a name and the time it fires. And everytime you fire another event it checks if the Event should or should not fire by checking if the event that is firing is on the list.

    /// <summary>
    /// Struct for storing an fired event by identifier and time
    /// </summary>
    public struct DebouncedEvent
    {
        private DateTime LastBounce;
        private string Identifier;
    }
    
    /// <summary>
    /// Limits Events from firing multiple times by tracking last time they were fired
    /// </summary>
    public static class EventDebouncer
    {
        private static List<DebouncedEvent> eventList = new List<DebouncedEvent>();
    
        public static List<DebouncedEvent> EventList { get => eventList; set => eventList = value; }
    
        /// <summary>
        /// Determines if event should be fired or not
        /// </summary>
        /// <param name="id">The identifier of the event.</param>
        /// <returns>If event may be fired</returns>
        public static bool ShouldBounce(string id)
        {
            ClearEventList();
    
            if (EventList.Any(x => x.Identifier.Equals(id)))
            {
                return false;
            }
            else
            {
                EventList.Add(new DebouncedEvent { Identifier = id, LastBounce = DateTime.Now });
                return true;
            }
        }
    
        /// <summary>
        /// Note Replace 2000 by delay in milliSeconds you want.
        /// Removes Event from the list when it can fire again
        /// </summary>
        private static void ClearEventList()
        {
            EventList.RemoveAll(x => (DateTime.Now - x.LastBounce).TotalMilliseconds > 2000);
        }
    }
    

    Then you put this code in the button on click events.

    /// The event you don't want to fire multiple times
    private void SomeClickEvent(object sender, EventArgs e)
    {
        if (!EventDebouncer.ShouldBounce("SomeName"))
            return;
    
        // Do stuff
    }
    

    This will check if the event may be fired or not by looking at the DebouncedEvent list.

Sign In or Register to comment.