Dragging and pressing buttons - android - android

I have several buttons and I would like to press on one of them and drag through another presing them. Could you tell me which MotionEvent or another functionality should I use. I'm using onTouchListener.
There is an image where you can see what I want to do (first ACTION_DOWN on 1st button and drag through 2nd-7th buttons still pressing the screen) and finally press every white buttons:
Below is my onTouch button code:
button1 = (Button) findViewById(R.id.button1);
button1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
soundIDs[0] = sound.play(R.raw.sample1);
button1.setBackgroundResource(R.drawable.white_clicked);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
sound.stop(soundIDs[0]);
button1.setBackgroundResource(R.drawable.white);
break;
}
return false;
}
});

You are setting the OnTouchListener on just the one button. That's not going to help you know when the pointer moves (e.g. user drags his finger) into another button.
You could set an OnTouchListener on the view that contains the buttons. Then check for ACTION_DOWN, ACTION_MOVE, and ACTION_UP events. You would then have to do some simple hit detection to figure out which button to activate.
Something along the lines of:
getWindow().getDecorView()
.findViewById(android.R.id.content)
.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
int action = event.getAction();
if (action != MotionEvent.ACTION_DOWN
&& action != MotionEvent.ACTION_MOVE
&& action != MotionEvent.ACTION_UP) return false;
Rect hitRect = new Rect();
Button button;
for(int i = 0; i < myButtons.size(); i++) {
button = myButtons.get(i);
button.getHitRect(hitRect);
if (hitRect.contains((int)event.getX(), (int)event.getY())) {
button.setText("hit!");
}
}
return true;
}
});
Where myButtons is an ArrayList of your buttons (piano keys in your example).
Also, you'd probably want to modify this to properly deactivate the currently active button if the user's touch leaves the button, but doesn't hit another button.
I tested the above code on an android device with a layout that has 3 buttons in a row. Dragging your finger across all the buttons causes each button's text to change to "hit!"
Like I said above, you were setting the touch listener on the just one button, that will not work. In this example I have set the touch listener on the entire view for the activity.

Related

How to programmatically cancel the OnTouchListener callback even if the user haven't released the touch from a view in Android?

I am working on an Audio Recording App. It works in a way that when the user presses and moves the record button, the button moves along with the finger. I have created a boundary and when the finger crosses that boundary I want the button to perform the hide() animation and get back to it orginal position.
The whole process works fine if the MotionEvent.ACTION_UP or MotionEvent.ACTION_CANCEL event is occurred, but the hide() operation is not occurring even if the touch crosses the boundary. The button plays a back and forth motion sometimes when it is outside the boundary. The touch event is still being called even if I set the visibility of the view to false.
I get the output in the logcat as well (Log.e("MSG","boundary crossed");).
This is the code:
int recordButtonStartX;
microPhoneListner=new View.OnTouchListener() {
#Override
public boolean onTouch(View v, final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
recordButtonStartX = (int) event.getX();
this.floatingRecordButton.display(event.getX());
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
this.floatingRecordButton.hide(event.getX());
break;
case MotionEvent.ACTION_MOVE:
int tempX = (int) event.getX();
if ((recordButtonStartX - tempX) > 200) {
Log.e("MSG","boundary crossed");
this.floatingRecordButton.hide(event.getX());
}
else
{
this.floatingRecordButton.moveTo(event.getX());
}
break;
}
recordMsgButton.setOnTouchListener(microPhoneListner);
To release the onTouchListener for any View set the listener to null.
recordMsgButton.setOnTouchListener(null);
or
After your condition satisfied you can set other listener to that View.
Make another listener
public final OnTouchListener mTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent rawEvent) {
return false;
}
};
When you want to disable the listener then set other listener to that view
v.setOnTouchListener(mTouchListener);

How to detect touch when you are moving over a button

I have created a button at the center of the layout with width 50dp.If I touch the screen at left extreme side of the layout(where the is no button) and hold down (Keep pressing ) till I reach the button in center then it should detect the touch .How can I do that .I have tried both onCLicklistener() and onTouchListener() but I still cannot detect the touch .
Basically like gesture but I thought button had a way of detecting that.
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Toast text : entered within button
return false;
}
}))
If we set Click or Touch listener to Button which is 50dp width at center, then the listener will get callback only if the user click/touch initiating from button.
While in your problem case, You are initiating click/touch from outside and coming to button.
So, button listener will not get callback from Android Framework
For your requirement to work, we need to add some logic, i have tried to add it here :
activity_main.xml :
<RelativeLayout android:id="#+id/parentLayout" ... >
<Button android:id="#+id/buttonCenter ... />
</RelativeLayout>
MainActivity.java :
// apply touch listener to parentLayout
button = (Button) findViewById(R.id.buttonCenter);
parent = (RelativeLayout) findViewById(R.id.parentLayout);
parent.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("tag","in onTouch...");
checkTouch(event);
return true;
}
});
// check if touch entered button area
// save button left, right, top and bottom edge
// update : This is API i found on google documentation
float[] params;
button.getLocationOnScreen(params);
public void checkTouch(MotionEvent event) {
x = event.getX();
y = event.getY();
if(x >= param[0] && x <= (param[0]+button.getWidth())) {
if(y >= param[1] && y <= (param[1]+button.getHeight())) {
Log.d("tag","this touch is in button area");
// do what you want to do when touch/click comes in button area
}
}
}
Actually when you place a touch MotionEvent.ACTION_DOWN event is dispatched on the view which you touch and as you move MotionEvent.ACTION_MOVE events will be dispatched for every pixel point moved and until you release viz. until MotionEvent.ACTION_UP event is dispatched on that same view the events will not be handed over to any other view.
That is why it is not detecting on the button since it is actually the event of its parent view. So write an ontouchlistener on the parent and in that handle the touch according to the event.getRawX() & event.getRawY() positions in pixels.

