Differentiate Single click and double click of a imageView in android - android

I have tried the following code to differentiate single click and double click. Single click is ok. When I double click the imageview, code inside both the single click and double click part execute.
private static final long DOUBLE_PRESS_INTERVAL = 250; // in millis
private long lastPressTime;
boolean mHasDoubleClicked;
img_feat_orgn_item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
findDoubleClick();
}
});
private boolean findDoubleClick() {
// Get current time in nano seconds.
long pressTime = System.currentTimeMillis();
// If double click...
if (pressTime - lastPressTime <= DOUBLE_PRESS_INTERVAL) {
mHasDoubleClicked = true;
startActivity(new Intent(
Wv_HomePage.this,
NewDonation.class));
// double click event....
} else { // If not double click....
mHasDoubleClicked = false;
Handler myHandler = new Handler() {
public void handleMessage(Message m) {
boolean mHasDoubleClicked = false;
if (!mHasDoubleClicked) {
// single click event
feature_class_val = listData_Feature_Organization
.get(j);
Intent intent = new Intent(
Wv_HomePage.this,
OrganizationDetails.class);
Bundle b = new Bundle();
b.putString("orgn_name",
feature_class_val.name);
intent.putExtras(b);
startActivity(intent);
}
}
};
Message m = new Message();
myHandler.sendMessageDelayed(m, DOUBLE_PRESS_INTERVAL);
}
lastPressTime = pressTime;
return mHasDoubleClicked;
}

By convention, Android apps don't have double clicking.
Maybe you'd rather use onLongClick?
Although, if you really want double click check out GestureDetector.OnDoubleTapListener

just a little change and my code works fine. I placed the mHasDoubleClicked boolean inside handler, that makes the trouble. The below code works.
img_feat_orgn_item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
findDoubleClick();
if (mHasDoubleClicked) {
Log.v("double click","double click");
startActivity(new Intent(
Wv_HomePage.this,
NewDonation.class));
}
}
});
private boolean findDoubleClick() {
// Get current time in nano seconds.
long pressTime = System.currentTimeMillis();
// If double click...
if (pressTime - lastPressTime <= DOUBLE_PRESS_INTERVAL) {
mHasDoubleClicked = true;
// double click event....
} else { // If not double click....
mHasDoubleClicked = false;
Handler myHandler = new Handler() {
public void handleMessage(Message m) {
if (!mHasDoubleClicked) {
// single click event
feature_class_val = listData_Feature_Organization
.get(j);
Intent intent = new Intent(
Wv_HomePage.this,
OrganizationDetails.class);
Bundle b = new Bundle();
b.putString("orgn_name",
feature_class_val.name);
intent.putExtras(b);
startActivity(intent);
}
}
};
Message m = new Message();
myHandler.sendMessageDelayed(m, DOUBLE_PRESS_INTERVAL);
}
lastPressTime = pressTime;
return mHasDoubleClicked;
}

Here's my code, which seem to work.
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
public class MultiTapImageView extends ImageView{
private GestureDetector gestureDetector;
private MultiTapImageViewListener mListener;
public interface MultiTapImageViewListener {
void onDoubleTap();
void onSingleTap();
}
public MultiTapImageView(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(context, new GestureListener());
}
public void setDoubleTapListener(MultiTapImageViewListener listener){
mListener = listener;
}
#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();
if(mListener != null){
mListener.onDoubleTap();
}
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if(mListener != null){
mListener.onSingleTap();
}
return true;
}
}
}

Try this.
btn.setOnClickListener(new View.OnClickListener() {
volatile int i = 0;
#Override
public void onClick(View v) {
i++;
Handler handler = new Handler();
Runnable r = new Runnable() {
#Override
public void run() {
if (i == 1) {
//single click logic
}
}
};
if (i == 1) {
handler.postDelayed(r, 150);
} else if (i == 2) {
handler.removeCallbacks(r);
i = 0;
//Double click logic
}
}
}
);
Or you can check DOUBLE-TAP example from following URL.
that is used in listView. i hope it is useful for you.
https://nodeload.github.com/NikolaDespotoski/DoubleTapListView/zip/master

btn.setOnClickListener(new View.OnClickListener() {
volatile int i = 0;
#Override
public void onClick(View v) {
i++;
Handler handler = new Handler();
Runnable r = new Runnable() {
#Override
public void run() {
if (i == 1) {
//single click logic
i = 0; //set to zero
}
}
};
if (i == 1) {
handler.postDelayed(r, 300);
} else if (i == 2) {
handler.removeCallbacks(r);
i = 0;
//Double click logic
}
}
}
);

