Android:如何检测双击?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2217670/
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: How to detect double-tap?
提问by alan
I have a problem with implementing double tap. Well I implemented the onGestureListener
and I had the gestureDetector
, but I'm not sure where is the problem, here is my code:
我在实现双击时遇到问题。好吧,我实现了onGestureListener
,我有gestureDetector
,但我不确定问题出在哪里,这是我的代码:
public class home extends TabActivity implements OnGestureListener {
/** Called when the activity is first created. */
private EditText queryText;
private ResultsAdapter m_adapter;
private ProgressDialog pd;
final Handler h = new Handler();
private TabHost mTabHost;
private ArrayList<SearchItem> sResultsArr = new ArrayList<SearchItem>();
private String queryStr;
private JSONObject searchResponse;
private GestureDetector gestureScanner;
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateListUi();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button search = (Button)findViewById(R.id.search);
Button testButt = (Button)findViewById(R.id.testbutt);
queryText = (EditText)findViewById(R.id.query);
ListView lvr = (ListView)findViewById(R.id.search_results);
//initialise the arrayAdapter
this.m_adapter = new ResultsAdapter(home.this, R.layout.listrow, sResultsArr);
lvr.setAdapter(this.m_adapter);
lvr.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false);
}
});
gestureScanner = new GestureDetector(this,this);
gestureScanner.setOnDoubleTapListener(new OnDoubleTapListener(){
public boolean onDoubleTap(MotionEvent e) {
//viewA.setText("-" + "onDoubleTap" + "-");
pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false);
return false;
}
public boolean onDoubleTapEvent(MotionEvent e) {
// viewA.setText("-" + "onDoubleTapEvent" + "-");
return false;
}
public boolean onSingleTapConfirmed(MotionEvent e) {
//viewA.setText("-" + "onSingleTapConfirmed" + "-");
return false;
}
});
//initialise tab contents
mTabHost = getTabHost();
mTabHost.addTab(mTabHost.newTabSpec("tab1").setIndicator("Home").setContent(R.id.homepage));
mTabHost.addTab(mTabHost.newTabSpec("tab2").setIndicator("Search Results").setContent(R.id.tab2));
mTabHost.setCurrentTab(0);
//sets the respective listeners
testButt.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
if(mTabHost.getTabWidget().getVisibility()==View.GONE){
mTabHost.getTabWidget().setVisibility(View.VISIBLE);
}
else{
mTabHost.getTabWidget().setVisibility(View.GONE);
}
}
});
search.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
sResultsArr.clear();
queryStr = "http://rose.mosuma.com/mobile?query=" + queryText.getText().toString();
pd = ProgressDialog.show(home.this, null,"Loading products from server", true, false);
goSearch();
}
});
}
//updates the listUI whenever after receiving the response from the server
public void updateListUi(){
if(sResultsArr.size() > 0){
}
try{
String ptypename;
int count;
LinearLayout ptypebar = (LinearLayout)findViewById(R.id.productCat);
ptypebar.removeAllViews();
JSONArray ptypes = searchResponse.getJSONArray("ptypes");
for(int index =0;index < ptypes.length();index++){
JSONObject ptype = ptypes.getJSONObject(index);
count = ptype.getInt("count");
ptypename = ptype.getString("ptypename");
//add into tab 2's UI
//ImageView icon = new ImageView(this);
TextView t = new TextView(home.this);
t.setText(ptypename + " (" + count + ")");
ptypebar.addView(t);
}
}
catch(JSONException e){
}
//if(m_adapter.getItems() != sResultsArr){
ArrayList<SearchItem> a = m_adapter.getItems();
a = sResultsArr;
//}
m_adapter.notifyDataSetChanged();
pd.dismiss();
}
public void goSearch(){
mTabHost.setCurrentTab(1);
//separate thread for making http request and updating the arraylist
Thread t = new Thread() {
public void run() {
searchResponse = sendSearchQuery(queryStr);
try{
JSONArray results = searchResponse.getJSONArray("results");
//this is stupid. i probably have to see how to make a json adapter
for(int index =0;index < results.length();index++){
JSONObject product = results.getJSONObject(index);
//gets the searched products from the json object
URL imgUrl = new URL(product.getString("image"));
String productname = product.getString("productname");
String ptypename = product.getString("ptypename");
int pid = product.getInt("pid");
int positive = product.getInt("pos");
int negative = product.getInt("neg");
int neutral = product.getInt("neu");
SearchItem item = new SearchItem(imgUrl,productname,ptypename,neutral,positive,negative,pid);
sResultsArr.add(item);
}
}
catch(JSONException e){
}
catch(Exception e){
}
//returns back to UI therad
h.post(mUpdateResults);
}
};
t.start();
}
//sends a request with qry as URL
//and receives back a JSONobject as response
public JSONObject sendSearchQuery(String qry){
HttpRequest r = new HttpRequest();
JSONObject response = r.sendHttpRequest(qry);
return response;
}
@Override
public boolean onDown(MotionEvent arg0) {
return gestureScanner.onTouchEvent(arg0);
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
Oh, another question, if my ListView
has an onItemClickListener
, can android detect between single tap or double tap for it?
哦,另一个问题,如果我ListView
有一个onItemClickListener
,android 可以检测单击或双击它吗?
采纳答案by Dave Webb
Why aren't you using a Long Press? Or are you using that already for something else? The advantages of a Long Press over a Double Touch:
你为什么不使用长按?或者你已经将它用于其他用途?长按相对于双击的优势:
- Long Press is a recommeded interaction in the UI Guidelines, Double Touch is not.
- It's what users expect; a user might not find a Double Touch action as they won't go looking for it
- It's already handled in the API.
- Implementing a Double Touch will affect handling of Single Touches, because you'll have to wait to see if every Single Touch turns into a Double Touch before you can process it.
- Long Press 是 UI Guidelines 中推荐的交互方式,Double Touch 不是。
- 这是用户所期望的;用户可能找不到 Double Touch 操作,因为他们不会去寻找它
- 它已经在 API 中处理了。
- 实现 Double Touch 会影响 Single Touch 的处理,因为在处理它之前,您必须等待查看每个 Single Touch 是否变成 Double Touch。
回答by Hannes Niederhausen
You can use the GestureDetector. See the following code:
您可以使用 GestureDetector。请参阅以下代码:
public class MyView extends View {
GestureDetector gestureDetector;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// creating new gesture detector
gestureDetector = new GestureDetector(context, new GestureListener());
}
// skipping measure calculation and drawing
// delegate the event to the gesture detector
@Override
public boolean onTouchEvent(MotionEvent e) {
return gestureDetector.onTouchEvent(e);
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
// event when double tap occurs
@Override
public boolean onDoubleTap(MotionEvent e) {
float x = e.getX();
float y = e.getY();
Log.d("Double Tap", "Tapped at: (" + x + "," + y + ")");
return true;
}
}
}
You can override other methods of the listener to get single taps, flinges and so on.
您可以覆盖侦听器的其他方法以获取单击、甩动等。
回答by bughi
As a lightweight alternative to GestureDetector you can use this class
作为 GestureDetector 的轻量级替代品,您可以使用此类
public abstract class DoubleClickListener implements OnClickListener {
private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds
long lastClickTime = 0;
@Override
public void onClick(View v) {
long clickTime = System.currentTimeMillis();
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
onDoubleClick(v);
} else {
onSingleClick(v);
}
lastClickTime = clickTime;
}
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
}
Example:
例子:
view.setOnClickListener(new DoubleClickListener() {
@Override
public void onSingleClick(View v) {
}
@Override
public void onDoubleClick(View v) {
}
});
回答by M.Hefny
combining "Bughi" "DoubleClickListner" and "Jayant Arora" Timer in one contained class:
将“Bughi”、“DoubleClickListner”和“Jayant Arora”计时器组合在一个包含类中:
public abstract class DoubleClickListener implements OnClickListener {
private Timer timer = null; //at class level;
private int DELAY = 400;
private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds
long lastClickTime = 0;
@Override
public void onClick(View v) {
long clickTime = System.currentTimeMillis();
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
processDoubleClickEvent(v);
} else {
processSingleClickEvent(v);
}
lastClickTime = clickTime;
}
public void processSingleClickEvent(final View v){
final Handler handler=new Handler();
final Runnable mRunnable=new Runnable(){
public void run(){
onSingleClick(v); //Do what ever u want on single click
}
};
TimerTask timertask=new TimerTask(){
@Override
public void run(){
handler.post(mRunnable);
}
};
timer=new Timer();
timer.schedule(timertask,DELAY);
}
public void processDoubleClickEvent(View v){
if(timer!=null)
{
timer.cancel(); //Cancels Running Tasks or Waiting Tasks.
timer.purge(); //Frees Memory by erasing cancelled Tasks.
}
onDoubleClick(v);//Do what ever u want on Double Click
}
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
}
and can be called as :
并且可以被称为:
view.setOnClickListener(new DoubleClickListener() {
@Override
public void onSingleClick(View v) {
}
@Override
public void onDoubleClick(View v) {
}
});
回答by Nayanesh Gupte
if you do not wish to go for custom view then you can use following approach. e.g. ImageView
如果您不想使用自定义视图,则可以使用以下方法。例如图像视图
// class level
GestureDetector gestureDetector;
boolean tapped;
ImageView imageView;
// inside onCreate of Activity or Fragment
gestureDetector = new GestureDetector(context,new GestureListener());
//--------------------------------------------------------------------------------
//------------------------------------------------ --------------------------------
public class GestureListener extends
GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
// event when double tap occurs
@Override
public boolean onDoubleTap(MotionEvent e) {
tapped = !tapped;
if (tapped) {
} else {
}
return true;
}
}
//--------------------------------------------------------------------------------
//------------------------------------------------ --------------------------------
for ImageView
用于图像视图
imageView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}
});
回答by Jayant Arora
GuestureDetecter Works Well on Most Devices, I would like to know how the time between two clicks can be customized on double click event, i wasn't able to do that. I updated the above code by "Bughi" "DoubleClickListner", added a timer using handler that executes a code after a specific delay on single click, and if double click is performed before that delay it cancels the timer and single click task and only execute double click task. Code is working Fine Makes it perfect to use as double click listner:
GuestureDetecter 在大多数设备上运行良好,我想知道如何在双击事件中自定义两次单击之间的时间,但我无法做到这一点。我通过“Bughi”“DoubleClickListner”更新了上面的代码,添加了一个使用处理程序的计时器,该处理程序在单击特定延迟后执行代码,如果在该延迟之前执行双击,它会取消计时器和单击任务并仅执行双击任务。代码工作正常使它完美地用作双击监听器:
private Timer timer = null; //at class level;
private int DELAY = 500;
view.setOnClickListener(new DoubleClickListener() {
@Override
public void onSingleClick(View v) {
final Handler handler = new Handler();
final Runnable mRunnable = new Runnable() {
public void run() {
processSingleClickEvent(v); //Do what ever u want on single click
}
};
TimerTask timertask = new TimerTask() {
@Override
public void run() {
handler.post(mRunnable);
}
};
timer = new Timer();
timer.schedule(timertask, DELAY);
}
@Override
public void onDoubleClick(View v) {
if(timer!=null)
{
timer.cancel(); //Cancels Running Tasks or Waiting Tasks.
timer.purge(); //Frees Memory by erasing cancelled Tasks.
}
processDoubleClickEvent(v);//Do what ever u want on Double Click
}
});
回答by Nikola Despotoski
This is my solution, it uses default setOnItemClickListener()
. I had the same task to implement. Soon I'll post example and custom class on my github.
Brief explanation is given. I'm not sure if the time in milliseconds is right difference for the system (See ViewConfiguration.getDoubleTapTimeout()
source) to decide between single and double tap.
这是我的解决方案,它使用 default setOnItemClickListener()
。我有同样的任务要执行。很快我将在我的 github 上发布示例和自定义类。给出了简要说明。我不确定以毫秒为单位的时间是否适合系统(请参阅ViewConfiguration.getDoubleTapTimeout()
源代码)来决定单击和双击之间的差异。
Edit:See it here: https://github.com/NikolaDespotoski/DoubleTapListViewor https://github.com/NikolaDespotoski/DoubleTapListViewHandler
编辑:在此处查看:https: //github.com/NikolaDespotoski/DoubleTapListView或 https://github.com/NikolaDespotoski/DoubleTapListViewHandler
回答by sumit dalakiya
boolean nonDoubleClick = true, singleClick = false;
private long firstClickTime = 0L;
private final int DOUBLE_CLICK_TIMEOUT = 200;
listview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
// TODO Auto-generated method stub
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
if (singleClick) {
Toast.makeText(getApplicationContext(), "Single Tap Detected", Toast.LENGTH_SHORT).show();
}
firstClickTime = 0L;
nonDoubleClick = true;
singleClick = false;
}
}, 200);
if (firstClickTime == 0) {
firstClickTime = SystemClock.elapsedRealtime();
nonDoubleClick = true;
singleClick = true;
} else {
long deltaTime = SystemClock.elapsedRealtime() - firstClickTime;
firstClickTime = 0;
if (deltaTime < DOUBLE_CLICK_TIMEOUT) {
nonDoubleClick = false;
singleClick = false;
Toast.makeText(getApplicationContext(), "Double Tap Detected", Toast.LENGTH_SHORT).show();
}
}
}
});
回答by Suragch
Double-tap andSingle-tap
双击和单击
Double-tap only
仅双击
It is quite easy to detect a double tap on a view by using SimpleOnGestureListener
(as demonstrated in Hannes Niederhausen's answer).
通过使用SimpleOnGestureListener
(如Hannes Niederhausen 的回答所示)可以很容易地检测到视图上的双击。
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
}
I can't see a big advantage to re-inventing the logic for this (like bughi's answer).
我看不出为此重新发明逻辑有什么大的优势(比如bughi 的回答)。
Double-tap and Single-tap with delay
双击和单击延迟
You can also use the SimpleOnGestureListener
to differentiate a single-tap and a double-tap as mutually exclusive events. To do that you just override onSingleTapConfirmed
. This will delay running the single-tap code until the system is certain that the user hasn't double-tapped (ie, the delay > ViewConfiguration.getDoubleTapTimeout()
). There is definately no reason to re-invent all the logic for that (as is done in this, thisand other answers).
您还可以使用 将SimpleOnGestureListener
单击和双击区分为互斥事件。为此,您只需覆盖onSingleTapConfirmed
. 这将延迟运行单击代码,直到系统确定用户没有双击(即延迟 > ViewConfiguration.getDoubleTapTimeout()
)。绝对没有理由为此重新发明所有逻辑(就像在这个、这个和其他答案中所做的那样)。
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
}
Double-tap and Single-tap with no delay
无延迟的双击和单击
The potential problem with onSingleTapConfirmed
is the delay. Sometimes a noticeable delay is not acceptable. In that case you can replace onSingleTapConfirmed
with onSingleTapUp
.
潜在的问题onSingleTapConfirmed
是延迟。有时明显的延迟是不可接受的。在这种情况下,您可以替换onSingleTapConfirmed
为onSingleTapUp
.
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
}
You need to realize, though, that bothonSingleTapUp
andonDoubleTap
will be called if there is a double-tap. (This is essentially what bughi's answerdoes and what some of the commenters were complaining about.) You either need to use the delay or call both methods. It's not possible have a single-tap with no delay and at the same time know whether the user is going to tap again.
你需要认识到,虽然,这两个onSingleTapUp
和onDoubleTap
是否有双击将被调用。(这基本上就是bughi 的回答所做的以及一些评论者所抱怨的。)您要么需要使用延迟,要么调用这两种方法。不可能在没有延迟的情况下单击一次,同时知道用户是否要再次单击。
If the single-tap delay is not acceptable for you then you have a couple options:
如果您无法接受单击延迟,那么您有几个选择:
- Accept that both
onSingleTapUp
andonDoubleTap
will be called for a double-tap. Just divide up your logic appropriately so that it doesn't matter. This is essentially what I did when I implemented a double-tap for caps-lock on a custom keyboard. Don't use a double-tap. It's not an intuitive UI action for most things. As Dave Webb suggests, a long press is probably better. You can also implement that with the
SimpleOnGestureListener
:private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { } }
- 接受这两者
onSingleTapUp
,onDoubleTap
将被要求双击。只需适当地划分您的逻辑,以便它无关紧要。这基本上是我在自定义键盘上实现双击大写锁定时所做的。 不要使用双击。对于大多数事情,这不是直观的 UI 操作。正如Dave Webb 所建议的那样,长按可能更好。您还可以使用以下方法实现
SimpleOnGestureListener
:private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { } }
回答by Santosh Kumar
Improvised dhruvi code
即兴的 dhruvi 代码
public abstract class DoubleClickListener implements View.OnClickListener {
private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds
long lastClickTime = 0;
boolean tap = true;
@Override
public void onClick(View v) {
long clickTime = System.currentTimeMillis();
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
onDoubleClick(v);
tap = false;
} else
tap = true;
v.postDelayed(new Runnable() {
@Override
public void run() {
if(tap)
onSingleClick();
}
},DOUBLE_CLICK_TIME_DELTA);
lastClickTime = clickTime;
}
public abstract void onDoubleClick(View v);
public abstract void onSingleClick();
}