Can some one help my with ScrolView in Android Mobile App.My problem- on my vertical ScrollView i have horizontal "hlistview»,and when i moved "hlistview" up/down/right/left play only one. How they can work together? At time,work only one.
hListView.setOnTouchListener(new HorizontalListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
v.onTouchEvent(event);
return true;
}
});
On nested scrollviews you must intercept the touch events. However if you just requestDisallowInterceptTouchEvent on ACTION_DOWN you are making the it's parent disable touch events the moment your finger touches the screen. That's not the wanted behavior. You need to set a threshold and only disallow the events the moment your scroll distance has hit that threshold.
On the nested scrollview ACTION_DOWN set a field saving the initial X (for horizontal swipe) like this:
mLastX = event.getX();
Next on ACTION_MOVE compare it with your threshold (50 in this example)
if (Math.abs(event.getX() - mLastX) > 50)
requestDisallowInterceptTouchEvent(true);
Related
I have a custom view which acts as a button. I am drawing all the canvas myself. Now I'm making an outline when ACTION_DOWN and remove it after ACTION_UP or ACTION_CANCEL.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("test", "ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.e("test", "ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e("test", "ACTION_CANCEL");
break;
}
return true;
}
This can work for me, except now it is blocking another gesture behind this view which is detecting ACTION_MOVE (scroll left).
If I return false, then it is working fine but now ACTION_UP is not called.
I want to call ACTION_UP if finger is lifted, but pass events down otherwise.
Have you tried overriding dispatchTouchEvent?
https://developer.android.com/reference/android/view/View.html#dispatchTouchEvent(android.view.MotionEvent)
UPDATE:
So touch events are a bit of a beast. The rundown of it is this...
They bubble up at first from your root container in your Activity. This is done by calling dispatchTouchEvent and then onInterceptTouchEvent assuming intercepting wasn't blocked by a child view.
If no view intercepts the event, it will bubble to the leaf node (such as a button) where onTouch is called. If the node doesn't handle it (returns true) its parent gets a chance and so on.
This means that you can use dispatchTouchEvent or onInterceptTouchEvent to spy on touch events without changing the behavior. Unless you're actually going to intercept the event I suggest using dispatchTouchEvent as it's guaranteed to run whereas intercepting may be blocked (example: DrawerLayout will intercept touch events near the edge in order to open the drawer).
So the final result is:
public class MyView extends Button {
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
Log.e("test", "ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.e("test", "ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e("test", "ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(event);
}
}
UPDATE:
Sorry, so I've been under the impression for some reason (mostly my poor reading) that we were dealing with the parent. Here's what I would do...
Go ahead and implement onTouch and return true to consume all the events. This means that any touch events that start on your view will be eaten up. What we'll do then is translate the point to the parent's coordinate space and manually pass the touch event up, it'll look like this inside your custom view...
private boolean passingTouchEventToParent = true;
final private Rect hitRect = Rect();
#Override
public boolean onTouch(MotionEvent event) {
// Handle your custom logic here
final ViewParent viewParent = getParent();
if (passingTouchEventToParent &&
viewParent != null &&
viewParent instanceof View) {
// Gets this view's hit rectangle in the parent's space
getHitRect(hitRect);
event.offsetLocation((float) hitRect.left, (float) hitRect.top);
passingTouchEventToParent = viewParent.onTouchEvent(event);
}
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
// Motion event finished, reset passingTouchEventToParent
passingTouchEventToParent = true;
}
return true;
}
How do i detect swipe up and down on a listView. I have tried the following methods
Using onSimpleGestureListener,onFling() works for fling( i.e when i leave the screen after swipe). But it doesnt get called on swipe( finger not lifted from screen finally).
2.In onScroll() of onSimpleGestureListener, distanceY is not helpful for detecting up and down swipe. It works fine for fling detection, but fluctuates its values from negative to positive in a particular swipe.
Using onTouchListener
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
before = event.getY();
break;
case MotionEvent.ACTION_MOVE:
now = event.getY();
if (now < before) {
upSwipe()
} else if (now > before) {
downSwipe();
}
}
return false;
}
When i swipe up, variable now sometimes becomes greater than variable previous and sometimes small. So both upSwipe() and downSwipe() is called.
I am banging my head for hours. Not able to sole this.
Have you tried to return true instead of returning false.
Based on the Detecting Common Gestures documentation.
"Beware of creating a listener that returns false for the ACTION_DOWN event. If you do this, the listener will not be called for the subsequent ACTION_MOVE and ACTION_UP string of events. This is because ACTION_DOWN is the starting point for all touch events."
I am developing an android application which has a scrollview. I have overriden my view from ScrollView and have implemented the onScrollChanged method. As the scrollview scrolls and reaches the bottom, it loads more products and add them to scrollvew. I am having a strange issue. First time when products loads and I try to drag up the scrollview, onScrollChanged never happens. Then I googled and found something:
scrollView.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
// Handle ListView touch events.
v.onTouchEvent(event);
return true;
}
});
After adding this, when I try to drag the scrollview from empty area of scrollview, it starts to call onScrollChanged and afterwards I start dragging from anywhere else, onScrollChanged is being called. My question is, why it is behaving like that? It should call onScrollChanged from whatever the touch event starts. I hope, I explained my point well. Any solution?
Why don't you use endless adapter with a view like ListView or GridView to achieve the desired behavior instead
I have some ImageViews inside a HorizontalScrollView.
I would like to be able to drag and drop the ImageViews somewhere else, but still maintain scrolling capability. Dragging should only be activated when the user starts a mostly vertical motion with their finger.
For now, I have drag and drop activate on long-press, but that is not a good solution.
To illustrate:
I had to do exactly this as well. After reading http://techin-android.blogspot.in/2011/11/swipe-event-in-android-scrollview.html I adapted the code as follows:
class MyOnTouchListener implements View.OnTouchListener {
static final int MIN_DISTANCE_Y = 40;
private float downY, upY;
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
downY = event.getY();
return true;
}
case MotionEvent.ACTION_MOVE: {
upY = event.getY();
float deltaY = downY - upY;
// swipe vertical?
if (Math.abs(deltaY) > MIN_DISTANCE_Y) {
if (deltaY < 0) {
//Start your drag here if appropriate
return true;
}
if (deltaY > 0) {
//Or start your drag here if appropriate
return true;
}
}
}
}
return false;
}
}
And then set the listener on the ImageViews:
imageView.setOnTouchListener(new MyOnTouchListener());
In this version of the code I am only checking for movement in the vertical direction (I also changed the minimum movement to be 40 instead of 100 as in the original code). If a vertical movement is detected, the specific ImageView can begin to drag or do any other actions you want. If a vertical movement is not detected, the ImageView's MyTouchListener returns false which means the ImageView does not consume the touch event. This allows the parent ScrollView to eventually get the touch event and consume it (for scroll detection). The answers here are helpful for understanding touch events: MotionEvent handling in ScrollView in Android.
I have a custom Android view which overrides onTouchEvent(MotionEvent) to handle horizontal scrolling of content within the view. However, when the ScrollView in which this is contained scrolls vertically, the custom view stops receiving touch events. Ideally what I want is for the custom view to continue receiving events so it can handle its own horizontal scrolling, while the containing view hierarchy deals with vertical scrolling.
Is there any way to continue receiving those motion events on scroll? If not, is there any other way to get the touch events I need?
Use requestDisallowInterceptTouchEvent(true) in the childview to prevent from vertical scrolling if you want to continue doing horizontal scrolling and latter reset it when done.
private float downXpos = 0;
private float downYpos = 0;
private boolean touchcaptured = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
downXpos = event.getX();
downYpos = event.getY();
touchcaptured = false;
break;
case MotionEvent.ACTION_UP:
requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_MOVE:
float xdisplacement = Math.abs(event.getX() - downXpos);
float ydisplacement = Math.abs(event.getY() - downYpos);
if( !touchcaptured && xdisplacement > ydisplacement && xdisplacement > 10) {
requestDisallowInterceptTouchEvent(true);
touchcaptured = true;
}
break;
}
super.onTouchEvent(event);
return true;
}
I'm answering my own question in case anyone else is as bad at Googling for the answer as I apparently was. :P
A workaround for this problem is to extend ScrollView and override the onInterceptTouchEvent method so that it only intercepts touch events where the Y movement is significant (greater than the X movement, according to one suggestion).