Android - Hold Button to Repeat Action

android repeat action on pressing and holding a button

There are multiple ways to accomplish this, but a pretty straightforward one would be to post a Runnable on a Handler with a certain delay. In it's most basic form, it will look somewhat like this:

Button button = (Button) findViewById(R.id.button);
button.setOnTouchListener(new View.OnTouchListener() {

private Handler mHandler;

@Override public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mHandler != null) return true;
mHandler = new Handler();
mHandler.postDelayed(mAction, 500);
break;
case MotionEvent.ACTION_UP:
if (mHandler == null) return true;
mHandler.removeCallbacks(mAction);
mHandler = null;
break;
}
return false;
}

Runnable mAction = new Runnable() {
@Override public void run() {
System.out.println("Performing action...");
mHandler.postDelayed(this, 500);
}
};

});

The idea is pretty simple: post a Runnable containing the repeated action on a Handler when the 'down' touch action occurs. After that, don't post the Runnable again until the 'up' touch action has passed. The Runnable will keep posting itself to the Handler (while the 'down' touch action is still happening), until it gets removed by the touch up action - that's what enables the 'repeating' aspect.

Depending on the actual behaviour of the button and its onclick/ontouch you're after, you might want to do the initial post without a delay.

Android - Hold Button to Repeat Action

This is more independent implementation, usable with any View, that supports touch event:

import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

/**
* A class, that can be used as a TouchListener on any view (e.g. a Button).
* It cyclically runs a clickListener, emulating keyboard-like behaviour. First
* click is fired immediately, next one after the initialInterval, and subsequent
* ones after the normalInterval.
*
* <p>Interval is scheduled after the onClick completes, so it has to run fast.
* If it runs slow, it does not generate skipped onClicks. Can be rewritten to
* achieve this.
*/
public class RepeatListener implements OnTouchListener {

private Handler handler = new Handler();

private int initialInterval;
private final int normalInterval;
private final OnClickListener clickListener;
private View touchedView;

private Runnable handlerRunnable = new Runnable() {
@Override
public void run() {
if(touchedView.isEnabled()) {
handler.postDelayed(this, normalInterval);
clickListener.onClick(touchedView);
} else {
// if the view was disabled by the clickListener, remove the callback
handler.removeCallbacks(handlerRunnable);
touchedView.setPressed(false);
touchedView = null;
}
}
};

/**
* @param initialInterval The interval after first click event
* @param normalInterval The interval after second and subsequent click
* events
* @param clickListener The OnClickListener, that will be called
* periodically
*/
public RepeatListener(int initialInterval, int normalInterval,
OnClickListener clickListener) {
if (clickListener == null)
throw new IllegalArgumentException("null runnable");
if (initialInterval < 0 || normalInterval < 0)
throw new IllegalArgumentException("negative interval");

this.initialInterval = initialInterval;
this.normalInterval = normalInterval;
this.clickListener = clickListener;
}

public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.removeCallbacks(handlerRunnable);
handler.postDelayed(handlerRunnable, initialInterval);
touchedView = view;
touchedView.setPressed(true);
clickListener.onClick(view);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(handlerRunnable);
touchedView.setPressed(false);
touchedView = null;
return true;
}

return false;
}

}

Usage:

Button button = new Button(context);
button.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
@Override
public void onClick(View view) {
// the code to execute repeatedly
}
}));

Android - Repeat Function While Button Held

first of all you must know that every call to findViewById() can waste a big time of your application so you'd better get them once and use them multiple times.

to repeat this code while button is holding you must use below link and use the custom listener this link provides:

Android - Hold Button to Repeat Action

i copy the code for your ease :

import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

