Android: как обнаружить двойной тап?
У меня проблема с реализацией двойного нажатия. Ну, я реализовал 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;
}
О, еще один вопрос, если мой ListView
имеет onItemClickListener
, Android может обнаружить между одним нажатием или двойным нажатием для него?
29 ответов
Почему вы не используете длинное нажатие? Или вы уже используете это для чего-то другого? Преимущества длительного нажатия по сравнению с двойным касанием:
- Long Press - рекомендуемое взаимодействие в Руководстве по пользовательскому интерфейсу, Double Touch - нет.
- Это то, что пользователи ожидают; пользователь может не найти действие Double Touch, поскольку он не будет искать его
- Это уже обработано в API.
- Реализация Double Touch повлияет на обработку Single Touch, потому что вам придется подождать, чтобы увидеть, превращается ли каждое Single Touch в Double Touch, прежде чем вы сможете его обработать.
Вы можете использовать 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;
}
}
}
Вы можете переопределить другие методы слушателя, чтобы получить одиночные нажатия, щелчки и так далее.
В качестве легкой альтернативы 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);
}
Пример:
view.setOnClickListener(new DoubleClickListener() {
@Override
public void onSingleClick(View v) {
}
@Override
public void onDoubleClick(View v) {
}
});
Объединение "Bughi", "DoubleClickListner" и "Jayant Arora" Timer в один содержащий класс:
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);
}
и может называться как:
view.setOnClickListener(new DoubleClickListener() {
@Override
public void onSingleClick(View v) {
}
@Override
public void onDoubleClick(View v) {
}
});
Если вы не хотите переходить к пользовательскому виду, используйте следующий подход. например, 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;
}
}
// ------------------------------------------------ --------------------------------
для ImageView
imageView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}
});
Двойное касание и одно касание
Только дважды нажмите
Это довольно легко обнаружить двойным нажатием на вид с помощью SimpleOnGestureListener
(как показано в ответе Ханнеса Нидерхаузена).
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
}
Я не вижу большого преимущества в повторном изобретении логики для этого (например, в ответе Буги).
Двойное и одно касание с задержкой
Вы также можете использовать 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;
}
}
Двойное и одно касание без задержки
Потенциальная проблема с 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;
}
}
Вы должны понимать, однако, что оба onSingleTapUp
а также onDoubleTap
будет вызван, если есть двойное нажатие. (Это, по сути , ответ Буги и на что жаловались некоторые комментаторы.) Вам нужно либо использовать задержку, либо вызвать оба метода. Невозможно выполнить одно касание без задержки и в то же время узнать, собирается ли пользователь снова нажать.
Если задержка одним касанием для вас неприемлема, у вас есть несколько вариантов:
- Примите, что оба
onSingleTapUp
а такжеonDoubleTap
будет вызван для двойного нажатия. Просто разделите свою логику соответствующим образом, чтобы она не имела значения. По сути, это то, что я сделал, когда реализовал двойное касание для caps-lock на пользовательской клавиатуре. Не используйте двойной тап. Это не интуитивно понятный пользовательский интерфейс для большинства вещей. Как предполагает Дэйв Уэбб, долгое нажатие, вероятно, лучше. Вы также можете реализовать это с
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) { } }
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
}
});
Это мое решение, оно использует по умолчанию setOnItemClickListener()
, У меня была такая же задача для реализации. Скоро я опубликую пример и пользовательский класс на моем github. Краткое объяснение дано. Я не уверен, что время в миллисекундах - это правильная разница для системы (см. ViewConfiguration.getDoubleTapTimeout()
источник), чтобы выбрать между одним и двойным нажатием.
Изменить: посмотреть здесь: https://github.com/NikolaDespotoski/DoubleTapListView или https://github.com/NikolaDespotoski/DoubleTapListViewHandler
Импровизированный код Дхруви
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();
}
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();
}
}
}
});
Если вы используете Kotlin, вы можете сделать это так:
Я трачу много времени на преобразование этого кода в Kotlin, надеюсь, это сэкономит кому-то время
Создать детектор жестов:
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
Toast.makeText(this@DemoActivity,"Double Tap",Toast.LENGTH_LONG).show()
//Show or hide Ip address on double tap
toggleIPaddressVisibility()
return true;
}
override fun onLongPress(e: MotionEvent) {
super.onLongPress(e);
//rotate frame on long press
toggleFrameRotation()
Toast.makeText(this@DemoActivity,"LongClick",Toast.LENGTH_LONG).show()
}
override fun onDoubleTapEvent(e: MotionEvent): Boolean {
return true
}
override fun onDown(e: MotionEvent): Boolean {
return true
}
})
Присвойте любому вашему мнению:
IPAddress.setOnTouchListener { v, event ->
return@setOnTouchListener gestureDetector.onTouchEvent(event)
}
Это решение, которое ждет, если будет второй щелчок, прежде чем выполнять какое-либо действие
int init = 0;
myView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (init == 0) {
init++;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (init == 1) {
Log.d("hereGoes", "actionOne");
} else {
Log.d("hereGoes", "actionTwo");
}
init = 0;
}
}, 250);
} else {
init++;
}
}
});
Мое решение, может быть полезно.
long lastTouchUpTime = 0;
boolean isDoubleClick = false;
private void performDoubleClick() {
long currentTime = System.currentTimeMillis();
if(!isDoubleClick && currentTime - lastTouchUpTime < DOUBLE_CLICK_TIME_INTERVAL) {
isDoubleClick = true;
lastTouchUpTime = currentTime;
Toast.makeText(context, "double click", Toast.LENGTH_SHORT).show();
}
else {
lastTouchUpTime = currentTime;
isDoubleClick = false;
}
}
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;
}
}
}
Вы можете реализовать двойное нажатие, используя GestureDetectorCompat
учебный класс. в этом примере при двойном нажатии на текстовое представление вы можете сделать свою логику.
public class MainActivity extends AppCompatActivity {
GestureDetectorCompat gestureDetectorCompat;
TextView textElement;
@Override
protected void onCreate(Bundle savedInstanceState) {
.....
textElement = findViewById(R.id.textElement);
gestureDetectorCompat = new GestureDetectorCompat(this, new MyGesture());
textElement.setOnTouchListener(onTouchListener);
}
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetectorCompat.onTouchEvent(event);
return true;
}
};
class MyGesture extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
// whatever on double click
return true;
}
}
Thread + Interface = DoubleTapListener, прослушиватель AnyTap и т. Д.
В этом примере я реализовал прослушиватель DoubleTap с потоком. Вы можете добавить мой слушатель с любым объектом View, как и с любым ClickListener. Используя этот подход, вы можете легко осуществить прослушивание любого вида щелчков.
yourButton.setOnClickListener(new DoubleTapListener(this));
1) Мой класс Listrener
public class DoubleTapListener implements View.OnClickListener{
private boolean isRunning= false;
private int resetInTime =500;
private int counter=0;
private DoubleTapCallback listener;
public DoubleTapListener(Context context)
{
listener = (DoubleTapCallback)context;
Log.d("Double Tap","New");
}
@Override
public void onClick(View v) {
if(isRunning)
{
if(counter==1)
listener.onDoubleClick(v);
}
counter++;
if(!isRunning)
{
isRunning=true;
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(resetInTime);
isRunning = false;
counter=0;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
2) Слушатель обратного вызова
public interface DoubleTapCallback {
public void onDoubleClick(View v);
}
3) Реализуйте в своей деятельности
public class MainActivity extends AppCompatActivity implements DoubleTapCallback{
private Button button;
private int counter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new DoubleTapListener(this)); // Set mt listener
}
@Override
public void onDoubleClick(View v) {
counter++;
textView.setText(counter+"");
}
Соответствующая ссылка:
Вы можете увидеть полный рабочий код ЗДЕСЬ
Реализация одиночного и двойного клика
public abstract class DoubleClickListener implements View.OnClickListener {
private static final long DOUBLE_CLICK_TIME_DELTA = 200;
private long lastClickTime = 0;
private View view;
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
onSingleClick(view);
}
};
private void runTimer(){
handler.removeCallbacks(runnable);
handler.postDelayed(runnable,DOUBLE_CLICK_TIME_DELTA);
}
@Override
public void onClick(View view) {
this.view = view;
long clickTime = System.currentTimeMillis();
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
handler.removeCallbacks(runnable);
lastClickTime = 0;
onDoubleClick(view);
} else {
runTimer();
lastClickTime = clickTime;
}
}
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
}
Эквивалентный код C#, который я использовал для реализации той же функциональности и даже могу настроить, чтобы принять N количество нажатий
public interface IOnTouchInterface
{
void ViewTapped();
}
public class MultipleTouchGestureListener : Java.Lang.Object, View.IOnTouchListener
{
int clickCount = 0;
long startTime;
static long MAX_DURATION = 500;
public int NumberOfTaps { get; set; } = 7;
readonly IOnTouchInterface interfc;
public MultipleTouchGestureListener(IOnTouchInterface tch)
{
this.interfc = tch;
}
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
clickCount++;
if(clickCount == 1)
startTime = Utility.CurrentTimeSince1970;
break;
case MotionEventActions.Up:
var currentTime = Utility.CurrentTimeSince1970;
long time = currentTime - startTime;
if(time <= MAX_DURATION * NumberOfTaps)
{
if (clickCount == NumberOfTaps)
{
this.interfc.ViewTapped();
clickCount = 0;
}
}
else
{
clickCount = 0;
}
break;
}
return true;
}
}
public static class Utility
{
public static long CurrentTimeSince1970
{
get
{
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local);
DateTime dtNow = DateTime.Now;
TimeSpan result = dtNow.Subtract(dt);
long seconds = (long)result.TotalMilliseconds;
return seconds;
}
}
}
В настоящее время код выше принимает 7 как количество нажатий, прежде чем он вызывает событие View Tapped. Но его можно настроить под любым номером
Простой способ сделать это в Котлине
button.setOnTouchListener(object : View.OnTouchListener{
val gestureDetector = GestureDetector(object : GestureDetector.SimpleOnGestureListener(){
override fun onDoubleTap(e: MotionEvent?): Boolean {
//do something here
return super.onDoubleTap(e)
}
})
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
//do something here
gestureDetector.onTouchEvent(event)
return true
}
})
Решение от Bughi & Jayant Arora для копирования:
public abstract class DoubleClickListener implements View.OnClickListener {
private int position;
private Timer timer;
private static final long DOUBLE_CLICK_TIME_DELTA = 300;//milliseconds
long lastClickTime = 0;
public DoubleClickListener (int position) {
this.position = position;
}
@Override
public void onClick(View v) {
long clickTime = System.currentTimeMillis();
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
if (timer != null) {
timer.cancel(); //Cancels Running Tasks or Waiting Tasks.
timer.purge(); //Frees Memory by erasing cancelled Tasks.
}
onDoubleClick(v, position);
} else {
final Handler handler = new Handler();
final Runnable mRunnable = () -> {
onSingleClick(v, position);
};
TimerTask timertask = new TimerTask() {
@Override
public void run() {
handler.post(mRunnable);
}
};
timer = new Timer();
timer.schedule(timertask, DOUBLE_CLICK_TIME_DELTA);
}
lastClickTime = clickTime;
}
public abstract void onSingleClick(View v, int position);
public abstract void onDoubleClick(View v, int position);}
Добавьте этот класс:
class DoubleTapListener(context: Context, doubleTapped: () -> Unit) : View.OnTouchListener {
private val gestureDetector: GestureDetector
init {
gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onDoubleTap(e: MotionEvent): Boolean {
doubleTapped()
return true
}
})
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
}
И тогда внутри любогоView
вы хотите слушать двойные нажатия, добавьте эту строку:
setOnTouchListener(DoubleTapListener(context) {
// Execute logic
})
Котлин без экстра класса
override fun onCreate(savedInstanceState: Bundle?) {
// ...
var listview: ListView = findViewById(R.id.<your_listview_id>)
var itemLastClickTime: Long = 0
var singleClickHandler: Handler = Handler(Looper.getMainLooper())
// ListView item 'double click' and 'single click'
listview.setOnItemClickListener(OnItemClickListener { av, iv, pos, id ->
// if double click occurs, disable single click handler
if (System.currentTimeMillis() - itemLastClickTime < 300) {
singleClickHandler.removeCallbacksAndMessages(null)
// do double click handling
return@OnItemClickListener
}
// store the time of the item's click event
itemLastClickTime = System.currentTimeMillis()
// single click handler only fires, if no double click occured
singleClickHandler.postDelayed({
// do single click handling
}, 300)
})
// ...
}
Java без дополнительного класса
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
ListView listview = findViewById(R.id.<your_listview_id>);
final long[] itemLastClickTime = {0};
Handler singleClickHandler = new Handler();
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> av, View iv, int pos, long id) {
// if double click occurs, disable single click handler
if (System.currentTimeMillis() - itemLastClickTime[0] < 300) {
singleClickHandler.removeCallbacksAndMessages(null);
// do double click handling
return;
}
// store the time of the item's click event
itemLastClickTime[0] = System.currentTimeMillis();
// single click handler only fires, if no double click occured
singleClickHandler.postDelayed(new Runnable() {
@Override
public void run() {
// do single click handling
}
}, 300);
}
});
// ...
}
Я реализовал простой пользовательский метод, используя сопрограммы kotlin (для Java можно сделать через потоки).
var click = 0
view.setOnClickListener{
click++
clicksHandling()
}
fun clicksHandling() {
if (click == 1) {
launch {
delay(300) // custom delay duration between clicks
// if user didn't double tap then click counter still 1
if (click == 1) {
// single click handling
runOnUiThread {
// whatever you wanna do on UI thread
}
}
click = 0 //reset counter , this will run no matter single / double tap
}
//double click handling
if (click == 2) {
// whatever on double click
}
}
Я создал простую библиотеку, чтобы справиться с этим. он также может обнаруживать более двух щелчков (все зависит от вас). После импорта класса ClickCounter вот как вы его используете для обнаружения одиночных и множественных щелчков:
ClickCounter counter = new ClickCounter();
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
counter.addClick(); // submits click to be counted
}
});
counter.setClickCountListener(new ClickCounter.ClickCountListener() {
@Override
public void onClickingCompleted(int clickCount) {
rewardUserWithClicks(clickCount); // Thats All!!😃
}
});
Хотя мне понравилась простота подхода в исходном ответе
Вот моя версия
public abstract class OnDoubleClickListener implements View.OnClickListener {
private static final int TIME_OUT = ViewConfiguration.getDoubleTapTimeout();
private TapHandler tapHandler = new TapHandler();
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
@Override
public void onClick(View v) {
tapHandler.cancelSingleTap(v);
if (tapHandler.isDoubleTap()){
onDoubleClick(v);
} else {
tapHandler.performSingleTap(v);
}
}
private class TapHandler implements Runnable {
public boolean isDoubleTap() {
final long tapTime = System.currentTimeMillis();
boolean doubleTap = tapTime - lastTapTime < TIME_OUT;
lastTapTime = tapTime;
return doubleTap;
}
public void performSingleTap(View v) {
view = v;
v.postDelayed(this, TIME_OUT);
}
public void cancelSingleTap(View v) {
view = null;
v.removeCallbacks(this);
}
@Override
public void run() {
if (view != null) {
onSingleClick(view);
}
}
private View view;
private long lastTapTime = 0;
}
}
Использование такое же, как и в оригинале
view.setOnClickListener(new OnDoubleClickListener() {
@Override
public void onSingleClick(View v) {
}
@Override
public void onDoubleClick(View v) {
}
});
В Котлине вы можете попробовать это,
как будто я использую cardview для нажатия,
(Пример: при двойном щелчке я показываю, что нравится и не нравится.)
cardviewPostCard.setOnClickListener(object : DoubleClickListener() {
override fun onDoubleClick(v: View?) {
if (holder.toggleButtonLike.isChecked) {
holder.toggleButtonLike.setChecked(false) //
} else {
holder.toggleButtonLike.setChecked(true)
}
}
})
и вот ваш класс DoubleClickListener,
abstract class DoubleClickListener : View.OnClickListener {
var lastClickTime: Long = 0
override fun onClick(v: View?) {
val clickTime = System.currentTimeMillis()
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
onDoubleClick(v)
}
lastClickTime = clickTime
}
abstract fun onDoubleClick(v: View?)
companion object {
private const val DOUBLE_CLICK_TIME_DELTA: Long = 300 //milliseconds
}
}
Это версия функции расширения Kotlin:
fun View.setOnDoubleTapListener(action: () -> Unit) {
// instantiate GestureDetectorCompat
val gDetector = GestureDetectorCompat(
this.context,
GestureDetector.SimpleOnGestureListener()
)
// Create anonymous class extend OnTouchListener and SimpleOnGestureListener
val touchListener =
object : View.OnTouchListener, GestureDetector.SimpleOnGestureListener() {
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
gDetector.onTouchEvent(event)
gDetector.setOnDoubleTapListener(this)
return true
}
override fun onDoubleTap(e: MotionEvent?): Boolean {
action()
return true
}
}
this.setOnTouchListener(touchListener)
}
Просто послушайте время предыдущего клика. Вы можете использовать этот класс для прослушивания двойным щелчком мыши.
abstract class DoubleClickListener : View.OnClickListener {
private var lastClickTime: Long = 0
override fun onClick(v: View) {
val clickTime = System.currentTimeMillis()
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
onDoubleClick(v)
lastClickTime = 0
}
lastClickTime = clickTime
}
abstract fun onDoubleClick(v: View)
companion object {
private const val DOUBLE_CLICK_TIME_DELTA: Long = 300 //milliseconds
}
}
И вы можете использовать его следующим образом:
yourView.setOnClickListener(object : DoubleClickListener() {
override fun onDoubleClick(v: View) {
// do to
}
})
Чтобы определить тип касания жеста, можно реализовать что-то встроенное с этим: (здесь projectText - это EditText)
projectText.setOnTouchListener(new View.OnTouchListener() {
private GestureDetector gestureDetector = new GestureDetector(activity, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
projectText.setInputType(InputType.TYPE_CLASS_TEXT);
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
return super.onDoubleTap(e);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
projectText.setInputType(InputType.TYPE_NULL); // disable soft input
final int itemPosition = getLayoutPosition();
if(!projects.get(itemPosition).getProjectId().equals("-1"))
listener.selectedClick(projects.get(itemPosition));
return super.onSingleTapUp(e);
}
});
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
return false; //true stops propagation of the event
}
});