Hello I am trying to perform some actions while the user is holding down a button.
My problem is that my Runnable wont "run".
Here is my code:
#Override
public boolean onLongClick(View v) {
final Runnable r = new Runnable()
{
public void run()
{//do the forwarding logic here
int test = 0;
if(holdingDown)
test++;
else
return;
Log.i("test", test+"");
}
};
r.run();
}
return false;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
{
holdingDown= false;
Log.i("holdingDown", "false");
break;
}
}
return false;
}
The onTouch is for detecting when the user stop pressing the button. When I look at my logs I see at that the Runnable runs only once.
My test log get only the value 1.
The log call for Log.i("holdingDown", "false") is getting triggered at the right time, only when I stop touching the button.
Why is it that my Runnable won't run? Thanks.
EDIT:
I tried this code:
#Override
public boolean onLongClick(View v) {
// TODO Auto-generated method stub
holdingDown = true;
new Thread(new Runnable() {
#Override
public void run() {
if(holdingDown)
{
int test = 0;
test++;
Log.i("test", test+"");
}
else
return;
}
}).start();
return false;
}
Its till not working.
You could try using a Thread instead of a Runnable like this:
Thread thread = new Thread() {
#Override
public void run() {
//code you want to run on long press
} };
thread.start();
OR
You could try putting the Runnable inside a Thread like this:
Thread thread = new Thread(new Runnable() {
public void run() {
// code you want to run on long press
}
});
thread.start();
UPDATE: - try this?
#Override
public boolean onLongClick(View v) {
// TODO Auto-generated method stub
holdingDown = true;
new Thread(new Runnable() {
#Override
public void run() {
if (holdingDown) {
int test = 0;
test++;
Log.i("test", test + "");
} else {
Log.i("test", "else");
}
return;
}
}).start();
return false;
}
You don't do r.run() to start a thread, that only runs it once.
You either do new Thread(r).start(); or you use a ScheduledExecutorService.
You can create a class called HoldingDown overriding Runnable and instantiate a Thread object passing a new instance of your HoldingDown and call the start method.
Also, you can use the mousePressed and mouseReleased events of MouseListener.
You should start a new Thread in your mousePressed, store the instance somewhere and stop it on mouseReleased.
See the documentation for more details.
Related
After click button I would like to change its color, then wait one second and change its color back.
This is my code:
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
t.join();
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
}
It doesn't work. I've checked it with more complex code and debugger and it looks like all UI changes are made collectively after finish this function.
I've found this thread: apply ui changes immediately and tried to put setBackgroundColor() and setClickable() into runOnUiThread function:
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
}
});
But it also doesn't work. What should I do?
Something like this :
private final Handler handler = new Handler();
public void click(final View view) {
view.setBackgroundColor(Color.parseColor("#0000ff"));
view.setClickable(false);
handler.postDelayed(() -> {
view.setBackgroundColor(Color.parseColor("#e2e2e2"));
view.setClickable(true);
}, 1000);
}
#Override protected void onPause() {
super.onPause();
handler.removeCallbacks(null);
}
The question is not very clear. However, I am trying to summarize the question that I have understood from your question.
You are trying to set a button's background color on clicking on it and change it back after some time. If this is the situation, then I think your idea of how threads work is wanting.
In your code, the button will change the color immediately as the sleep that you are using is running in another thread (other than UI thread). The code is executed correctly, however, you cannot see the effect of the Thread.sleep as its running in a separate thread.
So all you need to do here is to change the background color again inside the thread. Modify your code like the following.
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
This should work.
I have created a demo trying to show what the code will do.
However, using Handler in case of updating UI elements in this specific case is recommended. Please see the comments below.
public void click(final View view) throws InterruptedException {
final Button btn = findViewById(view.getId());
btn.setBackgroundColor(Color.parseColor("#0000ff"));
btn.setClickable(false);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
btn.setClickable(true);
}
}, 1000);
}
Not sure why that wouldn't work, but I've done something similar with
delayHandler = new Handler();
delayHandler.postDelayed(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
//change stuff on ui
}
});
}
}, 1000);
if that doesn't work the only other functional difference in my code is that instead of btn being a final Button it's a private global variable in my activity.
Hope the following code will help :
btn.setBackgroundColor(Color.RED); // color you want for a second
new CountDownTimer(1000, 1000) {
#Override
public void onTick(long arg0) {
// TODO Auto-generated method stub
}
#Override
public void onFinish() {
btn.setBackgroundColor(Color.BLUE); //to change back color to prior state
}
}.start();
Try this,i think it's work for you..
final Button bPdf = findViewById(R.id.pdf);
bPdf.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
bPdf.setBackgroundColor(Color.parseColor("#0000ff"));
new CountDownTimer(1000, 50) {
#Override
public void onTick(long arg0) {
// TODO Auto-generated method stub
}
#Override
public void onFinish() {
bPdf.setBackgroundColor(Color.parseColor("#e2e2e2"));
}
}.start();
}
});
I am trying to have a thread that waits for user input for 3 seconds, if the user clicks the button execute "done" and if he doesn`t click in 3 seconds execute "not". I searched through stackoverflow but couldn't find an exact and simplified answer. Sorry if this is a repeat and/or noob question.
Here is the code I have for the moment but feel free to offer me another way, couldn't solve it with this;
private Thread thread;
TextView maintext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MainActivity myActivity = this;
maintext = (TextView) findViewById(R.id.mainText);
}
public void start(View view){
changeText("Waiting.");
thread= new Thread(){
#Override
public void run(){
try {
synchronized(this){
wait(3000);
}
}
catch(InterruptedException ex){
}
}
};
thread.start();
}
public void done(){
maintext.setText("You pressed the button in 3 seconds");
}
public void not(){
maintext.setText("You failed");
}
public void changeText(String text){
maintext.setText(text);
}
public void click(View view){
synchronized(thread){
thread.notifyAll();
}
}
How about a CountDownLatch?
Construction (in your class instance variable declaration, i.e. the place where thread is declared now):
CountDownLatch latch = new CountDownLatch(1);
In your waiting thread (called start), instead of creating and starting that Thread:
// wait for it to be notched or for timeout
boolean buttonPressed = latch.await(3, TimeUnit.SECONDS);
if (buttonPressed) {
....
}
And in your button click listener (that is not shown in your post, usually it's configured like clickButton.setOnClickListener( new OnClickListener() {):
latch.countDown();
Later addition
But please note that if that start() method is run in a UI thread, waiting on it will simply freeze the UI which is not what you want. Another approach would be to use something like events. Define two events: 'three seconds passed' and 'the button was pressed' and process them.
Instance variables:
private boolean buttonPressed = false;
private boolean expired = false;
private final Object monitor = new Object();
Timer initialization:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
synchronized (monitor) {
if (!buttonPressed) {
// change text to 'You failed'
expired = true;
timer.cancel();
}
}
}
}, 3000);
In your button click listener:
synchronized (monitor) {
if (!expired) {
// change text to 'You pressed the button in 3 seconds'
buttonPressed = true;
timer.cancel();
}
}
There is no need to block any thread.
May be this answer will help you
private boolean isActive; //globally declare
changeText("Waiting.");
Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (!isActive) {
isActive = true;
}
}
};
handler.sendEmptyMessageDelayed(0,3000);
public void onClick(View v) {
if (!isActive) {
done();
} else {
not();
}
}
public void done() {
mainText.setText("You pressed the button in 3 seconds");
}
public void not() {
mainText.setText("You failed");
}
public void changeText(String text) {
mainText.setText(text);
}
I hope this will resolve your issue.
I am trying to do this simple task. I have two buttons called START and STOP, I want to execute some task in loop by clicking on START and want to exit from this loop by clicking STOP.
Code-
public class DrawPath extends Activity implements View.OnClickListener {
ArrayList<LatLng> positions = new ArrayList<LatLng>() ;
static int c=1;
Location location;
GoogleMap mMap;
Button btStart,btStop;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.drawpath);
initializeVar();
btStart.setOnClickListener(this);
btStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
c = 0;
System.out.println("tested2");
}
});
}
private void initializeVar()
{
btStart=(Button)findViewById(R.id.btStart);
btStop=(Button)findViewById(R.id.btStop);
}
#Override
public void onClick(View v) {
getupdate(1);
}
private void getupdate(int d) {
c = d;
CurrentPosition currentPosition = new CurrentPosition(this);
if (c == 0) {
System.out.println("Done");
} else {
location = currentPosition.getCurrentLocation();
LatLng pos = new LatLng(location.getLatitude(), location.getLongitude());
positions.add(pos);
System.out.println("running");
try {
Thread.sleep(5000);
getupdate(c);
} catch (Exception e) {
}
}
}
}
Somebody please share any idea how to achieve it.
You can use Handler with Runnable to stop your thread after STOP button click.
I am giving you hint use following code according to your requirement
Handler handler = new Handler();
Runnable runable = new Runnable({
#Override
public void run(){
// count
handler.postDelayed(this, 1000);
}
});
Now you can call following line from your btnStop.onClick().
handler.removeCallbacks(runable);
Check this for more details on Handler and Runnable
What I suggest it create a inner class which extends Thread and according to user's action start and stop the thread. here is an example
class DrawPath extends Activity implements View.OnClickListener {
MyThread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawpath);
initializeVar(); //not in my code so you add it
btStart.setOnClickListener(this);
btStop.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btStart:
if (thread == null) {
thread = new MyThread();
thread.start();
}
break;
case R.id.btStop:
if (thread != null) {
thread.interrupt();
}
break;
}
}
class MyThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
//your stuff goes here or before sleep
} catch (InterruptedException e) {
e.printStackTrace();
thread = null;
break;
}
}
}
}
//whey interrupt here bcz infinite loop will be running until and unless you stop it.
#Override
protected void onDestroy() {
super.onDestroy();
if (thread != null)
thread.interrupt();
}
}
I saw your code which needs little more improvements that's why I wrote this big file :)
Suggestions :
Checkout the implementation of onClickListener.
Stopping thread at onDestroy() because thread will be running even after you close your application, so you need to stop when you
come out (destroyed) of your main activity.
I am simulating a camera feed and have a button that goes through an infinite loop of images.
It starts when the button is clicked, when the button is clicked it is supposed to stop the thread until the button is clicked again.
Right now it starts and loops fine, but when I click it again It crashes
on click
public void onClick(View v) {
switch(v.getId()){
case R.id.cam1btn:{
if(thread1 == null){
thread1 = new Thread(){
public void run(){
Cam1();
}
};
thread1.start();
}
else
{
thread1.stop();
thread1=(null);
}
}
break;
Cam1 function
protected void Cam1() {
int i=0;
do{
System.out.println("got into loop");
for(int x=0;x<4;x++){
//imgFeed1.setImageAlpha(camFeed1[x]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int temp = x;
mCam1Handler.post(new Runnable(){
public void run(){
imgFeed1.setImageResource(camFeed1[temp]);
}
});
}
}while(i == 0);
}
logcat
You should not forcibly stop the thread in general. Thread has to stop by completing its task.
In your case, you need to define i = 0 in a instance scope and set that to some value when user clicks stop button, with that your while loop ends and the thread completes its task.
I would rather suggest you to maintain a boolean flag instead of an int.
UPDATE
Okay, then your you should have a flag and toggle that ex: toRun = false for the first time, when the user clicks the button you do toRun = !toRun;. In your run method there should be a while(toRun) {
//repeat your task
}
just to help you understand Rp- answer:
first declare boolean toRun outside your thread because it is a variable that is accessed by two threads(UI and your created) you must use 'synchornize` block to access it and read and write it.
boolean toRun = false;
public void onClick(View v) {
switch(v.getId()){
case R.id.cam1btn:{
boolean isThreadRunning;
synchronize(toRun){
isThreadRunning = toRun;
}
if(!isThreadRunning){
toRun =true; // here you do not need synchronize block because the thread has not been created.
thread1 = new Thread(){
public void run(){
Cam1();
}
};
thread1.start();
}
else
{
synchornize(toRun){
toRun = false;
}
}
}
break;
and in your cam1:
protected void Cam1() {
boolean myRun;
do{
System.out.println("got into loop");
for(int x=0;x<4;x++){
//imgFeed1.setImageAlpha(camFeed1[x]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int temp = x;
mCam1Handler.post(new Runnable(){
public void run(){
imgFeed1.setImageResource(camFeed1[temp]);
}
});
}
synchronize(toRun){
myRun = toRun;
}
}while(myRun);
}
Is there a way I can do this without having to create new timers and tasks? My code basically scans for wifi signals every 10 seconds. To ensure that the scan returns new results, I used another support class. Can someone verify that there aren't any obvious errors with this as well?
//Inside Button to start scanning
final int DELAY = 10000;
final Handler handler = new Handler();
chkScan.setOnClickListener(new View.OnClickListener() {
final Handler handler = new Handler();
ReceiverWifi = new WifiReceiver();
WIFI_Manager = new wifiScanner();
registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
final Timer timer = new Timer();
final TimerTask task = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
mainWifi.startScan();
if ((WIFI_Manager.resultsAvailable())) {
processResults();
}
// to ensure results come from latest scan
// say there are no new results as of now
WIFI_Manager.waitForNextScan();
}
}
);
timer.schedule(task, 0, DELAY);
}
}
});
}
public void processResults() {
results = mainWifi.getScanResults();
WIFI_Manager.pause() //stop getting wifi results
//continue to process here
//
//
//I wish to put a button here, but and wait for user input before continuing
//but scans continue..
chkLabel.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
}
//more code
// ...
WIFI_Manager.resume();
}
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent intent) {
WIFI_Manager.getNewResults();
}
}
public class wifiScanner {
public wifiScanner() {
Pause = false;
new_results = false;
}
public boolean resultsAvailable() {
return new_results;
}
public void waitForNextScan() {
new_results = false;
}
public void getNewResults() {
new_results = true;
}
public boolean onPause() {
return Pause;
}
public void pause() {
unregisterReceiver(receiverWifi);
Pause = true;
}
public void resume() {
registerReceiver(receiverWifi, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
Pause = false;
}
// pause: false- access to scan results are allowed
// pause: true- cannot access scan results
private boolean Pause;
// new_results: false- no new WIFI_resuls
// new_results: true - there are new results to be processed
private boolean new_results;
}
Instead of using a Timer, create a boolean field named stop, and use a while(!stop) loop with Thread.sleep(1000) at the end of an iteration. Your processResults() method will do stop = true; and in your restart button's listener do a stop=false;
But I think it's dirtier than use a Timer ;)