/**
* A class, that can be used as a TouchListener on any view (e.g. a Button).
* It cyclically runs a clickListener, emulating keyboard-like behaviour. First
* click is fired immediately, next one after the initialInterval, and subsequent
* ones after the normalInterval.
*
* <p>Interval is scheduled after the onClick completes, so it has to run fast.
* If it runs slow, it does not generate skipped onClicks. Can be rewritten to
* achieve this.
*/
public class RepeatListener implements OnTouchListener {

private Handler handler = new Handler();

private int initialInterval;
private final int normalInterval;
private final OnClickListener clickListener;

private Runnable handlerRunnable = new Runnable() {
@Override
public void run() {
handler.postDelayed(this, normalInterval);
clickListener.onClick(downView);
}
};

private View downView;

/**
* @param initialInterval The interval after first click event
* @param normalInterval The interval after second and subsequent click
* events
* @param clickListener The OnClickListener, that will be called
* periodically
*/
public RepeatListener(int initialInterval, int normalInterval,
OnClickListener clickListener) {
if (clickListener == null)
throw new IllegalArgumentException("null runnable");
if (initialInterval < 0 || normalInterval < 0)
throw new IllegalArgumentException("negative interval");

this.initialInterval = initialInterval;
this.normalInterval = normalInterval;
this.clickListener = clickListener;
}

public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.removeCallbacks(handlerRunnable);
handler.postDelayed(handlerRunnable, initialInterval);
downView = view;
downView.setPressed(true);
clickListener.onClick(view);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(handlerRunnable);
downView.setPressed(false);
downView = null;
return true;
}

return false;
}

}

and then use it like below in your onCreate() :

View player1 =  findViewById(R.id.player1);
View player2 = findViewById(R.id.player2);
button.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
@Override
public void onClick(View view) {
// the code to execute repeatedly
player1.setX(player1.getX() - 30);
player2.setX(player2.getX() + 30);
}
}));

for better code you can define player1 and player2 public (out of your onCreate) then initiate them in onCreate()

Android - How to hold a button for some time to repeat an action?

You need to use onTouchListener

button.setOnTouchListener(new View.OnTouchListener() {        
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
// You can call a thread here which will keep increasing the time
return true;
case MotionEvent.ACTION_UP:
// RELEASED
// Stop the thread here
return true;
}
return false;
}
});

Repeat a callback on Button Hold down

First we need a way to check if the Button is still pressed. For that purpose define a new boolean field called buttonPressed:

private boolean buttonPressed = false;

Next we define the Runnable we want to run as long as the Button is pressed:

private final Runnable buttonPressedRunnable = new Runnable() {
@Override
public void run() {
// Check if the Button is pressed
if(buttonPressed) {
// Insert whatever you want to do while the Button is pressed here
...

// Repost the Runnable with an appropriate delay
button.postDelayed(this, 100);
}
}
};

Finally in your OnTouchListener you need to set buttonPressed to true in ACTION_DOWN and again to false in ACTION_UP. You also need to post the buttonPressedRunnable once, as long as the Button is pressed it will repost itself.

button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCamera.setPreviewCallback(previewCallback);
buttonPressed = true;
button.post(buttonPressedRunnable);
break;
case MotionEvent.ACTION_UP:
buttonPressed = false;
break;
}
return false;
}
});

How can I repeat/retrigger onLongClick in Android

Here's the solution that I cobbled together thats working well for me. I would have accepted the previous answers but they didn't take into account moving outside of the button's view. I hope the below code helps somebody else out in the same predicament. 90% of this code is not mine - so many thanks to the other stackoverflow posting that provided this.

EDIT: Actually, this is not perfect either - it doesn't animate button touches.

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