Related

Application restart suddenly

I made simple picture matching game with four Activities After finishing you can play again.
The problem is most of the time the game stops and return to previous
The error:
2020-01-29 15:05:51.001 11438-11438/? E/AndroidRuntime: FATAL
EXCEPTION: main
Process: com.mk_kadish.playandlearn, PID: 11438
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy#4f4586f is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:798)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.app.Dialog.show(Dialog.java:329)
at androidx.appcompat.app.AlertDialog$Builder.show(AlertDialog.java:1007)
at com.mk_kadish.playandlearn.game1.game1_HomeActivity$4.onFinish(game1_HomeActivity.java:284)
at android.os.CountDownTimer$1.handleMessage(CountDownTimer.java:127)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
The Code of one of the files and the other repeats the same:
public class game1_HomeActivity extends AppCompatActivity {
private AllSharedPrefernces allSharedPrefernces;
View view1;
int position1;
private int height;
private int width;
private static final long START_TIME_IN_MILLIS = 120000;
private TextView mTextViewCountDown;
private TextView textViewPairs;
private Button mButtonStartPause;
private Button mButtonReset;
private CountDownTimer mCountDownTimer;
private Long pointsCounter;
private boolean mTimerRunning;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
ArrayList<Game1KeyValue> allpairs;
boolean isMatch=false;
ImageAdapter imageAdapter2;
ImageView curView = null;
private int countPair = 0;
TextView tv6;
TextView myPoints;
TextView temPoints;
public int[] drawable;
int[] pos;
int maxnum=2;
int currentPos = -1;
MediaPlayer mediaPlayerno_ar;
MediaPlayer mediaPlayer_no;
MediaPlayer mediaPlayeryes;
MediaPlayer mediaPlayeryes_ar;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
allSharedPrefernces = new AllSharedPrefernces(this);
setContentView(R.layout.activity_game1__home);
drawable=new int[]
{
R.drawable.yes_ar,
R.drawable.no_ar,
R.drawable.no,
R.drawable.yes,
};
pos=TawleedRandom.walledArray(4);
allpairs = new ArrayList<>();
allpairs.add(new Game1KeyValue(R.drawable.yes,R.drawable.yes_ar));
allpairs.add(new Game1KeyValue(R.drawable.no ,R.drawable.no_ar));
temPoints=findViewById(R.id.l1g1_tempoints);
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
height = displayMetrics.heightPixels;
width = displayMetrics.widthPixels;
tv6 = findViewById(R.id.g2_tv4_pairsCount);
myPoints=findViewById(R.id.g2_tv6_Ponits);
pointsCounter=getPoints();
myPoints.setText(pointsCounter+"");
pointsCounter=0L;
tv6.setText(""+drawable.length /2);
mTextViewCountDown = findViewById(R.id.g1_tv2_text_view_countdown);
textViewPairs=findViewById(R.id.g1_tv2_pairsCount);
mButtonStartPause = findViewById(R.id.button_start_pause);
mButtonReset = findViewById(R.id.button_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mTimerRunning) {
pauseTimer();
} else {
startTimer();
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playAgain(v);
}
});
updateCountDownText();
final ImageAdapter imageAdapter = new ImageAdapter(this,height,width,drawable.length);
GridView gridView = findViewById(R.id.gridView);
gridView.setNumColumns(2);
gridView.setEnabled(false);
RelativeLayout.LayoutParams parameter = (RelativeLayout.LayoutParams) gridView.getLayoutParams();
parameter.setMargins(width * 2 /7, parameter.topMargin, parameter.rightMargin, parameter.bottomMargin); // left, top, right, bottom
gridView.setLayoutParams(parameter);
gridView.setAdapter(imageAdapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (currentPos < 0) {
currentPos = position;
curView = (ImageView) view;
runSuitableMusika(drawable[pos[position]]);
((ImageView) view).setImageResource(drawable[pos[position]]);
view1 = view;
position1 = position;
} else
{
if (currentPos == position)
{
((ImageView) view).setImageResource(R.drawable.questionbg);
}
else
{
runSuitableMusika(drawable[pos[position]]);
for (Game1KeyValue d : allpairs) {
if ((d.key == drawable[pos[currentPos]]) || (d.value == drawable[pos[currentPos]])) {
if (d.key == drawable[pos[currentPos]]) {
if (d.value == drawable[pos[position]]) {
isMatch = true;
d.setUsed(1);
}
break;
} else {
if (d.value == drawable[pos[currentPos]]) {
if (d.key == drawable[pos[position]]) {
isMatch = true;
}
break;
}
}
}
}
((ImageView) view).setImageResource(drawable[pos[position]]);
view1 = view;
if (!isMatch) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
curView.setImageResource(R.drawable.questionbg);
((ImageView) view1).setImageResource(R.drawable.questionbg);
}
}, 600);
}
else if (isMatch) {
isMatch = false;
((ImageView) view).setImageResource(drawable[pos[position]]);
runSuitableMusika(drawable[pos[position]]);
countPair++;
pointsCounter=pointsCounter+1L;
temPoints.setText(pointsCounter+"");
textViewPairs.setText(countPair + "");
if(countPair != maxnum)
{
int curNum=drawable.length;
int[] newArray = new int[curNum-2];
int[] pos1=TawleedRandom.walledArray(curNum-2);
int k=0;
for(int i=0;i<curNum;i++)
{
if(i==position || i==currentPos)
{
}
else
{
newArray[pos1[k]] = drawable[pos[i]];
k++;
}
}
drawable=newArray;
pos=pos1;
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
removeFromGrid();
}
}, 1000);
}
else
if (countPair == maxnum) {
try
{
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
passToNext(view);
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
currentPos = -1;
}
}
});
}
private void removeFromGrid()
{
GridView gridView = findViewById(R.id.gridView);
gridView.setAdapter(null);
imageAdapter2=new ImageAdapter(this,height,width,drawable.length);
gridView.setNumColumns(2);
gridView.setAdapter(imageAdapter2);
currentPos = -1;
}
private void startTimer() {
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
GridView gridView0 = findViewById(R.id.gridView);
gridView0.setVisibility(View.INVISIBLE);
Button btn2=findViewById(R.id.button_start_pause);
btn2.setVisibility(View.INVISIBLE);
AlertDialog.Builder builder = new AlertDialog.Builder(game1_HomeActivity.this);
builder.setCancelable(true);
builder.setTitle(" انتهت اللعبة ");
builder.setMessage("حظ أوفر المرة القادمة");
builder.setNegativeButton("الخروج من اللعبة", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialogInterface, int i) {
backToFirstMethod();
}
});
builder.setPositiveButton("العب مرة أخرى", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = getIntent();
finish();
startActivity(intent);
}
});
builder.show();
}
}.start();
mTimerRunning = true;
mButtonStartPause.setText("إيقاف مؤقت");
mButtonReset.setVisibility(View.INVISIBLE);
GridView gridView = findViewById(R.id.gridView);
gridView.setNumColumns(2);
gridView.setEnabled(true);
}
private void pauseTimer() {
mCountDownTimer.cancel();
mTimerRunning = false;
mButtonStartPause.setText("استئناف");
mButtonReset.setVisibility(View.VISIBLE);
GridView gridView = findViewById(R.id.gridView);
gridView.setNumColumns(2);
gridView.setEnabled(false);
}
private void updateCountDownText() {
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
}
public void passToNext(View view)
{
allSharedPrefernces.writeTimer1(mTimeLeftInMillis);
allSharedPrefernces.saveDbPoints(getPoints());
allSharedPrefernces.storeTempPoints(2L);
startActivity(new Intent(this,game1_secondActivity.class));
finish();
}
public void playAgain(View view)
{
Intent intent = getIntent();
finish();
startActivity(intent);
}
public void backToFirstMethod()
{
startActivity(new Intent(this, indexActivity.class));
finish();
}
public Long getPoints()
{
SchoolDbHelper schoolDbHelper=new SchoolDbHelper(this);
SQLiteDatabase mydatabase=schoolDbHelper.getReadableDatabase();
Long c= schoolDbHelper.getCurrentPoints(11,mydatabase);
return c;
}
Your exceptions shows that you are calling activity that is destroyed or vice versa.
Make sure there is no activity or dialog that is called after destroying that activity.
For more information Check following link
Bad window token, you cannot show a dialog before an Activity is created or after it's hidden
you are trying to show the dialog for a context that no longer exists.
try using this in side your onFinish() method of startTimer()
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity().this);
Also check if activity is visible, like, e.g.
if (getActivity().hasWindowFocus()) {
// create & show dialog here
}

