How to detect touch when you are moving over a button - android

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.

Related

Why can't I execute both onClick and onTouch simultaneously?

Disclaimer: I have already tried following similar threads (namely How can I use OnClickListener in parent View and onTouchEvent in Child View? and How can i get both OnClick and OnTouch Listeners), yet my code still fails to work.
I have a (parent) activity which contains a (child) view. Activity has all the network code, while view contains a bitmap displaying game state. View executes the game moves and draws them, while activity sends and receives the moves.
As such, I need to listen for player's actions on the bitmap, handle them there and immediately after it is handled in the view, I need to send the made move to the server. So the best way I've found to handle all that so far is by using onClickListener along with onTouchListener. But I've spent 4 hours and can't get this to work, so any help would be appreciated.
My parent onClick:
gameView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//gameView.performClick();
sendMove(theGame.getLastMove());
}
});
My child view:
#Override
public boolean onTouchEvent(MotionEvent event)
{
if(canIMove) {
float x = event.getX();
float y = event.getY();
int recWidth = this.getWidth() / 7;
int currentWidth = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x > currentWidth && x < currentWidth + recWidth)
{
//Bitmap touched
theGame.Move();
ReloadGameBoard();
}
return true;
}
}
return false;
}
It matters not whether I try to performClick() in onClick or in onTouch, it doesn't also matter whether I return true or false in onTouch, it doesn't matter whether I put it in ACTION_DOWN nor ACTION_UP, the result is always the same: child View onTouch gets executed and parent onClick does not.
And please, if you decide to downvote this, at least tell me why.

MotionEvent.ACTION_DOWN in Android is too sensitive. This event is received even if the screen is simply touched for a moment

I have a layout(A table layout). Whenever the user performs a "down" gesture, a specific function needs to be called. However,the ontouchlistener captures the event immediately instead of waiting until the user performs a proper "pull down" gesture.
My code is :
homePageTableLayout.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_DOWN)) {
startSyncData();
}
return true;
}
});
Currently, what's happening is that the startSyncData() function gets called immediately.
The end result should be somewhat similar to the commonly used "pulltorefresh" library for android but in this usecase, I don't have to update the view. I just have to post some data to the server.
I can't use the "pulltorefresh" library because it does not support "pulling" a tablelayout. I tried putting the tablelayout inside a scrollview but that only spoiled the UI.
You can try doing it in ACTION_MOVE or ACTION_UP - if you need it to be done exclusively on swipe down gesture, this should help, introduce four global floats: x, y, x1, y1, then in ACTION_DOWN:
x = event.getX();
y = event.getY();
ACTION_MOVE or ACTION_UP:
x1 = event.getX();
y1 = event.getY();
and then in ACTION_UP:
if (y1 > y) { //pointer moved down (y = 0 is at the top of the screen)
startSyncData();
}
Its not that easy. It works properly since you are catching MotionEvent.ACTION_DOWN. It doesn't mean a gesture like swipe down it means that user touched the screen (finger down, the touch or gesture just started). Now you need to catch MotionEvent.ACTION_UP (finger up, gesture or touch ends here) and decide if there was a gesture you need. http://developer.android.com/training/gestures/detector.html
http://developer.android.com/training/gestures/index.html
MotionEvent.ACTION_MOVE repeatedly gets called. So if you want startSyncData() to run after moving a certain distance, calculate how much you want the user to move before running the method.
The key is that ACTION_DOWN is not a downwards movement on the screen, but it means the user pressed down onto the screen.
homePageTableLayout.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_DOWN)) {
startFlg = 1;
} else if ((event.getAction() == MotionEvent.ACTION_MOVE)) {
if (startFlg==1){
// Calculate distance moved by using event.getRawX() and event.getRawX() and THEN run your method
startSyncData();
}
}
return true;
}
});

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;
});

Dragging and pressing buttons - 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.

Floating List view in android?

Is it possible to create a list view in android that is not fixed to a particular position and user can move the list view on a gesture some what floating listview that can be moved anywhere on the screen?I tried finding it but could not find some link.Does any one have some idea about it?
You can achieve this by setting an OnTouchListener on the list view and overriding it's onTouch() method. In the onTouch() method you will have to handle the touch events like(ACTION_DOWN, ACTION_MOVE). Here is how you can handle touch event:
#Override
public boolean onTouch (View v, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY()); //saving the initial x and y position
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG) {
float scrollByX = event.getX() - start.x; //computing the scroll in X
float scrollByY = event.getY() - start.y; //computing the scroll in Y
v.scrollBy((int)scrollByX/20, 0);
}
break;
}
return false;
}
When the user places the finger down on the screen the ACTION_DOWN event is triggered and when the user drags his finger on the screen ACTION_MOVE event is triggered. Hence a drag event is a combination of ACTION_DOWN and ACTION_MOVE.
Once you have the scrollX and scrollY value the you can move the view on the screen by passing these values to the scrollBy() method of the View class.
Here in the code there are two things to notice:
first is that I have passed 0 as the value of scrollY as it will prevent the list view to be dragged and scrolled simultaneously.
second is that the return value is false as you want the android to handle the scrolling of the list items
Here you can find the complete code for this
I would move the listview using the margins from the layout.
Outline:
Override OnTouch, and based on the event.getX() and event.getY(), update the margins of the listview as follows:
if the parent is a relative layout:
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mListView.getLayoutParams();
lp.leftMargin = event.getX();
lp.topMargin = event.getY();
mListView.setLayoutParams(lp);
Add the dispatchTouchEvent to your activity so that events from the listview get passed up to onTouch:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
Hope this helps.
Although it's hard to maintain, AbsoluteLayout lets you specify a position in (x,y) coordinates. You would just declare your ListView inside a AbsoluteLayout, get a handle to it in java code where you intercept the clicks and then set new coordinates.
In the Android framework, the word "floating" is usually synonymous with new Window (such as a Dialog). It depends somewhat on what your end goal is, but if you want to create a ListView that floats over the rest of the content in your hierarchy, that you can easily hide and show, you should probably consider wrapping that view inside a PopupWindow, which has methods to allow you to easily show the window with showAsDropdown() and shotAtLocation() and also move it around while visible with update().
This is how the newer versions of the SDK create pop-up lists for Spinner and Menu

Categories

Resources