I am trying to write a message to stdout in android every second for which a given button is held(I intend to place a method there later). I use switch in a standard onTouch method to detect button press:
protected void setFab(FloatingActionButton fab) {
fab.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
System.out.println("Button pressed");
handleButtonDown();
return true;
}
case MotionEvent.ACTION_UP: {
System.out.println("Button released");
handleButtonUp();
return true;
}
default:
return true;
}
}
});
}
And global two global booleans:
private boolean captureThreadRunning = false;
private boolean cancelCaptureThread = false;
Which are to stop the looping when the button is released:
public void handleButtonUp() {
cancelCaptureThread = true;
}
However, when I start the worker thread, it gets stuck in an infinite loop, even when the button is released and thus the global boolean should be changed:
public void handleButtonDown() {
System.out.println("Capture thread running: " + captureThreadRunning);
if (!captureThreadRunning) {
System.out.println("Thread starting");
startCaptureThread();
}
}
public void startCaptureThread() {
System.out.println("Thread started");
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
captureThreadRunning = true;
System.out.println("Got to try");
while (!cancelCaptureThread) {
System.out.println("Success");
try {
System.out.println("Falling asleep");
Thread.sleep(1000);
System.out.println("Slept 1000");
} catch (InterruptedException e) {
throw new RuntimeException(
"Interrupted.", e);
}
}
} finally {
System.out.println("got to finally");
captureThreadRunning = false;
cancelCaptureThread = false;
}
}
});
thread.run();
}
Not only that, but the UI also gets frozen, which it certainly shouldn't, cause I am doing everything in a separate thread. When I release the button, the loop should halt, because the boolean gets changed.
I am quite new to both threads and android, so I guess I am just missing something.
But you are indeed executing your code in your main thread. Notice that you are calling
thread.run();
so your execution path enters into an infinite loop. Change it to
thread.start();
That will start a new Thread
I am developing a android app, this is a requirement. I need different functionality on onclick and also different functionality on stop and start method of ontouch. can any one help me? How can i implement this feature in android?
I understand your problem and i hope below code will be help you.
public class MyFragment extends BaseFragment implements View.OnTouchListener {
#Override
public boolean onTouch(final View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
prev_timestamp = System.currentTimeMillis();
case MotionEvent.ACTION_UP:
current_timestamp = System.currentTimeMillis();
upcount++;
if (current_timestamp - prev_timestamp < 250) {
if (upcount == 2) {
upcount = 0;
Log.d(TAG, "click event");
//here code for onClick event
} else {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
if (upcount != 0) {
TOUCH_END=false;
Log.d(TAG, "touch start");
//here code for onTouchStart event
}
super.onPostExecute(aVoid);
}
}.execute();
}
} else {
upcount = 0;
Log.d(TAG, "touch end");
//here code for onTouchEnd event
}
case MotionEvent.ACTION_CANCEL:
//here code for cancel eent if you want
}
return true;
}
}
Source :see example here
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
}
}
}
I'm using a pretty standard loop with sleep that increments by 100 milliseconds each time, but I want to add a text color change halfway through... However, I get an error. Is there a problem in the following code?
public class splashActivity extends Activity {
TextView tv;
LinearLayout ll;
protected boolean _active=true;
protected int _splashTime=5000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
tv=(TextView)findViewById(R.id.textViewSplash);
ll=(LinearLayout)findViewById(R.id.linearLayoutSplash);
Thread splashThread = new Thread(){
#Override
public void run() {
try{
int waited=0;
while (_active && waited < _splashTime){
sleep(100);
if(_active){
waited += 100;
}
if(waited >=2500){
tv.setTextColor(Color.GREEN);
}
}
}
catch(InterruptedException e){
}
finally{
finish();
startActivity(new Intent("com.kleaverdevelopment.splashTest.SplashTest.mainActivity")); //package.package.package.appName.nextActivity
stop();
}
}
};
splashThread.start();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
_active = false;
}
return true;
}
}
You must not update UI elements (TextView in your case) from a background thread. See the article about UI and threading on Android.
So in my further attempts to implement a while loop in android I have come up with the code below :
private boolean connected = false;
private Thread loop = new Thread() {
public void run() {
Looper.prepare();
while (connected) {
do_Something();
}
Looper.loop();
}
};
onCreate() {
//.....
ok = (Button) findViewById(R.id.button1);
ok.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (connected) {
try {
loop.start();
}
catch (Exception e) {
Toast.makeText(getBaseContext(), "Exception caught", Toast.LENGTH_LONG).show();
}
}
return true;
}
});
stop = (Button)findViewById(R.id.button2);
stop.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//disconnects current connection
connected = false;
}
return true;
}
});
}
That is, I am trying to on the touch of the first button have my UI thread switch to the thread that will do_something over and over again until the touch of the second button, in which case the boolean var k will switch off and completely stop the newly created thread from the button press of the first button. I have google'd "threads/handlers/android while loops" but to no avail. Any help towards what I am trying to do would be much appreciated
Simply put, how do I kill the thread that was created via pressing the second button?
Did you tried AsyncTask ? What you can do...start a new AsyncTask on firstButton click and cancel it on secondButton click.
//define global variable
private DoSomething doSomething;
//register firstButton onClickListener
firstButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
//start your asynctask
if(doSomething == null || doSomething.isCancelled()){
doSomething = new DoSomething();
doSomething = doSomething.execute();
}
}
});
//register secondButton onClickListener
secondButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
doSomething.cancel();
}
});
//Inner AsyncTask class
class DoSomething extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
//doSomething();
while(true){
System.out.println(1);
if(isCancelled()){
break;
}
}
return null;
}
}
Note: This is pseudocode...might contain error...just want to give you overview. hope this help.