Android gesture is not working in activity

i have some images in the activity i need to implement gesture when i swife images so bellow i have posted my code (there is full code but the gesture is not working)
As bellow is my code (activity class ), implemented gesture
i have created gesture in my code but it is not working,
it is not calling onTouch event also, when i click its not printing any logs
how can i solve this problem.
at the last there is onFling method is there
public class PageView extends AppCompatActivity implements ReadOutTextAnimation.ReadingPageCompleted, OnGestureListener {
public static final String TAG = PageView.class.getName();
private Button mPlayBtn;
private Button mBtnNext;
private Button mBackBtn;
private ImageView mBgImage;
private Integer mPageNumber;
private String mMainFolder;
private Handler handler = new Handler();
private Runnable mTimerCallback;
private boolean mIsPaused;
private GestureDetector gestureDetector;
private boolean isoneclick;
private LinearLayout mLenearLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_page_view);
gestureDetector = new GestureDetector(this, this);
// mLenearLayout = (LinearLayout)findViewById(R.id.clickhide);
// mLenearLayout.setVisibility(View.INVISIBLE);
isoneclick = true;
if (AppManager.getInstance().getNeedsBGMusic())
SoundManager.getInstance().resumeBackgroundMusic();
mPageNumber = 1;
mMainFolder = String.valueOf(BaseApplication.getInstance().getAppContext().getDir("Books", Context.MODE_PRIVATE));
mMainFolder += "/Downloads" + AppManager.getInstance().getCurrentBook().getBookId().toString();
mTimerCallback = new Runnable() {
#Override
public void run() { loadNextPage(); }
};
ReadOutTextAnimation.getInstance().setListner(this);
mBgImage = findViewById(R.id.page_view_bgimage);
mBgImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onSlideViewButtonClick(v);
//loadNextPage();
}
});
//Next Button Action
mBtnNext = findViewById(R.id.page_view_nextbtn);
mBtnNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loadNextPage();
}
});
//Back Button Action
mBackBtn = findViewById(R.id.page_view_backbtn);
mBackBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
loadPreviousPage();
}
});
//Play & Pause Button Action
mPlayBtn = findViewById(R.id.page_view_palybtn);
mPlayBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(mIsPaused){
resumeApp();
} else {
pauseApp();
}
}
});
loadPageDetails();
enableBtn();
Button mDoneBtn = findViewById(R.id.page_view_done);
mDoneBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onBackPressed();
}
});
}
#Override
protected void onPause() {
super.onPause();
pauseApp();
}
private void enableBtn(){
if(mPageNumber > 1) {
mBackBtn.setEnabled(true);
mBtnNext.setEnabled(true);
mBackBtn.setAlpha(.9f);
}else {
mBackBtn.setAlpha(.5f);
mBtnNext.setEnabled(true);
}
}
private void loadPageDetails(){
ReadOutTextAnimation.getInstance().stopReadOut();
PageDetailProperties mCurrentPage = AppManager.getInstance().getCurrentBook().getPageDetailsForNumber(mPageNumber);
if (mCurrentPage.getPageAudioDurationJSON() == null)
{
loadNextPage();
return;
}
File imgFile = new File(mMainFolder,ContentfulConstants.PAGE_IMAGE+ mPageNumber.toString() +".png");
if (imgFile.exists()){
Bitmap bookImage = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
mBgImage.setImageBitmap(bookImage);
}
File soundFile = new File(mMainFolder,ContentfulConstants.PAGE_AUDIO+mPageNumber.toString()+".mp3");
if (soundFile.exists()){
SoundManager.getInstance().playPageAudio(soundFile.getPath());
}
ReadOutTextAnimation.getInstance().startReadOut(mPageNumber);
mIsPaused = false;
mPlayBtn.setBackgroundResource(R.drawable.pause);
}
private void loadNextPage(){
handler.removeCallbacks(mTimerCallback);
mPageNumber++;
if (mPageNumber > AppManager.getInstance().getCurrentBook().getPageDetail().size()){
mPageNumber = AppManager.getInstance().getCurrentBook().getPageDetail().size();
ReadOutTextAnimation.getInstance().setListner(null);
ReadOutTextAnimation.getInstance().stopReadOut();
SoundManager.getInstance().stopALL();
Intent intent = new Intent(this , BookCompleteView.class);
this.finish();
startActivity(intent);
} else {
loadPageDetails();
enableBtn();
}
}
private void loadPreviousPage(){
mPageNumber--;
handler.removeCallbacks(mTimerCallback);
if (mPageNumber < 1){
mPageNumber = 1;
} else {
loadPageDetails();
}
enableBtn();
}
#Override
public void readingPageCompleted() {
handler.postDelayed(mTimerCallback, 700);
}
#Override
public void onBackPressed() {
ReadOutTextAnimation.getInstance().setListner(null);
handler.removeCallbacks(mTimerCallback);
ReadOutTextAnimation.getInstance().stopReadOut();
SoundManager.getInstance().stopALL();
super.onBackPressed();
}
#Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
#Override
public void onShowPress(MotionEvent motionEvent) {
}
#Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
#Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
#Override
public void onLongPress(MotionEvent motionEvent) {
}
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "touch" , Toast.LENGTH_SHORT).show();
gestureDetector.onTouchEvent(motionEvent);
return false;
}
#Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
System.out.println("Gesture event touch ");
if (motionEvent.getX() - motionEvent1.getX() > 50) {
loadNextPage();
return true;
}
if (motionEvent1.getX() - motionEvent.getX() > 50) {
loadPreviousPage();
return true;
}
return false;
}
}
If I understood you correctly. You have to set touch listener for your image view, instead of activity.
mBgImage.setOnTouchListener(yourTouchListener);

