ViewPager inside ScrollView - vertical scroll doesn't work
Solution 1
I had the problem that ViewPager
behaved weird and I found out the reason for that it is because sometimes the ScrollView
regains focus and the ViewPager
then loses focus. If you have a ViewPager
in a ScrollView
and you want it always to stay in focus when you touch it and the ScrollView
never getting a focus, setting the requestDisallowInterceptTouchEvent
does that. Did that help?
mViewPager= (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(adapter);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
Solution 2
Extending on to Marius's answer to allow vertical scrolling on the parent:
I noticed that when you call the 'requestDisallowInterceptTouchEvent' method on the initial scroll, it will block scrolling vertically on the parent view when scrolling vertically on top of the view pager.
My solution was to only trigger that method after the user started scrolling horizontally for a specified distance (the 'margin' variable).
mViewPager.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
// How far the user has to scroll before it locks the parent vertical scrolling.
final int margin = 10;
final int fragmentOffset = v.getScrollX() % v.getWidth();
if (fragmentOffset > margin && fragmentOffset < v.getWidth() - margin) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
Solution 3
mPager.setOnTouchListener(new View.OnTouchListener() {
int dragthreshold = 30;
int downX;
int downY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getRawX();
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
if (distanceY > distanceX && distanceY > dragthreshold) {
mPager.getParent().requestDisallowInterceptTouchEvent(false);
mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
} else if (distanceX > distanceY && distanceX > dragthreshold) {
mPager.getParent().requestDisallowInterceptTouchEvent(true);
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
mPager.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
Solution 4
I had same problem try my code to, you we should to disable vertical scroll and sent event to Scrollview :
private int dragThreshold = 10, int downX = 0, int downY = 0;
vPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP){
downX = (int) event.getRawX();
downY = (int) event.getRawY();
return false;
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
ExceptionHelpers.dLog("OnTouchListener", "distance X : "+distanceX+" , distance Y : "+distanceY);
if(distanceY > distanceX && distanceY > dragThreshold){
v.getParent().requestDisallowInterceptTouchEvent(false);
return true;
}
}
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
Also you can set Min Horizontal Scroll By Add :
else {
distanceX > distanceY && distanceX > dragThreshold)
}
And remember, Building workers do -> Try and Try and Try
developers (us) do -> Think And Think and Try
Good Lock
Solution 5
Try this
public static class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) < Math.abs(distanceX);
}
}
and for the viewpager
final GestureDetector mGestureDetector= new GestureDetector(this,new XScrollDetector());
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScrollView.requestDisallowInterceptTouchEvent(mGestureDetector.onTouchEvent(event));
return false;
}
});
Genkaku
Updated on July 09, 2022Comments
-
Genkaku almost 2 years
So, I have this
ScrollView
with one child - aLinearLayout
that has two children: aTextView
and aViewPager
.ViewPager
contains layout with many elements, that's why i need the ability to scroll vertically. Only pages inViewPager
may be scrolled horizontally (that is: I'd like to swipe horizontally only within theViewPager
). That oneTextView
must not scroll horizontally but should scroll together with myViewPager
.Simple? No.
I've seen extremely similar issues at StackOverflow popping up (here, here and here and here). None of the suggested solutions work for me :(
What I see is this <- my sweet UI :), However I cannot scroll vertically :(
Embedding ScrollViews inside ViewPager is not an option - desing of the UI forbids this.
Maybe it's something with my programmatically filling each page in view pager? Hmmm...
Any suggestions would be appreciated.
My code:
activity_main.xml:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scrollView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white" android:fillViewport="true" > <LinearLayout android:id="@+id/linearLayoutGeneral" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tvText" android:layout_width="fill_parent" android:layout_height="200dp" android:text="Test text" /> <android.support.v4.view.ViewPager android:id="@+android:id/viewpager" android:layout_width="fill_parent" android:layout_height="fill_parent" > </android.support.v4.view.ViewPager> </LinearLayout> </ScrollView>
each page in ViewPager has this layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <LinearLayout android:id="@+id/layoutData" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > </LinearLayout> </LinearLayout>
single element's layout in such a page:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="40dp" android:gravity="center" android:orientation="vertical" android:background="@android:color/white" > <TextView android:id="@+id/textView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Large Text" android:background="@android:color/black" android:textColor="@android:color/white" android:textAppearance="?android:attr/textAppearanceLarge" /> </LinearLayout>
A single page Fragment is also very simple:
public class DayFragment extends Fragment { private static final String TAG = DayFragment.class.getSimpleName(); public String tag; LinearLayout data; View mView; final int ROWS_NUM = 60; public DayFragment() { } /** * (non-Javadoc) * * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, * android.view.ViewGroup, android.os.Bundle) */ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // We have different layouts, and in one of them this // fragment's containing frame doesn't exist. The fragment // may still be created from its saved state, but there is // no reason to try to create its view hierarchy because it // won't be displayed. Note this is not needed -- we could // just run the code below, where we would create and return // the view hierarchy; it would just never be used. return null; } mView = (LinearLayout) inflater.inflate(R.layout.day, container, false); setUpControls(); generateData(); String text = getArguments().getString("text"); Log.d(TAG, "creating view with text: " + text); return mView; } private void setUpControls() { data = (LinearLayout) mView.findViewById(R.id.layoutData); } private void generateData() { for (int i = 0; i < ROWS_NUM; i++) { View v = createRow(i); data.addView(v); } } private View createRow(int num) { LayoutInflater inflater = (LayoutInflater) getActivity() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflater.inflate(R.layout.row, null); TextView tv = (TextView) v.findViewById(R.id.textView); tv.setText("Data nr: " + num); return v; } public static DayFragment newInstance(String text) { Log.d(TAG, "newInstance with text: " + text); DayFragment f = new DayFragment(); f.tag = text; // Supply text input as an argument. Bundle args = new Bundle(); args.putString("text", text); f.setArguments(args); return f; } }