Well I have made a custom Android UI, and I need my UI view to handle this control.
a) While my mouse button is pressed (onDown / Key pressed) , the view should keep doing a thing (For example : Key is down)
b) As soon as i release my mouse key , the view should say (example: Key is up).
The Sample Flow should be something like when I press the view.
Output:
(I press mouse button on the view and hold it)
Key is down
Key is down
Key is down
Key is down
Key is down
Key is down
(I now release mouse button)
Key is up.
To more explain my problem . I am posting a code snippet where the logic should go
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, event.getAction());
}
When I press my mouse button , it prints "0" (meaning mouse is down), while if i leave it , it prints on the log "1", meaning mouse is up. That should help with the logic.
Thanks for helping.
Well I have tried something like
private static int checkValue = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
checkValue = event.getAction();
while (checkValue == 0 ){
Log.d(TAG, "Pressed");
checkValue = checkMethod(event.getAction);
}
private int checkMethod(int test){
if (checkValue == 0){
checkValue = 0;
}
else checkValue = 1;
}
Just define an OnTouchEventListener...
private OnTouchListener onTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.button) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//start loop or background task
} else if (event.getAction() == MotionEvent.ACTION_UP) {
//do something different
}
}
return true;
}
}
...and assign it to your button.
button.setOnTouchListener(onTouchListener);
When the user touches the button the ACTION_DOWN event is fired. When the user releases the button, the ACTION_UP event is fired. If you want you can start a loop in a thread which can check against a boolean variable. I didn't check if it is correct but it should look like this:
private OnTouchListener onTouchListener = new OnTouchListener() {
private boolean flag = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.button) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
flag = true;
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(flag) {
//do something
}
}
});
thread.start();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
flag = false;
}
}
return true;
}
}
Maybe you could create and start a thread when the touch occurs that has a volatile boolean stop = false field and that checks for this value in every loop after sleeping for a while. When the touch ends, you can set the boolean variable to false.
Edit: added an example
private MyHandler h;
public void handleDown() {
System.out.println("touch began");
h = new MyHandler();
h.start();
}
public void handleUp() {
h.pleaseStop = true;
System.out.println("touch ended");
}
class MyHandler extends Thread {
volatile boolean pleaseStop = false;
public void run() {
while (!pleaseStop) {
System.out.println("touch is active...");
Thread.sleep(500);
}
}
}
checkValue = event.getAction();
while (checkValue == 0 ){
You should never ever be using magical integers. Use the constants as defined by the API:
http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_DOWN
In fact your code isn't even accounting for move events; it seems to assume that if it isn't a down event it is an up... which is completely wrong.
Look at the documentation to see what action values you can expect, and do the right thing for the specific actions you are interested in. For example up:
http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_UP
Create a thread which is initiated by the touch and have boolean flag to keep it alive. When the touch is released, set the flag to false so the thread joins (terminates).
Because the thread is independent from the main logic, the process is faster.
Related
I have two views which are siblings of each other. They both cover the full screen. So one is behind the other. If the upper one gets touched (onTouch), I delegate the touch events to the one underneath it (with dispatchTouchEvent).
But sometimes I want to delay that delegation, till the next time onTouch gets called. But somehow that does not work.
An example to clarify:
To viewA - which is in front of viewB - I have applied the following (simplified) code:
viewA.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
int touchType = event.getActionMasked();
if (touchType == MotionEvent.ACTION_DOWN) {
savedEvent = event;
return true; // I also tried returning false here
} else {
if (savedEvent != null) {
viewB.dispatchTouchEvent(savedEvent);
savedEvent = null;
}
viewB.dispatchTouchEvent(event);
return true; // I also tried returning false here
}
}
});
To test the dispatchTouchEvent call, I have the following code for viewB
viewB.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
Log.d("test", "test"); // this code gets logged, so it is being called, but the view seems to not execute any of the touch events
return true;
}
});
When I change to code for viewA to this:
viewA.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
viewB.dispatchTouchEvent(event);
return true;
}
});
everything works just fine.
But the thing is that for my use case, I sometimes have to call the dispatchTouchEvent method with the event parameter outside its originating onTouch method, so to speak.
Is this even possible? And if yes, how?
So I found out what prevents it from working. If you manually call dispatchTouchEvent you should pass through an event that you created yourself with MotionEvent.obtain().
Working example:
viewA.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
int touchType = event.getActionMasked();
if (touchType == MotionEvent.ACTION_DOWN) {
// do not do this, somehow passing on a reference of the event directly does not work:
// savedEvent = event;
// this works though:
savedEvent = MotionEvent.obtain(event);
return true;
} else {
if (savedEvent != null) {
viewB.dispatchTouchEvent(savedEvent);
savedEvent = null;
}
viewB.dispatchTouchEvent(event);
return true;
}
}
});
Although I don't understand why that does work and just passing the event reference directly does not, I tested this and it does work perfectly.
I developed an standalone app for a smartwatch, and I want to have an OnTouchListner which detect if a Touch lasted for at least 3 sec and perform action only in that case.
Here is the code I use : (a simple boolean state which is set to false when MotionEvent is up and a PostDelayed action on the Handler)
private int longClickDuration = 3000;
private boolean isLongPress = false;
static Runnable longTouchRunnable;
mContainerView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
longTouchRunnable = new Runnable() {
#Override
public void run() {
if (isLongPress) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
long[] vibrationPattern = {0, 500, 50, 300};
//-1 - don't repeat
final int indexInPatternToRepeat = -1;
vibrator.vibrate(vibrationPattern, indexInPatternToRepeat);
// set your code here
setStateStarting(); //Block the GUI, only one click and waiting for tablet
presenter.onStartClick();
Log.d("Long","LONG CLICK PERFORMED");
// Don't forgot to add <uses-permission android:name="android.permission.VIBRATE" /> to vibrate.
}
}
};
switch (currentState) {
case START: {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isLongPress = true;
Log.e("Long touch","Start");
myUIHandler.postDelayed(longTouchRunnable, longClickDuration);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
isLongPress = false;
myUIHandler.removeCallbacks(longTouchRunnable);
Log.e("Long touch", "Stop");
}
return true;
}
}
return false;
}
});
My problem :
Everything work perfectly expect that the Runnable doesn't seem to be delete when it's necessary.
--> If I click quickly two times on the view and then a third time and holding it (So the boolean state is true for the three PostDelayed which came after 3 sec) I will have my specific action code executed 3 times --> the Runnable task wasn't removed with the ACTION_UP event.
Info : The message debug are prompt as expected...
How can I properly destroy a Runnable that was postdelayed in a handler ?
Thanks
handler.removeCallbacksAndMessages(null);
In the docs for removeCallbacksAndMessages it says...
Remove any pending posts of callbacks and sent messages whose obj is token. If a token is null, all callbacks and messages will be removed.
removeCallbacks only stops pending messages (Runnables). If your runnable has already started, then there's no stopping it (at least not this way).
Alternatively, you can extend the Runnable class and give it some kind of kill switch like this:
public class MyRunnable implements Runnable
{
public boolean killMe = false;
public void run()
{
if(killMe)
return;
/* do your work */
}
public void killRunnable()
{
killMe = true;
}
}
This will only prevent it from starting, but you could occasionally check killMe and bail out. If you are looping the runnable (like some kind of background thread) you can say:
while(!killMe) {
/* do work */
}
I am using android eclipse for programming.
I will use this basic calculation to fit to what I want to happen.
Here:
I got two buttons add and minus. if i press add it will obviously call the method add.
But my problem is. if I will keep pressing add button. It will keep adding multiple times and
if I will click 2 buttons at the same time it will also do add and minus. What I want is that
if i click both button at the same time there's priority that add button will execute first
and the minus button will not send data.
Add(){
a = b + c;
}
Minus(){
a = b - c;
}
public void add(View view){
Add();
}
public void subtract(View view){
Minus();
}
Just set button_minus.setClickable(false); in Add method and
button_add.setClickable(false); in Minus method. Then enable them back.
try:
private boolean isAddButtonPressed;
private void setListener(){
buttonAdd.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
isAddButtonPressed = true;
else
isAddButtonPressed = false;
return false;
}
});
}
public void add(View view){
doMath(true);
}
public void subtract(View view){
if(!isAddButtonPressed)
doMath(false);
}
// "synchronized" means that this method can ran only one time at a time
private synchronized void doMath(boolean isAdd) {
if(isAdd) {
Add();
} else {
Minus();
}
}
I currently have a while loop that starts when a user taps the screen. The loop is meant to be infinite, and should stop when it senses another screen tap from the user. (And, if tapped again, it'll start looping again, etc.) I’ve tried to break the loop in two different ways, with both yielding non-satisfactory results:
METHOD 1: When I try this method, what happens is that the user taps, the loop starts. The user taps again, and that tap doesn't get registered and the loop just keeps on going.
boolean playing = false;
#Override
public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
if(!playing)
{
playing = true;
while(playing)
{
//do something here
}
}
else if(playing)
{
playing = false;
}
}
return false;
}
METHOD 2: When I try this method, what happens is that the user taps, the loop starts, goes through one iteration, then stops.
boolean playing = false;
#Override
public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
if(!playing)
{
playing = true;
whileLoop:
while(playing)
{
//do something here
}
}
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
playing = false;
break whileLoop;
}
}
return false;
}
I've looked at these questions, but neither helped me with my specific situation:
How do I exit a continuous while loop by some user input?
How to exit an infinite while loop using user input?
So how do I implement this while loop? I just want a simple interface where a user's tap would toggle the while loop on/off.
Put the infinite loop within a Thread. Having it there, you can just have a control variable inside (like playing), and once tap again and you want to stop, simply set it to false. The Thread, running paralelly, will be able to handle both events (the tap and the Thread stop).
---- EDIT ----
Example added:
// You'd probably need to store this variable class-wide,
// so you can control when the user has tapped for the first or second time
boolean playing = false;
#Override
public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
if(!playing)
{
playing = true;
new Thread(
new Runnable() {
public void run() {
while(playing)
{
//do something here
}
}
}
).start();
}
else if(playing)
{
playing = false;
// As you're controlling the playing value in the Thread,
// setting it here to false would mean that your thread stops too.
}
}
return false;
}
try this:
boolean playing = false;
Handler handler = new Handler();
Runnable runner = new Runnable() {
#Override
public void run() {
// do something here
handler.post(this);
}
};
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
if(!playing) {
playing = true;
runner.run();
return true;
}
if(playing) {
playing = false;
handler.removeCallbacks(runner);
handler = null;
runner = null;
Toast.makeText(context, "NO", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
});
I want to check that view (no matter which, in my case ImageView) is not touched/clicked by user, and I want to do it constantly. I think that i should use some kind of thread but I have no idea how to start. The operation which I want to do is hiding the ActionBar, when there is no action I want to hide it.
When view is touched I use this code:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
//if (registerImageViewCallback(slidesPagerAdapter);
gestureImageView = slidesPagerAdapter.getGestureImageView();
gestureImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
getSupportActionBar().show();
return false;
}
});
}
}, 10);
But what I have to do when screen is not touched? :-)
Create an additional boolean (ex: notTouched) to true, then set it to false inside your onTouch override, then at any point you can just check if the boolean is true or false.
EDIT :
If you want it to hide it after it has not been touched for a while (as you said)
you could implement an additional counter integer in a loop on a separate thread like this:
while(true) {
if (notTouched) {
counter++
if(counter == 20) { // Assuming 20 seconds have passed of not touching
hideMethodHere(); // Execute hiding
}
Thread.sleep(1000); // Sleep for a second (so we dont have to count in miliseconds)
} else {
counter = 0; // If it was touched, reset the counter
notTouched == true; // Reset the touch flag as well because otherwise it will be visible forever if you touch it
}
}
Add a flag to your Activity:
boolean wasTouched = true;
Add a timer that checks if the view was touched:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if ( wasTouched == false ) {
runOnUiThread(new Runnable() {
public void run(){
hideYourActionBar();
}
});
} else {
wasTouched = false;
}
}
}, 0, YourDelayInMsHere);
In your onTouchListener, set wasTouched to true:
public boolean onTouch(View v, MotionEvent event) {
wasTouched = true;
}
This should at least give you an idea of how to do it.