Single click and double click of a button in android

In my application i have a button. After single and double clicking of the button will perform separate operation. How can i do that? Thanks
Well it is simple just override.
onClick method of OnClickListener
public abstract class DoubleClickListener implements View.OnClickListener {
private static final long DEFAULT_QUALIFICATION_SPAN = 200;
private boolean isSingleEvent;
private long doubleClickQualificationSpanInMillis;
private long timestampLastClick;
private Handler handler;
private Runnable runnable;
public DoubleClickListener() {
doubleClickQualificationSpanInMillis = DEFAULT_QUALIFICATION_SPAN;
timestampLastClick = 0;
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
if (isSingleEvent) {
onSingleClick();
}
}
};
}
#Override
public void onClick(View v) {
if((SystemClock.elapsedRealtime() - timestampLastClick) < doubleClickQualificationSpanInMillis) {
isSingleEvent = false;
handler.removeCallbacks(runnable);
onDoubleClick();
return;
}
isSingleEvent = true;
handler.postDelayed(runnable, DEFAULT_QUALIFICATION_SPAN);
timestampLastClick = SystemClock.elapsedRealtime();
}
public abstract void onDoubleClick();
public abstract void onSingleClick();
}
Usage
button.setOnClickListener(new DoubleClickListener() {
#Override
public void onDoubleClick() {
Log.i("onClick", "double");
}
#Override
public void onSingleClick() {
Log.i("onClick", "single");
}
});
You may need to create a delay variable which will differenciate between single click and double click.
See this code,
private static final long DOUBLE_PRESS_INTERVAL = 250; // in millis
private long lastPressTime;
private boolean mHasDoubleClicked = false;
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// Get current time in nano seconds.
long pressTime = System.currentTimeMillis();
// If double click...
if (pressTime - lastPressTime <= DOUBLE_PRESS_INTERVAL) {
Toast.makeText(getApplicationContext(), "Double Click Event", Toast.LENGTH_SHORT).show();
mHasDoubleClicked = true;
}
else { // If not double click....
mHasDoubleClicked = false;
Handler myHandler = new Handler() {
public void handleMessage(Message m) {
if (!mHasDoubleClicked) {
Toast.makeText(getApplicationContext(), "Single Click Event", Toast.LENGTH_SHORT).show();
}
}
};
Message m = new Message();
myHandler.sendMessageDelayed(m,DOUBLE_PRESS_INTERVAL);
}
// record the last time the menu button was pressed.
lastPressTime = pressTime;
return true;
}
You may want to consider not using a DoubleTap. It is not a normal Android behavior.
When I first started programming on the Android, I kept running into things that were really "hard" to do on the android. Over time, I've found that many of them were difficult because they were a pretty bad idea.
If you are porting an iOS app, or emulating an iOS app's behavior, you may want to consider converting the UI over to Android style behaviors and use a longPress or other 'androidy' gestures.
Here is a similar question and answer:
Android: How to detect double-tap?
You have to implement GestureDetector and put your code in single/double click.
TestActivity.java
iv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//putyour first activity call.
}
}
iv.setOnTouchListener(new OnTouchListener() {
GestureDetector gestureDetector = new GestureDetector(context, new MyGestureDetector(context));
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
Now you have to create GestureDetector.java class.
public class MyGestureDetector extends SimpleOnGestureListener {
public Context context;
public String phno;
public MyGestureDetector(Context con) {
this.context=con;
}
#Override
public boolean onDown(MotionEvent e) {
return super.onDown(e);
}
#Override
public boolean onDoubleTap(MotionEvent e) {
System.out.println("in Double tap");
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
System.out.println("in single tap up");
//put your second activity.
return super.onSingleTapUp(e);
}
}
Thanks to #NikolaDespotoski.
You can check DOUBLE-TAP example from following URL.
that is used in listView. i hope it is useful for you.
https://nodeload.github.com/NikolaDespotoski/DoubleTapListView/zip/master
Though it's too late, but anyone can figure out if they see this.
int number_of_clicks = 0;
boolean thread_started = false;
final int DELAY_BETWEEN_CLICKS_IN_MILLISECONDS = 250;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
++number_of_clicks;
if(!thread_started){
new Thread(new Runnable() {
#Override
public void run() {
thread_started = true;
try {
Thread.sleep(DELAY_BETWEEN_CLICKS_IN_MILLISECONDS);
if(number_of_clicks == 1){
client.send(AppHelper.FORMAT_LEFT_CLICK);
} else if(number_of_clicks == 2){
client.send(AppHelper.FORMAT_DOUBLE_CLICK);
}
number_of_clicks = 0;
thread_started = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
});
Explanation:
Before the button click, number_of_click is initialized to 0.
thread_started is a flag detecting if the thread is started before or not.
Now, on button click, increase the number of button click by incremental operator.
check if the thread is previously started or not, if not, then start the thread.
on thread, apply your logic by using the number_of_clicks. and the thread will wait for next milliseconds and then will go through your logic.
So, now you can apply as many clicks as you want.
This is #saksham's answer in Kotlin.
abstract class DoubleClickListener : View.OnClickListener {
private val DEFAULT_QUALIFICATION_SPAN = 200L
private var isSingleEvent = false
private val doubleClickQualificationSpanInMillis =
DEFAULT_QUALIFICATION_SPAN
private var timestampLastClick = 0L
private val handler = Handler(Looper.getMainLooper())
private val runnable: () -> Unit = {
if (isSingleEvent) {
onSingleClick()
}
}
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - timestampLastClick < doubleClickQualificationSpanInMillis) {
isSingleEvent = false
handler.removeCallbacks(runnable)
onDoubleClick()
return
}
isSingleEvent = true
handler.postDelayed(runnable, DEFAULT_QUALIFICATION_SPAN)
timestampLastClick = SystemClock.elapsedRealtime()
}
abstract fun onDoubleClick()
abstract fun onSingleClick()
}
Solving this by inherit from the View.OnClickListener and checking the click time to distinguish the single click or double click, this also solve the problem of fast clicking. This solution will bring minor code change, just replace View.OnClickLister. You also can override the getGap() to redefine the time between two clicks.
import android.os.SystemClock;
import android.view.View;
/*****
* Implement to fix the double click problem.
* Avoid the fast double click for button and images.
*/
public abstract class OnSingleClickListener implements View.OnClickListener {
private long prevClickTime =0;
#Override
public void onClick(View v) {
_onClick(v);
}
private synchronized void _onClick(View v){
long current = SystemClock.elapsedRealtime();
if(current-prevClickTime> getGap()){
onSingleClick(v);
prevClickTime = SystemClock.elapsedRealtime();
}else {
onDoubleClick(v);
prevClickTime = 0;
}
}
public abstract void onSingleClick(View v);
public abstract void onDoubleClick(View v);
/********
*
* #return The time in ms between two clicks.
*/
public long getGap(){
return 500L; //500ms
}
}
double click and single click in android
A solution that fits almost all versions
Detect the type of event within the standard duration you want to define It can detect the event in an accelerated and
sequential manner, such as passing a video for a certain period
//general definition
private CountDownTimer countDownTimer;
private int click_duble = 1;
button.setOnClickListener(view -> {
if (countDownTimer == null) {
float Second= (float) 0.25; //Detecting the type of event within a quarter of a second
countDownTimer= new CountDownTimer((long) (Second * 1000), 50) {
#Override public void onTick(long l){}
#Override
public void onFinish() {
if (click_duble >= 2) {
Toast.makeText(player_fb.this, "Double", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(player_fb.this, "Single", Toast.LENGTH_SHORT).show();
}
click_duble = 1;
countDownTimer = null;
}};countDownTimer.start();
}else
click_duble += 1;
});
The up solution cannot work for multi click, i test it but failed.
So i suggest to use RxBinding with ProgressDialog.
when click button, the progressDialog show setting it cannot be cancel, Fix it.
I also got the same problem once
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(isFmOn()){
//stopFM
}else{
//do other things
}
}
}
when I clicked the Button,FM stopped;but when I double clicked,FM did not stop.The problem was that single and double clicking of the button ,the value of isFmOn() was difference.
I sloved the problem using this:
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread.sleep(500);//500ms was enough to finish stopFM before the second click
if(isFmOn()){
//stopFM
}else{
//do other things
}
}
}

