我想在按住按钮时实施重复动作。示例:当用户单击按钮并按住它时,它应该以固定的间隔一次又一次地调用类似的方法,直到用户将手指从按钮上移开。
答案 0 :(得分:64)
有多种方法可以实现这一目标,但非常简单的方法是在Runnable
上以一定的延迟发布Handler
。在它最基本的形式中,它看起来有点像这样:
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);
}
};
});
这个想法非常简单:当“向下”触摸操作发生时,在Runnable
上发布包含重复操作的Handler
。之后,请不要再次发布Runnable
,直到“向上”触摸操作过去为止。 Runnable
会继续将自己发布到Handler
(当'向下'触摸动作仍在发生时),直到它被修饰操作移除 - 这就是启用'重复'方面的原因。< / p>
根据按钮的实际行为及其所使用的onclick / ontouch,您可能希望毫不拖延地进行初始发布。
答案 1 :(得分:2)
兼容的Kotlin版本和基于Faisal Shaikh答案的示例:
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
}
}
答案 2 :(得分:1)
虽然不是一个好主意。这可以通过在onKeyDown
上启动计时器来实现,以一定间隔触发,在此期间您将光标移动一步并重新启动计时器。然后,您可以取消onKeyUp
事件上的计时器。这在其他系统上工作的方式通常是向下移动第一个键,然后稍等一下以确保用户确定按住按钮...然后重复可以更快一些。想想键盘自动重复。这应该有效,不应该对ui线程造成不利影响。
答案 3 :(得分:0)
您可以为该视图注册View.OnKeyListener
。请考虑最好去除这些回调,否则 - 如果你的方法做了一些甚至稍微“沉重”的事情 - 用户界面也不会顺利。
答案 4 :(得分:0)
如果您使用普通视图点击,则这是另一种方法。如果是这样,则在释放视图时,将调用单击侦听器。因此,我利用长按侦听器来完成第一部分。
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;
}
});
答案 5 :(得分:0)
这是更独立的实现,可用于任何支持触摸事件的View。
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;
}
}
用法:
Button button = new Button(context);
button.setOnTouchListener(new RepeatListener(400, 100, new OnClickListener() {
@Override
public void onClick(View view) {
// the code to execute repeatedly
}
}));