public class AutoRepeatButton extends Button
{

private long initialRepeatDelay = 500;
private long repeatIntervalInMilliseconds = 1000;
private boolean mRepeatStarted = false;

private Runnable repeatClickWhileButtonHeldRunnable = new Runnable()
{
@Override
public void run()
{
// Perform the present
// repetition of the
// click action
// provided by the
// user
// in
// setOnClickListener().
mRepeatStarted = true;

performLongClick();

// Schedule the next
// repetitions of the
// click action, using
// a faster repeat
// interval than the
// initial repeat
// delay interval.
postDelayed( repeatClickWhileButtonHeldRunnable, repeatIntervalInMilliseconds );
}
};

private void commonConstructorCode()
{
mRepeatStarted = false;

this.setOnTouchListener( new OnTouchListener()
{
@Override
public boolean onTouch( View v, MotionEvent event )
{
int action = event.getAction();

if( action == MotionEvent.ACTION_DOWN )
{
// Just to be sure that we removed all callbacks,
// which should have occurred in the ACTION_UP
removeCallbacks( repeatClickWhileButtonHeldRunnable );

// Schedule the start of repetitions after a one half second delay.
postDelayed( repeatClickWhileButtonHeldRunnable, initialRepeatDelay );
}
else
if( action == MotionEvent.ACTION_UP )
{
// Cancel any repetition in progress.
removeCallbacks( repeatClickWhileButtonHeldRunnable );

if( mRepeatStarted == false )
{
// PDS: I put this here..
performClick();
}

mRepeatStarted = false;
return true;
}
else
if( action == MotionEvent.ACTION_MOVE )
{
int[] l = new int[2];
v.getLocationOnScreen( l );

Rect rect = new Rect( l[0], l[1], l[0] + v.getWidth(), l[1] + v.getHeight() );

if( ! rect.contains( v.getLeft() + (int) event.getX(),
v.getTop() + (int) event.getY()))
{
// User moved outside bounds
removeCallbacks( repeatClickWhileButtonHeldRunnable );

mRepeatStarted = false;
}
}
else
if( action == MotionEvent.ACTION_CANCEL )
{
removeCallbacks( repeatClickWhileButtonHeldRunnable );

mRepeatStarted = false;
}

// Returning true here prevents performClick() from getting called
// in the usual manner, which would be redundant, given that we are
// already calling it above.
return true;
}
} );
}

public AutoRepeatButton( Context context, AttributeSet attrs, int defStyle )
{
super( context, attrs, defStyle );
commonConstructorCode();
}

public AutoRepeatButton( Context context, AttributeSet attrs )
{
super( context, attrs );
commonConstructorCode();
}

public AutoRepeatButton( Context context )
{
super( context );
commonConstructorCode();
}
}

set button pressed and keep executing same task in Android

I think onTouchListerner will best suits for your requirement. Use thread for doing the work. On event ACTION_DOWN start the thread and on event ACTION_UP stop the thread.

((Button) findViewById(R.id.leftBtn)).setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View arg0, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
// start the thread
return true;
} else if(event.getAction() == MotionEvent.ACTION_UP){
// stop the thread
return true;
}
return false;

}
});

Hope this will help for you.

Repeat task while button is pressed (from adapter)

When you press the button and use the notifyDataSetChanged() method it will cause the MotionEvent.ACTION_UP fail.

Please use the viewHolder.tbCount.setText() method to replace notifyDataSetChanged() and don't forget to update SizeCount(item) object value.

public class ItemAdapter extends ArrayAdapter<SizeCount> {
private static final String LOG_TAG = "MemoListAdapter";

private Collection<SizeCount> list;

public ItemAdapter(Context context, List<SizeCount> list) {
super(context, 0, list);
this.list = list;
}

public Collection<SizeCount> getList() {
return list;
}

@SuppressLint("ClickableViewAccessibility")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final SizeCount item = getItem(position);
if (item == null) return convertView;

final ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.entry_size_count, parent, false);
viewHolder = new ViewHolder();
viewHolder.tbCount = (EditText) convertView.findViewById(R.id.tbCount);
viewHolder.tvSize = (TextView) convertView.findViewById(R.id.tvSize);
viewHolder.btAdd = convertView.findViewById(R.id.btAdd);
viewHolder.btAdd.setOnTouchListener(new View.OnTouchListener() {
private Handler mHandler = new Handler();

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHandler.postDelayed(mAction, 500);
break;
case MotionEvent.ACTION_UP:
mHandler.removeCallbacks(mAction);
break;
}
return false;
}

Runnable mAction = new Runnable() {
@Override
public void run() {
float currentCount = item.getCount();
float nextCount = currentCount + 1;
item.setModified(true);

// update SizeCount value
item.setCount(nextCount);

// set value to tbCount
viewHolder.tbCount.setText(String.valueOf(item.getCount()));

// this will cause the MotionEvent.ACTION_UP fail
// notifyDataSetChanged();

mHandler.postDelayed(this, 500);
}
};

});

convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

viewHolder.tvSize.setFocusable(false);
viewHolder.tvSize.setText(item.getArticles().getSize());
viewHolder.tbCount.setText(item.getCount() + "");
return convertView;
}

private class ViewHolder {
TextView tvSize;

TextView tbCount;

Button btAdd;
}
}

I think it is not good to call notifyDataSetChanged() method for this case. It is wrong to refresh the whole View just to change value on single item.



Related Topics



Leave a reply



Submit