Implement double click for button in Android

How can I implement double click for a button in Android?
Should I use OnDoubleTapListener?
int i = 0;
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
i++;
Handler handler = new Handler();
Runnable r = new Runnable() {
#Override
public void run() {
i = 0;
}
};
if (i == 1) {
//Single click
handler.postDelayed(r, 250);
} else if (i == 2) {
//Double click
i = 0;
ShowDailog();
}
}
});
private long lastTouchTime = 0;
private long currentTouchTime = 0;
..
#Override
public void onClick(View view) {
lastTouchTime = currentTouchTime;
currentTouchTime = System.currentTimeMillis();
if (currentTouchTime - lastTouchTime < 250) {
Log.d("Duble","Click");
lastTouchTime = 0;
currentTouchTime = 0;
}
}
This is probably a good place to start:
Android: How to detect double-tap?
I recommend switching to a more native way like long press (answer to linked question) or something more creative (using multi-touch), unless you are bent on the Windows default double-click way of doing things?
You may have a valid reason though - double clicking is after all faster than long press.
I wrote this for popping up a Toast message on a double click in a mapping application:
private long lastTouchTime = -1;
#Override
public boolean onTouchEvent(MotionEvent e, MapView mapView) {
GeoPoint p = null;
if (e.getAction() == MotionEvent.ACTION_DOWN) {
long thisTime = System.currentTimeMillis();
if (thisTime - lastTouchTime < 250) {
// Double click
p = mapView.getProjection().fromPixels((int) e.getX(), (int) e.getY());
lastTouchTime = -1;
} else {
// too slow
lastTouchTime = thisTime;
}
}
if (p != null) {
showClickedLocation(p);// Raise a Toast
}
return false;
}
This is a good site for performing double click...
I used it and worked.
http://mobile.tutsplus.com/tutorials/android/android-gesture/
I used it and worked:
public class DoubleClickTest extends Activity {
String TAG = "DoubleOrSingleClickTest";
private boolean waitDouble = true;
private static final int DOUBLE_CLICK_TIME = 350; // double click timer
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.double_click_test);
Button button = (Button) findViewById(R.id.buttonDoubleOrSingleClicked);
button.setOnClickListener(listenerDoubleOrSingle);
}
View.OnClickListener listenerDoubleOrSingle = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (waitDouble == true) {
waitDouble = false;
Thread thread = new Thread() {
#Override
public void run() {
try {
sleep(DOUBLE_CLICK_TIME);
if (waitDouble == false) {
waitDouble = true;
singleClick();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
} else {
waitDouble = true;
doubleClick();
}
}
};
// single event
private void singleClick() {
Log.i(TAG, "singleClick");
}
// double event
private void doubleClick() {
Log.i(TAG, "doubleClick");
}
}
It comes from "https://elingwange.iteye.com/blog/1613177"
Create your own DoubleTapListener
You can create a DoubleTapListener by inheriting View.OnClickListener and adding a Callback of your listener.
MyDoubleClickListener.class
public class MyDoubleClickListener implements View.OnClickListener{
private boolean isRunning= false;
private int resetInTime =500;
private int counter=0;
private DoubleClickCallback listener;
public DoubleTapListener(Context context)
{
listener = (DoubleClickCallback)context;
}
#Override
public void onClick(View v) {
if(isRunning)
{
if(counter==1) //<-- makes sure that the callback is triggered on double click
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();
}
}
}
DoubleClickCallback.class
public interface DoubleClickCallback {
public void onDoubleClick(View v);
}
And you are done. You can use this Listener in any Activity.
How do I use this DoubleClickListener in my Activity?
Implement Callback in your activity and override the method.
public class MainActivity extends AppCompatActivity implements MyDoubleClickListener{
private Button button;
#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 listener
}
#Override
public void onDoubleClick(View v) {
// Toast to show double click
}
}
Important point is using this concept you can create any kind of listener (Triple-click listener)
Relevant Links:
See the full working code HERE

