I'm making an app where some sort of widgets are created on the fly and can be re-arranged with drag and drop. I'm trying to implement 2 things:
Long press to initiate drag and drop
short press(or click) to open a menu to change some settings.
But i'm having a problem where I can't cancel the code in handler's postdelayed() function. I'm using the following code to make it happen.
_sliders_item[_sliders_counter].setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View v, MotionEvent event) {
Handler hndlr = new Handler();
Runnable _run = new Runnable() {
#Override
public void run() {
ClipData data = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
v.startDrag(data, shadowBuilder, v, 0);
v.setVisibility(View.INVISIBLE);
_t1.setText("executed");
_t1.show();
}
};
if (_enable_editor) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
hndlr.postDelayed(_run,1000);
//return true;
}
if (event.getAction() == MotionEvent.ACTION_UP){
_t1.setText("menu");
_t1.show();
hndlr.removeCallbacks(_run);
//return true;
}
}
return _enable_editor;
}
});
But the problem is, When I long press the object, it works fine. I can initiate the drag and drop. But when I short press it, The code for ACTION_UP event executes but the code inside the runnable still executes. How can I cancel the runnable code when I short press the object?
You have to keep references to your Runnables and Handler and use Handler#removeCallbacks().
So basically:
Handler h = new Handler();
Runnable r = new Runnable() { /* does something */
h.postDelayed(r, 1000);
// When you want to cancel.
h.removeCallbacks(r);
Declare your Runnable outside of onTouch event, like seperate method in your Class. Then when you want to actually execute runnable, call your method.
Quick'n'Dirty Example:
onTouch(..) {
if (eventDown) {
// start Runnable
}
if (eventUp) {
// Do not call runnable
}
}
private void executeMyRunnable( /* Any arguments you need in Runnable */) {
// Your runnable code here
}
You can check for long press by taking the time difference between system time at action.down and system time at action.up. If the difference is greater than say 5000 milliseconds, then do the runnable code else the dialog code.
Simply run handler.removeCallbacksAndMessages(null);
Related
I have a long process in the background to do. So onCreate, I post a runnable in my handler from handlerThread but I have a button that allows users to cancel it. It's possible to stop a Runnable after it starts?
#Override
protected void onCreate(Bundle savedInstanceState) {
Handler h = new Handler( HandlerThread.getLooper() );
Runnable runnable = //class that implements runnable;
h.post( runnable );
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
h.removeCallbacks(runnable);
}
}
}
but it seems that doesn't cancel runnable that already running, Or should I use postDelayed() with a huge delay?
Inside of your runnable have a boolean flag for running.
Your button then can set this flag to true/false, to stop it running.
You can implement pause, resume, or start, stop, it all depends on your usecase.
i.e. something like
while(running) {
// Your repeated background code
}
or
if(running) {
// do some one shot code, i.e. the user can stop it if they press the button before the if, but not after.
}
You could also have multiple steps, allowing you to cancel mid way.
if(running) {
// do step 1 code
}
if(running) {
// do step 2 code
}
Use below code
handler.removeCallbacksAndMessages(null);
I need that a button can run automatically every 1-2 seconds, and, when the if condition (that i have in the method which is used by the button) is fulfilled, this function must be stopped.
I've tried this but it wasn't what i wanted because with this code the button only runs one time:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Consulta.performClick();
}
}, 1000);
onClick of my button:
public void consultaBD(View view)
{
DB db = new DB(getApplicationContext(),null,null,1);
String buscar = text_view.getText().toString();
String[] datos;
datos=db.buscar_reg(buscar.trim());
db.infraccion(buscar.trim());
if(datos[2] =="Encontrado")
{
App.matricula=buscar;
startActivity(new Intent(getApplicationContext(), MatriculasActivity.class));
Toast.makeText(getApplicationContext(),datos[2],Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getApplicationContext(),datos[2],Toast.LENGTH_SHORT).show();
}
}
Another method would be to use Timers to initiate the button click every x seconds. However, in this answer I'll stick with the method you're using. Your handler appears to be incorrect, try something like this instead:
Replace your handler with:
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
#Override
public void run() {
Consulta.performClick();
handler.postDelayed(this, 1000);
}
};
And initiate it with: (where 1000 is the time (in milliseconds) between each execution)
handler.postDelayed(runnable, 1000);
UPDATE:
You have also requested that the event is fired when the text inside of a textbox is changed. To do this, you need to create a new event listener (make sure you replace field1 with the actual reference to your textbox):
field1.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
/* Add the Handler Call here */
handler.postDelayed(runnable, 1000);
}
});
whatever context I understood, here is the raw code which may help you.
Handler handler = new Handler();
//initialize this method once by either clicking on button or as the activity starts
void checkAndPerformClick(boolean conditionFulfilled) {
if (conditionFulfilled) {
handler.removeCallbacksAndMessages(null);
return;
}
handler.postDelayed(new Runnable() {
#Override
public void run() {
Consulta.performClick();
checkAndPerformClick(datosEqualsEncontrado());
}
}, 1000);
}
boolean datosEqualsEncontrado() {
// apply your logic here as the name suggests
return false;
}
I want to set dynamically auto scroll speed to WebView. In onCreate calling autoScroll(25) and remove it, nextly calling autoScroll(300) but when the apk is running the auto scroll speed is 25 so earlier called 'mHandler.postDelayed' do not removing. How to fix the problem?
Handler mHandler;
Runnable runnable;
WebView wv;
protected void onCreate(Bundle savedInstanceState) {
...
autoScroll(25);
mHandler.removeCallbacks(runnable);
autoScroll(300);
}
public void autoScroll(final int speed){
if(mHandler == null) {
mHandler = new Handler();
}
wv.post(runnable = new Runnable() {
#Override
public void run() {
wv.scrollBy(0, 1);
mHandler.postDelayed(this, speed);
}
});
}
mHandler.removeCallbacks(runnable);
will only remove any pending posts of Runnable r that are in the message queue. It will not stop an already running thread. You have to explicitly stop the thread. One way to stop that thread is to use a boolean variable as a flag and run your code inside runnable based on the value of that flag. You can take some hints from https://stackoverflow.com/a/5844433/1320616
I'm trying to set my own long click listener on Unlock button. Whenever I press the Unlock button it summarize duration and I can unlock permanently clicking.
Unlock.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Unlock.setText("Press to unlock");
isLongPress = true;
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (isLongPress) {
Unlock();
}
}
}, longClickDuration); //amount of time of long click
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Unlock.setText("Unlock");
isLongPress = false;
}
return true;
}
});
}catch (Exception e) {
// TODO: handle exception
}
}
If you want to just handle long clicks consider using the following code:
Unlock.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
your code
}
});
But if the Unlock(); should be invoked after a certain (customizable) amount of time, you should measure this time in MotionEvent.ACTION_UP handler. As #Attaullah Khan said, use SystemClock.elapsedRealtime() system timer to correctly count number of milliseconds at two moments (when button was pressed and released) and if the time is greater than longClickDuration then invoke Unlock
The handler.postDelayed that you call in MotionEvent.ACTION_DOWN handler just invokes a check of pressed state after longClickDuration interval and if your button gets suddenly pressed at that moment, the verification passes that is not correct
I wrote a working code using isEnabled().
if(btn.isEnabled()){
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// say send a udp packet
}
}
);
}
Now instead of the packet to be sent when the button is clicked, I want to send it when it stays pressed. How do I handle this?
when i tried isPressed instead of isEnabled, there was a blank screen and the activity was not even displayed...
EDIT : also tried btn.isPressed() - it doesn't work ... the udp packet gets sent immediately after I click on the button... I want it to send ONLY when I am pressing it ...
Any help would be appreciated.
Thanks
your condition is vague. "ONLY when I am pressing it" would mean you'll start sending when the button starts being pressed, which would mean on MotionEvent.ACTION_DOWN. if you want some delay before the action gets executed, create a timer thread that would start when MotionEvent.ACTION_DOWN is detected, and will execute your action after a few seconds. the timer should also reset when MotionEvent.ACTION_UP is detected, or if the action is already in progress, interrupt the action.
but honestly, you may want to rephrase your condition.
Not sure if this will work, but worth a try
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.isPressed()){
//do sth.
}
else{
//do sth. else
}
}
Your going to want to use the onTouchListener instead of onClick and stuff. Small change you also want to track when the user lets go.
EDIT adding timer stuff
Timer timer;
UDP request;
btn.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
if(event.getAction() == MotionEvent.ACTION_DOWN){
//TODO start sending udp in background
timer = new Timer();
timer.schedule(new TimerTask(){
public void run(){
request.start();
}
},DELAY_MS);
}
if(event.getAction() == MotionEvent.ACTION_UP){
//TODO stop sending udp
timer.cancel()
if(request.isTransmitting()){
request.stop();
}
}
//needed to get both calls
return true;
}
});