I am currently trying to implement Drag and Drop in Android. The draging should be done on an item from a ListView. The thing is that the elements(items) in this ListView should have some sort of action when they are touched (single taped) and they should be dragable after the user perform a long press on one of the items. I got the code for draging from a tutorial but the problem that i cannot solve is:
After the user holds his finger on an item from the ListView the onTouch(MotionEvent ev) is called at the beginning and after a second GestureDetector.onLongPress is called.
My draging logic is implemented in the onTouch methods. What i currently do is set a boolean (isLongPressed) to true after LongPress is enabled and in the onTouch methods i check if this boolean is true - if yes then perform the draging.
Here is my code :
public class DragNDropListView extends ListView implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener
{
private boolean isLongPressed
#Override
public boolean onTouchEvent(MotionEvent ev)
{
mGestureDetector.onTouchEvent(ev);
if (!isLongPressed)
{
Log.e("Not Long", "Its Not Longpressed yet");
return super.onTouchEvent(ev);
}
final int action = ev.getAction();
final int x = (int) ev.getX();
final int y = (int) ev.getY();
if (action == MotionEvent.ACTION_DOWN && isLongPressed)
{
mDragMode = true;
}
if (!mDragMode)
return super.onTouchEvent(ev);
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e("Action down", "Down");
break;
case MotionEvent.ACTION_MOVE:
Log.e("ACTION MOVE", "Move");
drag()
break;
case MotionEvent.ACTION_UP:
{
isLongPressed = false;
}
}
#Override
public void onLongPress(MotionEvent ev)
{
Log.e("LongPress", "LongPressed");
isLongPressed = true;
mGestureDetector.setIsLongpressEnabled(isLongPressed);
super.onTouchEvent(ev);
}
What happens is that i get "isNotLongPressed" if i just tab the items (desired behavior), After a long press i get "LongPressed" but then if i start draging i get nothing. If i release my finger and then start the drag everything is ok but i want to be able to drag immediately after the long press is registered.
Related
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);
In a Custom ListView extending Cursoradapter, I need both Listview and image button to get focussed. How to do it?
Implement your own OnTouchListener for ListView which conditional return values. If you return TRUE from onTouch, the system will assume that the onTouch was handled and it won't handle it by itself. If you return FALSE from onTouch, system will assume that the OnTouchListener did not handle the touch events and will do further processing. This way you can manage how the touch / click should work with ImageButton in a ListView.
I don't know what exactly your implementation is. But it may give you a little idea...
boolean pressedImageButton = false;
myListView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(action)
{
case MotionEvent.ACTION_DOWN:
float positionX = event.getRawX();
float positionY = event.getRawY();
if(some conditions with positionX and positionY)
pressedImageButton = true;
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if(pressedImageButton)
myImageButton.performClick(); // or whatever action you want to perform on that ImageButton
pressedImageButton = false;
break;
}
}
});
I hope this helps. :)
I have a button that I'm animating on button press. I want it to snap back to its "normal" state after I drag outside a certain threshold.
I create a rectangle of the view bounds on ACTION_DOWN and check whether it's out of the touch area in ACTION_MOVE. I successfully detect the "out of bounds" touch, but I can't get the view to stop listening to touches. It's like it ignores my animateToNormal() method.
I've tried changing the boolean return value to true instead of false, which didn't help. I've also tried removing the touch listener in the ACTION_MOVE case (set null), but I need to re-attach to continue listening to touches. I figure I can add an arbitrary delay before adding it back, but that seems like a terrible hack.
I'm testing this on a 4.2 device (LG G2).
private static class AnimationOnTouchListener implements View.OnTouchListener {
private Rect rect;
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch(motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
rect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
animatePressed();
return false;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// back to normal state
animateBackToNormal();
return false;
case MotionEvent.ACTION_MOVE:
if(!rect.contains(view.getLeft() + (int) motionEvent.getX(), view.getTop() + (int) motionEvent.getY())){
d(TAG, "out of bounds");
animateBackToNormal();
// STOP LISTENING TO MY TOUCH EVENTS!
} else {
d(TAG, "in bounds");
}
return false;
default:
return true;
}
}
Why you just don't keep listening but set an statement to ignore the motion event?
Something like that:
private static class AnimationOnTouchListener implements View.OnTouchListener {
private Rect rect;
private boolean ignore = false;
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(ignore && motionEvent.getAction()!=MotionEvent.ACTION_UP)
return false;
switch(motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
rect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
animatePressed();
return false;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// back to normal state
animateBackToNormal();
// IMPORTANT - touch down won't work if this isn't there.
ignore = false;
return false;
case MotionEvent.ACTION_MOVE:
if(!rect.contains(view.getLeft() + (int) motionEvent.getX(), view.getTop() + (int) motionEvent.getY())){
d(TAG, "out of bounds");
animateBackToNormal();
// STOP LISTENING TO MY TOUCH EVENTS!
ignore = true;
} else {
d(TAG, "in bounds");
}
return false;
default:
return true;
}
}
Ok... in my app i update the layout on MotionEvent.ACTION_DOWN and then i check the motion event coordinates to locate my buttons. I can show a toast when finger is released on different buttons. The problem is i need a long touch on my buttons to call another action without conflicting with the MotionEvent.ACTION_UP. Implemented a long click handler but since i don't 'click' its not working. Hope you guys understand my problem.
Whats the best way to get my app working as intended?
My class implements OnTouchListener, OnGestureListener
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
// UPDATE LAYOUT
break;
case MotionEvent.ACTION_UP:
// GET BUTTON X Y
if (x and y match the button location){
// DO ACTION
}else{
// DO NOTHING
}
// CHANGE LAYOUT TO INITIAL STATE
break;
case MotionEvent.ACTION_MOVE:
break;
}
return false;
mybutton.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// DO STUFF
return true;
}
});
}
just try to return false in your onTouch(...) method and use onLongClickListener(...) as usual
My application have three pages (three tabs) and I want to switch beetween two gridviews by moving finger horizontaly. The touch code works fine but I can't click anymore on the grid items! I use the method onItemClickListener (onClickListener don't works on Gridview) but the grid item is not clicked.
Thanks for your help!
The code is :
myGrid.setOnTouchListener(this);
myGrid.setOnItemClickListener(this);
....
public boolean onTouch(View v, MotionEvent event) {
int eventaction = event.getAction();
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
xStart = event.getX();
break;
case MotionEvent.ACTION_UP:
xEnd = event.getX();
if (xEnd - xStart > 20){
//switch to previous tab
}
if (xEnd - xStart < -20){
//switch to next tab
}
return true;
default:
break;
}
return true;
}
What view is that onTouch code in? You could try changing that last return true to return false so that if the action wasn't a motionevent, the event is not consumed by the view.