Android: How to detect double-tap?

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:
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?
You can use the GestureDetector. See the following code:
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.
As a lightweight alternative to GestureDetector you can use this class
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) {
}
});
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.
combining "Bughi" "DoubleClickListner" and "Jayant Arora" Timer in one contained class:
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) {
}
});
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);
}
});
Double-tap and Single-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).
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).
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, this and other answers).
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.
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 both onSingleTapUp and onDoubleTap will be called if there is a double-tap. (This is essentially what bughi's answer does 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.
If the single-tap delay is not acceptable for you then you have a couple options:
Accept that both onSingleTapUp and onDoubleTap 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) {
}
}
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.
Edit:
See it here:
https://github.com/NikolaDespotoski/DoubleTapListView or
https://github.com/NikolaDespotoski/DoubleTapListViewHandler
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:
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
}
});
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();
}
}
}
});
Improvised dhruvi code
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();
}
My solution, may be helpful.
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;
}
}
If you are using Kotlin then you can do it like this:
I spend a lot of time to convert this code to Kotlin hope it save someone's time
Create a gesture detector:
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
}
})
Assign to any of your view:
IPAddress.setOnTouchListener { v, event ->
return#setOnTouchListener gestureDetector.onTouchEvent(event)
}
This is a solution that wait if there is a second clic before executing any action
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++;
}
}
});
Realization single and double click
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);
}
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 implement double-tap using the GestureDetectorCompat class.
in this sample when double-tap on textview you can do your logic.
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 listener etc
In this example, I have implemented the DoubleTap Listener with a Thread.
You can add my listener with any View object as you do with any ClickListener.
Using this approach you can easily pull off any kind of click listener.
yourButton.setOnClickListener(new DoubleTapListener(this));
1) My Listrener class
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) Listener Callback
public interface DoubleTapCallback {
public void onDoubleClick(View v);
}
3) Implement in your Activity
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+"");
}
Relevant link:
You can see the full working code HERE
Solution by bughi & Jayant Arora for copypast:
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);}
Equivalent C# code which i used to implement same functionality and can even customize to accept N number of Taps
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;
}
}
}
Currently Above code accepts 7 as number of taps before it raises the View Tapped event.
But it can be customized with any number
I have implemented a simple custom method using kotlin coroutines (for java can be done via threads).
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
}
}
I created a simple library to handle this. it can also detect more than two clicks (it all depends on you). after you import the ClickCounter class, here is how you use it to detect single and multiple clicks:
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!!😃
}
});
In Kotlin you can try this,
like i am using cardview for clicking,
(Example : on double click i perform like and dislike.)
cardviewPostCard.setOnClickListener(object : DoubleClickListener() {
override fun onDoubleClick(v: View?) {
if (holder.toggleButtonLike.isChecked) {
holder.toggleButtonLike.setChecked(false) //
} else {
holder.toggleButtonLike.setChecked(true)
}
}
})
and here is your DoubleClickListener class,
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
}
}
While I liked the simplicity of the approach in the original answer
Here is my version
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;
}
}
Usage is same as the original
view.setOnClickListener(new OnDoubleClickListener() {
#Override
public void onSingleClick(View v) {
}
#Override
public void onDoubleClick(View v) {
}
});
A simple way to do this in Kotlin:
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
}
})
To detect the type of gesture tap one can implement something inline with this
(here projectText is an 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
}
});
This is the Kotlin extension function version:
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)
}
Kotlin w/o extra class
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 w/o extra class
#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);
}
});
// ...
}

Categories

Resources