Select other buttons on touch move

I'm writing a test which options are placed in buttons. I have a selector for buttons that displays normal and pressed state and everything works great.
What i want is that when user touches an option and moves to next option, the next button get focus and select. And when user takes the hand, perform the selected button click.
In images above you can see exactly what i mean.
You need to write a custom touch event management that will trigger setPressed(true) on MotionEvent.ACTION_DOWN OR MotionEvent.ACTION_MOVE (the default implementation expects to always receive MotionEvent.ACTION_DOWN).
Code like this should work (add it to all your buttons):
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (!v.isPressed()) {
v.setPressed(true);
}
return true;
case MotionEvent.ACTION_UP:
// perform action ON CLICK
break;
}
v.setPressed(false);
return false;
}
});

Cancel button press if user moves finger off button

Here is my very standard button onTouchListener:
b.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
b.setPressed(true);
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
b.setPressed(false);
// Do the button action stuff here
}
return true;
}
});
When the user presses down on the button I set the button to "pressed". When the user lifts the finger I set the button to "not pressed" and then do the stuff the button is supposed to do.
This is all great except for the case where the user presses the button and then, while continuing to keep the finger down, moves the finger outside of the button area. When the finger is lifted the ACTION_UP stuff runs anyway. I would like for this behavior to act as a "cancel" of the button press. So, as soon as the pressed finger moves outside the button area I'd like to b.setPressed(false); and then NOT do the button action stuff when the finger is raised.
I've tried using MotionEvent.ACTION_CANCEL and MotionEvent.ACTION_OUTSIDE (as suggested by How to detect when finger slides off the ImageButton?) but I must be missing something because they're not working.
From Detect when a user moves off of a button in Android it would appear that this can be done with event.getX() and event.getY(). If getX() is negative you're off the left and if getY() is negative you're off the top, but to figure out if you're off the right or bottom I'll have to figure out the size of the button and then... there has to be a simpler way. Is there?
You had the right answer. Use getWidth() and getHeight() to get the width and height and check if x or y is greater.
ACTION_CANCEL doesn't work because CANCEL is generated when a parent view takes control of the touch events, like a ListView does when scrolling.
ACTION_OUTSIDE only happens in some unusual cases where you request it. It isn't going to be generated for just a normal MOVE or UP.
Since no one posted any code, here is one. A Boolean is used to indicate when the respective actions should be performed.
The MotionEvent.ACTION_MOVE event is handled to check if the finger is moved out of the bounds. If yes, b.setPressed(false); is called and the boolean is used to indicate that MotionEvent.ACTION_UP shouldn't be fired the next time, ie to cancel the button click.
And in MotionEvent.ACTION_UP, call b.setPressed(false); only if our boolean doesn't indicate a skip (as mentioned above). And if it does, invert the boolean so that button action stuff would execute the next time.
private Boolean notNow = false;
private Rect rect; // Variable rect to hold the bounds of the view
b.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event){
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN){
b.setPressed(true);
rect = new Rect(v.Left, v.Top, v.Right, v.Bottom);
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP){
if (!notNow){
b.setPressed(false);
}
else //button press canceled
notNow = false;
}
if((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE){
if(!notNow)
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
// finger moved out of bounds
b.setPressed(false);
notNow = true; //cancel button press the next time
}
}
return true;
});

On pressed button block the other buttons

How could I make my button to do the on click action even if the button is pressed and the finger is still on the screen (the button is not released). Or I would accept the solution that when a button is pressed and is not release, the other buttons on the layout to not be blocked.
Instead of using an OnClickListener consider using an OnTouchListener. You can then detect when the user's finger touches:ACTION_DOWN and releases ACTION_UP the button.
A method like this would probably work fine:
public boolean onTouch(View v, MotionEvent event)
{
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//DISABLE THE OTHER BUTTONS
break;
case MotionEvent.ACTION_UP:
// RE-ENABLE THE OTHER BUTTONS
break;
}
return true;
}
One option would be to use the onmousedown event rather than the onclick event in your javascript:
myButton.onmousedown = myOnClickFunction;

Categories

Resources