Android Repeat Action on Pressing and Holding a Button

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 an action as long button is pressed

A general approach (not specific to Android) would be to detect the press and release event separately. The press event starts a periodic task (Runnable or Thread) which adds to the counter (let us say 5 times a second, or once every 200 ms). The release event stops the periodic task.

How can I call a function repeat when user press a button until user release that button

You can use OnTouchListener:

public class MainActivity extends Activity implements OnTouchListener
{

private Button button;

// ...

@Override
public void onCreate(Bundle savedInstanceState)
{
// ...

button = (Button) findViewById(R.id.button_id);
button.setOnTouchListener(this);

// ...
}

// ...

@Override
public boolean onTouch(View v, MotionEvent event)
{
/* get a reference to the button that is being touched */
Button b = (Button) v;

/* get the action of the touch event */
int action = event.getAction();

if(action == MotionEvent.ACTION_DOWN)
{
/*
A pressed gesture has started, the motion contains
the initial starting location.
*/
}
else if(action == MotionEvent.ACTION_UP)
{
/*
A pressed gesture has finished, the motion contains
the final release location as well as any intermediate
points since the last down or move event.
*/
}
else if(action == MotionEvent.ACTION_MOVE)
{
/*
A change has happened during a press gesture (between
ACTION_DOWN and ACTION_UP). The motion contains the
most recent point, as well as any intermediate points
since the last down or move event.
*/
}
else if(action == MotionEvent.ACTION_CANCEL)
{
/*
The current gesture has been aborted. You will not
receive any more points in it. You should treat this
as an up event, but not perform any action that you
normally would.
*/
}
}
}

Continuously calling method until button is pressed

Use an onTouchListener to detect the button press. Then you can use a Handler or AlarmManager to call your event until a certain condition is reached:

this.handlerMethod = new MethodHandler(this);
boolean myCondition = false;

imageButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
if (myCondition == false) {
handlerMethod.sendEmptyMessageDelayed(0, 55000);

}
return true;
}
return false;
}
});

/**
* private static handler so there are no leaked activities.
*/
private static class MethodHandler extends Handler {

private final WeakReference<Home> activity;

public MyTimeHandler(Home activity) {
this.activity = new WeakReference<Home>(activity);
}
@Override
public void handleMessage(Message msg) {
if (activity.get() != null) {

activity.get().myMethod();
}

sendEmptyMessageDelayed(0, 55000);
}
}

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