Android doesn’t provide any easy to use native API for getting touch held down callbacks continuously. So here is my approach to the problem.

Since Android lets us handle incoming touch events on a view we can easily adopt OnTouchListener for our use case.

Before getting straight into implementation lets list some possible requirements that developers need.

Adjustable starting delay

Before handling continuous held down events, there should be a starting delay between the first touch down and touch up. By default for long presses, Android uses 500ms of delay so this will be our default as well.

Adjustable polling rate

While handling continuous events developers may need to limit the time window between each callback. For example, they may decide to take callbacks every millisecond or once a second not to interrupt UI thread more than they need. I decided to use 500ms by default for this configuration as well, as it’s a good balance for incrementation of a numerical value.

Excluding view padding

Android continues to deliver touch events inside view paddings but in some cases, developers may need to exclude padding areas. By default, we should continue to give touch held down callbacks inside view’s padding.

Should work along with Click and LongClick listeners

When you set OnTouchListener to a view, it’s a very high probability that you will break the working click and long click listeners. So our implementation should work in parallel with a click and long click listeners without breaking them, as they are also important for Accessibility.

Should handle multiple fingers touch

We should only care about the first finger that touches view.

It should be usable like it was supported natively

Thanks to Kotlin Extensions, for this we will write an extension to the Android View class so developers will use like it’s provided natively.

On return, we should provide a total elapsed time since the first touch.

Implementation

I will just go through the general overview of the implementation since the code itself is pretty self-explanatory.

Since I didn’t want to use any third-party library or coroutine I decided to use old-school Handler’s to delay callbacks according to starting delay and polling rate.

So basically I implemented the OnTouchListener interface in OnTouchHoldListener, get the desired parameters for the requirements described above on a wrapper class TouchHoldConfig and the callback listener on an interface OnTouchHoldCallback as constructor parameters.

To handle default click and long click listeners along with my implementation, I used a workaround and got the default result from the view itself and return each time.

As obvious when the first ACTION_DOWN event occurs we will have to post a runnable to the handler with starting delay. If the touch is held down more then a starting delay we should deliver future callbacks with polling rate.

Also to filter multiple fingers, on the first ACTION_DOWN event we should get the finger id from the motion event’s getPointerId(int) method, set it on a variable to filter any future fingers.

To return the total elapsed time on each callback, we also should start a simple stopwatch on the first ACTION_DOWN event.

Finally, when touch is being moved outside of our interested area or any other canceling events occur, we should cancel delivering updates simply by removing callbacks from our Handler.

As a bonus, I wrote an extension to view class for easy use.

Possible Use Cases

Animations with elapsed time.

Continues incrementation of numerical value.

Continues exponential incrementation of numerical value.

Source Code

There is no doubt that this is not a very complex problem to solve but even simple and core problems can be painful to solve.

If I made your day easier or you’ve learned something new, please don’t forget to clap and star my project on Github 😇