I have some layouts inside a scrollView. Those layouts have to be "swipable" in left and right directions but it creates some conflicts with the scrollView.
I've tried to disable the scrollView while touching on one of those layouts but in that case my scrollView is almost always disable.
This is how my layout looks like.
What I've now :
private void swipePositionsEvent() {
for(LinearLayout layout : layouts){
layout.setOnTouchListener(new OnSwipeTouchListener(this,
((ScrollView)findViewById(R.id.scroll_view_confirm_games))){
public void onSwipeRight() {
flipper.showNext();
}
public void onSwipeLeft() {
flipper.showPrevious();
}
});
}
In OnSwipeTouchListener:
Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
scrollView.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
scrollView.requestDisallowInterceptTouchEvent(false);
break;
}
return gestureDetector.onTouchEvent(event);
}
Thank you!
Detect whether the first movement is horizontal or vertical before you disable the scrollview.
Related
Is it possible to remove scroll event for a layout that is inside a ScrollView?
I have the following layout hierarchy:
ScrollView
RelativeLayout
LinearLayout
TableLayout
RelativeLayout
GestureOverlayView
I want to remove the scroll event for the RelativeLayout that contains a GestureOverlayView so that when the user draws a vertical line, the event is not intercepted as scroll but as drawing.
I don'y know you still need that or not but here is the answer I tested.
this is how you prevent the parent View from stealing touchEvent of a child view:
childView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.childView) {
v.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
return false;
}
});
you can go up in parents by:
v.getParent().getParent ....
The swipe of ViewPager is smooth inside the vertical scrollview when I add this code into my ViewPager.
mPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
But when I add onClickListener to my ImageView [which is found in the Fragment added to the Adapter of the Viewpager], the swipe of my ViewPager is incorrect. Incorrect wherein I need to have a STRAIGHT HORIZONTAL LINE swipe for it to go to another page unlike before [when I did not add the onClickListener], the viewpager goes to the next page even I do DIAGONAL swiping. Any help is much appreciated. Thanks.
I just used setontouchlistener to my imageview and set conditions there.
Here's the code:
imgCarousel.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
pStart = new Point((int) event.getX(), (int) event.getY());
break;
}
case MotionEvent.ACTION_UP: {
break;
}
case MotionEvent.ACTION_MOVE:{
break;
}
case MotionEvent.ACTION_CANCEL:{
Point pCancel = new Point((int) event.getX(), (int) event.getY());
int difference = pStart.x - pCancel.x;
if(difference > 10){
mPager.setCurrentItem(HomeCarousel.currentPage+1);
}
}
default:
break;
}
return true;
}
});
I think, it happens because ImageView intercept touchEvent using onClickListener.
You can use GestureOverlayView with your ImageView - http://developer.android.com/reference/android/gesture/GestureOverlayView.html
It could handle touch events without intercepting them.
Hi and Thanks for your help.
I need a button to perform the following behavior:
when the button is pressed an action begins (in this case a view scrolls)
as long as the button is kept pressed the action continues (the view continues scrolling)
when the button is released the action (the scrolling) stops.
I have tried this, but does not work:
Button left = (Button) findViewById(R.id.left);
left.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("","left");
return false;
}
});
I think you are looking for a combination of OnTouchListener and MotionEvent.ACTION_DOWN
Edit:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// TODO start scroll
break;
case MotionEvent.ACTION_UP:
// TODO stop scroll
break;
default:
break;
}
return false;
Hope this helps.. :)
Button button = (Button) findViewById(R.id.button);
button.setOnTouchListener(new View.OnTouchListener() {
#Override public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
//Start your work
break;
case MotionEvent.ACTION_UP:
// stop the work
break;
case MotionEvent.ACTION_CANCEL:
// stop the work
break;
}
return false;
}
});
Use MotionEvent.ACTION_DOWN and MotionEvent.ACTION_UP for detecting action up as well as down
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//start action here
break;
}
return super.onTouchEvent(event);
}
By default you can use onlongclick listener, But in your case it won't give effect as its max time 1-2 sec, so apply motion event on button and apply your logic..
You can use MotionEvent.ACTION_DOWN and MotionEvent.ACTION_UP for detecting action up and down
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//start action
break;
case MotionEvent.ACTION_UP:
//stop action
break;
}
return super.onTouchEvent(event);
}
make new scroll view
ScrollView sv=new ScrollView(this);
and when you click/start button. then start thread which increment int value and scroll, and on key action down. stop the thread.
//start thread which incrment value one by one
sv.scrollBy(x, y); //x the amount of pixels to scroll by horizontally
//y the amount of pixels to scroll by vertically
I have a scrollView with lot of elements
ScrollView scroller = (ScrollView)findViewById(R.id.scrollView);
I need to attach an onClickListener to the scrollview so I do
scroller.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// This is all you need to do to 3D flip
AnimationFactory.flipTransition(viewAnimator, FlipDirection.LEFT_RIGHT);
}
});
But this is not getting triggered when I touch. Any Ideas?
The best solution seem to put LinearLayout into ScrollView and set the setOnClickListener on it.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/myLayout"
android:clickable="true"
android:orientation="vertical">
<!-- content here -->
</LinearLayout>
</ScrollView>
in the Activity :
LinearLayout lin = (LinearLayout) fragment.rootView.findViewById(R.id.myLayout);
lin.setOnTouchListener(new setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Whatever
}
});
You need to set the setOnClickListener directly on the ScrollView's child.
Since a ScrollView can have only one child, you can simply use this approach:
ScrollView scrollView = //...
View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
// ...
}
//Set the onClickListener directly on the ScrollView's child
scrollView.getChildAt(0).setOnClickListener(mOnClickListener);
It is because the child of the ScrollView is getting the touch event of the user and not the ScrollView. You must set android:clickable="false" attribute to each and every child of the ScrollView for the onClickListener to work on ScrollView.
Or else the alternate could be to set the onClickListener on each of the ScrollView's children and handle it.
UPDATE 22/12/2020
sadly this also triggers after each scroll event.
This the actually the answer to the question without any odd cases by using View.OnTouchListener instead of View.OnClickListener on the ScrollView and detecting the MotionEvent.ACTION_UP where the finger is left off the screen.
To make sure that it's not a scroll, then save previous touched screen x, y values of the MotionEvent.ACTION_DOWN and compare it to those of MotionEvent.ACTION_UP. If they are not equal then certainly the user is moving their finger (i.e. scrolling) before they left it off the screen.
int mXOld, mYOld; // field values to save the tap down on the ScrollView
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mXOld = x;
mYOld = y;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (x == mXOld || y == mYOld) { // detecting it's not a horizontal/vertical scrolling in ScrollView
// HERE add the code you need when the ScrollView is clicked
Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
return false;
}
}
return false;
}
});
Original Answer: Odd case that is different than the question
My problem was somehow different, so I wanted to share it..
I have a ScrollView that I have to use match_parent in its width & height; and I have an internal TextView that is centered in the ScrollView.
The text of the TextView can be long so it occupies the full height of the ScrollView, and sometimes it can be short, so there will be blank areas on the top and bottom., So setting the OnClickListener on the TextView didn't help me whenever the text is short as I want the blank areas detects the click event as well; and also the OnClickListener on the ScrollView doesn't work..
So, I solved this by setting OnTouchListener on the ScrollView and put code into MotionEvent.ACTION_UP So it can kind of simulating complete tap by lefting off the finger off the screen.
private View.OnTouchListener mScrollViewTouchListener = new View.OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// DO Something HERE...
}
return false;
}
};
As #Zain pointed out, sometimes it is necessary to capture OnClicks for the whole area of the Scrollview, while the childs may be smaller or invisible.
To circumvent scrolling detected as an onClick, we used a GestureDetector:
final protected GestureDetector gestureDetector = new GestureDetector(getActivity(),
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
in onCreateView
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(gestureDetector.onTouchEvent(event)){
[do stuff]
}
return false;
}
});
I think you can custom a ScrollView, override the dispatchTouchEvent method, add add the custom onClick callback.
I have an android application. I have four screens and each screen contains a listview.. My requirement is that when a user flips horizontally then it should change the ui screen and when the user flips vertically it should scroll through the list.My code is
public boolean dispatchTouchEvent(MotionEvent event)
{
System.out.println("22");
int eventaction=event.getAction();
switch(eventaction)
{
case (MotionEvent.ACTION_MOVE):
System.out.println("move");
a=1;
break;
case MotionEvent.ACTION_UP:
System.out.println("up");
if(a==1)
{
view1.setAnimation(AnimationUtils.loadAnimation(this, R.anim.slide_left));
view1.showNext();
}
a=0;
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
I had a similar issue where the children were eating up all the scroll events. I ended up overiding the onInterceptTouchEvent in the parent to detect if it was a horizontal or vertical swipe and acting accordingly. You would want something like this
#Override
public boolean onInterceptTouchEvent (MotionEvent ev){
if(horizontalSwipe(ev)){
return true;
}
else
return false;
}
where you have some function that determines from your history if the new motion event constitutes a horizontal motion event.