android重复按住按钮的动作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10511423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
android repeat action on pressing and holding a button
提问by MH.
I want to implement repeat action on pressing and holding a button. Example: When user click on a button and hold it,it should call a similar method again and again on a fixed interval until the user remove his finger from the button.
我想在按住按钮时实现重复操作。示例:当用户点击一个按钮并按住它时,它应该以固定的时间间隔一次又一次地调用类似的方法,直到用户将手指从按钮上移开。
回答by MH.
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:
有多种方法可以实现这一点,但一个非常简单的方法是Runnable
在Handler
有一定延迟的情况下在 a上发布。在最基本的形式中,它看起来有点像这样:
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.
这个想法非常简单:当“向下”触摸动作发生时,Runnable
在 a 上发布包含重复动作的 a Handler
。之后,Runnable
在“向上”触摸动作通过之前不要再次发布。该Runnable
会继续发布自己的Handler
(而“向下”的触摸动作仍在进行),直到它被通过触摸后续行动删除-这就是使“重复”的方面。
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.
根据按钮的实际行为及其您所追求的 onclick/ontouch,您可能希望立即进行初始发布。
回答by Cícero Moura
Here is another approach in case you use the normal view click. If so, when you release the view, the click listener is called. So I take advantage of the long click listener to do the first part.
如果您使用普通视图单击,这是另一种方法。如果是这样,当您释放视图时,将调用单击侦听器。所以我利用长点击监听器来做第一部分。
button.setOnLongClickListener(new OnLongClickListener() {
private Handler mHandler;
@Override
public boolean onLongClick(View view) {
final Runnable mAction = new Runnable() {
@Override
public void run() {
//do something here
mHandler.postDelayed(this, 1000);
}
};
mHandler = new Handler();
mHandler.postDelayed(mAction, 0);
button.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
if (mHandler == null) return true;
mHandler.removeCallbacks(mAction);
mHandler = null;
button.setOnTouchListener(null);
return false;
}
return false;
}
});
return true;
}
});
回答by cstrutton
Although not a great idea. It could be accomplished by starting a timer on onKeyDown
to fire at an interval during which you move the cursor one step and restart the timer. You could then cancel the timer on the onKeyUp
event. The way this works on other systems is to typically to move on the first key down then wait a bit to ensure that the user is definitly holding the button... then the repeat can be a bit faster. Think of a keyboard auto repeating. This should work and should not affect the ui thread adversely.
虽然不是一个好主意。这可以通过启动一个计时器来完成,onKeyDown
在您将光标移动一步并重新启动计时器的时间间隔内触发。然后,您可以取消onKeyUp
事件的计时器。这在其他系统上的工作方式通常是向下移动第一个键,然后稍等片刻以确保用户明确按住按钮......然后重复可能会更快一些。想想键盘自动重复。这应该可以工作,并且不会对 ui 线程产生不利影响。
回答by Kenneth Argo
A compatible Kotlin version and example based on Faisal Shaikhanswer:
基于Faisal Shaikh答案的兼容 Kotlin 版本和示例:
package com.kenargo.compound_widgets
import android.os.Handler
import android.view.MotionEvent
import android.view.View
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 initialRepeatDelay.
*
* @param initialInterval The interval after first click event
* @param initialRepeatDelay The interval after second and subsequent click events
*
* @param clickListener The OnClickListener, that will be called
* periodically
*
* 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.
*
* Usage:
*
* someView.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
* @Override
* public void onClick(View view) {
* // the code to execute repeatedly
* }
* }));
*
* Kotlin example:
* someView.setOnTouchListener(RepeatListener(defaultInitialTouchTime, defaultRepeatDelayTime, OnClickListener {
* // the code to execute repeatedly
* }))
*
*/
class RepeatListener(
initialInterval: Int,
initialRepeatDelay: Int,
clickListener: View.OnClickListener
) : OnTouchListener {
private val handler = Handler()
private var initialInterval: Int
private var initialRepeatDelay: Int
private var clickListener: View.OnClickListener
private var touchedView: View? = null
init {
require(!(initialInterval < 0 || initialRepeatDelay < 0)) { "negative intervals not allowed" }
this.initialInterval = initialRepeatDelay
this.initialRepeatDelay = initialInterval
this.clickListener = clickListener
}
private val handlerRunnable: Runnable = run {
Runnable {
if (touchedView!!.isEnabled) {
handler.postDelayed(handlerRunnable, initialRepeatDelay.toLong())
clickListener.onClick(touchedView)
} else {
// if the view was disabled by the clickListener, remove the callback
handler.removeCallbacks(handlerRunnable)
touchedView!!.isPressed = false
touchedView = null
}
}
}
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
handler.removeCallbacks(handlerRunnable)
handler.postDelayed(handlerRunnable, initialRepeatDelay.toLong())
touchedView = view
touchedView!!.isPressed = true
clickListener.onClick(view)
return true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
handler.removeCallbacks(handlerRunnable)
touchedView!!.isPressed = false
touchedView = null
return true
}
}
return false
}
}
回答by Faisal Shaikh
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
}
}));
回答by avimak
You can register a View.OnKeyListener
for that View. Please take into an account that it is better to debounce such callbacks, otherwise - in case your method doing something even slightly "heavy" - the UI won't be smooth.
您可以View.OnKeyListener
为该视图注册一个。请考虑到最好对此类回调进行去抖动,否则 - 如果您的方法做了一些稍微“重”的事情 - 用户界面将不会流畅。