3

I am trying to avoid repeated touches, but want a sensitive screen. Using the method advised in this answer:

Android Preventing Double Click On A Button

I have been re-enabling buttons within the program flow, but there are some instances where, I cannot rely on program flow and need to ensure the button is enabled.

I have devised a way to re-enable these button using a countdowntimer and shown in my code:

button.setOnTouchListener(new View.OnTouchListener() {
        @Override public boolean onTouch(View v, MotionEvent event) {
            disableButton(button);
            countDwn();
            // Do something
            return false;
        }
    });

public void disableButton(Button button) {
    button.setEnabled(false);
}

public void enableButton(Button button) {
    button.setEnabled(true);
}

public void countDwn() {
    CountDownTimer countDownTimer = new CountDownTimer(2000, 1000) {
        public void onTick(long millisUntilFinished) {
        }

        public void onFinish() {
            enableButton(button);
        }
    }.start();
}

What I am concerned about, is that this may be a clumsy or ineffective way to go about this. I am wanting advice about this and if anyone has a more elegant suggestion?

Community
  • 1
  • 1
  • Why are you setting a count down timer for re-enabling the button? You could instead finish your tasks and at the end of task enable the button. – rusted brain Jul 18 '15 at 12:34
  • 1
    All of the operations are being inside the onTouch() method hence when one touches the button it would be delayed for the certain time. Instead you can toggle a boolean variable and check for that to enable/disable the timer. – chitraketu Pandey Jul 18 '15 at 12:38
  • Yup, you can add a boolean flag and when you disable button, make that flag 0 and vice versa. – rusted brain Jul 18 '15 at 12:40

2 Answers2

2

I am not sure setting a button to enable=false is the correct way to solve the "repeated touches" issue.

The main reason for that is since when a button is enable=false, most of the time it will have a Disabled graphics assigned to it. Since we only want to prevent accidental repeated touches, and not to invoke the disable graphics, I am not sure this is the correct solution.

Prevent Repeated Touches

I will suggest a simpler solution, prevent the action if the time from last action is less than MINIMUM_ACTION_DELAY.

If you want to get the click animation, prevent the action on the onClick listener. If you don't want the click animation, prevent the action on the onTouch.

For example, onClick will be something like this:

button.setOnClickListener(new View.OnClickListener() {

    private long mLastActionTime;

    @Override
    public void onClick(View v) {
        long currentTime = System.currentTimeMillis();
        if (currentTime - mLastActionTime < MINIMUM_ACTION_DELAY) {
            // Too soon, we don't want to handle this event
            return;
        }

        // Save the action time
        mLastActionTime = currentTime;

        // Execute action
        // TODO do something
    }
});
Eyal Biran
  • 5,656
  • 1
  • 28
  • 29
  • No need, since mLastActionTime is initialized to 0 the condition of the minimum time will always not hold for the first time. Try it :) – Eyal Biran Jul 18 '15 at 13:17
  • Indeed, in Java all variables are initialized to default values. For primitive types (int, long, etc) it will be 0. For objects it will be NULL. – Eyal Biran Jul 18 '15 at 13:20
2

Another solution is to create a custom Button, that you can use all times you want, without rewrite the timer manager. Can be something like this:

    public class OneTimeButton extends Button {
    private int timoeut;
    private CountDownTimer timer = null;


    @Override
    public boolean performClick() {
        boolean result = super.performClick();

        if(timer!=null && timoeut > 0){
            setEnabled(false);
            timer.start();
        }

        return result;
    }

    public OneTimeButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.OneTimeButton,
                0, 0);

        try {
            timoeut = a.getInteger(R.styleable.OneTimeButton_timeout, 0);
            setTimer();
        } finally {
            a.recycle();
        }
    }

    private void setTimer() {
        timer = new CountDownTimer(timoeut, timoeut) {
            @Override
            public void onTick(long millisUntilFinished) {}

            @Override
            public void onFinish() {
                setEnabled(true);
            }
        };
    }

    public OneTimeButton(Context context) {
        super(context);
    }

    public OneTimeButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

Define an attrs.xml file in \values folder:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="OneTimeButton">
        <attr name="timeout" format="integer" />
    </declare-styleable>
</resources>

Then, simply in your XML call

<it.your_package_name.OneTimeButton 
        android:id="@+id/test_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TEST"
        custom:timeout="5000"/>

You can assign the timeout you want (in milliseconds) to custom:timeout attribute.

Remember to declare in your layout (at the top level ViewGroup) this:

xmlns:custom="http://schemas.android.com/apk/res-auto"
GVillani82
  • 17,196
  • 30
  • 105
  • 172
  • Uhm, if you need to execute an operation that continue outside of the component lifecycle (e.g. Activity Lifecycle) maybe you need another component, like Service (that continue all work in background). Can be the case? – GVillani82 Jul 18 '15 at 13:21
  • Yes, the solution I proposed in fact, manage correctly that behaviors. If the user presses back when you return on the activity you can see the button enabled. Right? – GVillani82 Jul 18 '15 at 13:28
  • @Heyyou I have added edited my answer, so you can customize the timeout for each different `OneTimeButton` declared in your layout, simply setting the custom attribute `custom:timeout` – GVillani82 Jul 18 '15 at 13:44