Scroll Webview Horizontally Inside a Viewpager

Horizontally Scrolling WebView Inside ViewPager

So, I ended up developing a solution by disallowing propagation of touch events to the ViewPager and scrolling pages when the overScrollBy event of the WebView is triggered. You can determine the direction you want to scroll in by checking the deltaX of the event. Here's a kotlin solution:

override fun overScrollBy(deltaX: Int, deltaY: Int, scrollX: Int, scrollY: Int, scrollRangeX: Int, scrollRangeY: Int, maxOverScrollX: Int, maxOverScrollY: Int, isTouchEvent: Boolean): Boolean {
if(parent.parent is WebViewPager) {
val viewPager: WebViewPager = (parent.parent as WebViewPager)
if(viewPager.scrollState == ViewPager.SCROLL_STATE_IDLE) {
if(deltaX > 0) {
if(checkAdapterLimits(1, viewPager.currentItem)) //right
(parent.parent as ViewPager).setCurrentItem(viewPager.currentItem + 1,true)
}
else {
if(checkAdapterLimits(-1, viewPager.currentItem)) //left
(parent.parent as ViewPager).setCurrentItem(viewPager.currentItem - 1,true)
}
}
}
return false
}

override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
parent.requestDisallowInterceptTouchEvent(true)

return super.onInterceptTouchEvent(event)
}

private fun checkAdapterLimits(direction: Int, position: Int) : Boolean {
return if(direction < 0) //left
position >= 1
else //right
position < (parent.parent as ViewPager).adapter.count - 1

}

Edited, in order to stop scrolling to just one page at a time, you need to check the scroll state of the ViewPager to make sure it's idle.

You'll need to add a property to your custom ViewPager and toggle it with an OnPageChangedListener on the ViewPager instance you're using.

ViewPager:

class WebViewPager(context: Context, attrs: AttributeSet) : ViewPager(context, attrs) {

var scrollState: Int = SCROLL_STATE_IDLE

fun setDurationScroll(millis: Int) {
try {
val viewpager = ViewPager::class.java
val scroller = viewpager.getDeclaredField("mScroller")
scroller.isAccessible = true
scroller.set(this, OwnScroller(context, millis))
} catch (e: Exception) {
e.printStackTrace()
}

}

inner class OwnScroller(context: Context, durationScroll: Int) : Scroller(context, DecelerateInterpolator()) {

private var durationScrollMillis = 1

init {
this.durationScrollMillis = durationScroll
}

override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) {
super.startScroll(startX, startY, dx, dy, durationScrollMillis)
}
}

}

OnPageChangedListener:

viewPager!!.setDurationScroll(300)
viewPager!!.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {

override fun onPageScrollStateChanged(state: Int) {
viewPager.scrollState = state
}

override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {

}
override fun onPageSelected(position: Int) {

}

})

Scroll webview horizontally inside a ViewPager

The following is a real working solution which will scroll the WebView on a horizontal swipe as long as it can scroll. If the WebView cannot further scroll, the next horizontal swipe will be consumed by the ViewPager to switch the page.

Extending the WebView

With API-Level 14 (ICS) the View method canScrollHorizontally() has been introduced, which we need to solve the problem. If you develop only for ICS or above you can directly use this method and skip to the next section. Otherwise we need to implement this method on our own, to make the solution work also on pre-ICS.

To do so simply derive your own class from WebView:

public class ExtendedWebView extends WebView {
public ExtendedWebView(Context context) {
super(context);
}

public ExtendedWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public boolean canScrollHor(int direction) {
final int offset = computeHorizontalScrollOffset();
final int range = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
}

Important: Remember to reference your ExtendedWebView inside your layout file instead of the standard WebView.

Extending the ViewPager

Now you need to extend the ViewPager to handle horizontal swipes correctly. This needs to be done in any case -- no matter whether you are using ICS or not:

public class WebViewPager extends ViewPager {
public WebViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ExtendedWebView) {
return ((ExtendedWebView) v).canScrollHor(-dx);
} else {
return super.canScroll(v, checkV, dx, x, y);
}
}
}

Important: Remember to reference your WebViewPager inside your layout file instead of the standard ViewPager.

That's it!

Update 2012/07/08: I've recently noticed that the stuff shown above seems to be no longer required when using the "current" implementation of the ViewPager. The "current" implementation seems to check the sub views correctly before capturing the scroll event on it's own (see canScroll method of ViewPager here). Don't know exactly, when the implementation has been changed to handle this correctly -- I still need the code above on Android Gingerbread (2.3.x) and before.

Scroll Webview in Viewpager

don't ask me why this code gets formatted like this,

1. implement a custom ViewPager Instance like this:

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

public class CustomViewPager extends ViewPager {

private MagazineWebView_WithoutFlipWebView mCurrentPageWebView_; //custom webview

public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

if (Constants.LOGGING) {
Log.v(Constants.LOG_OEAMTC_APP, "CustomViewPager - onInterceptTouchEvent");
}

// if view zoomed out (view starts at 33.12... scale level) ... allow
// zoom within webview, otherwise disallow (allow viewpager to change
// view)
if (mCurrentPageWebView_ != null && (mCurrentPageWebView_.getScale() * 100) > 34) {
Log.v(Constants.LOG_OEAMTC_APP, "CustomViewPager - intrcepted: " + String.valueOf((mCurrentPageWebView_.getScale() * > 100)));
this.requestDisallowInterceptTouchEvent(true);
}
else {
if (mCurrentPageWebView_ != null) {
Log.v(Constants.LOG_OEAMTC_APP,
"CustomViewPager - not intrcepted: " + String.valueOf(mCurrentPageWebView_.getScale() * 100));
}
this.requestDisallowInterceptTouchEvent(false);
}

return super.onInterceptTouchEvent(event);
}

public MagazineWebView_WithoutFlipWebView getCurrentPageWebView() {
return mCurrentPageWebView_;
}

public void setCurrentPageWebView(MagazineWebView_WithoutFlipWebView currentPageWebView) {
mCurrentPageWebView_ = currentPageWebView;
}
}

2. in your main (ViewPager) Activity add the following lines to the view pager

mViewPager_ = new AwesomePagerAdapter();
viewpapgerInLayout = (CustomViewPager) findViewById(R.id.awesomepager);
viewpapgerInLayout.setAdapter(mViewPager_);
viewpapgerInLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
viewpapgerInLayout.setCurrentPageWebView(mLstPagesWebviews_.get(position));
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageScrollStateChanged(int state) {

}
});

3. finally, run it :=) if the zoom level is at initial zoom,
changes pages is allowed, all the other time you can navigate your web view

Ps.: *Don't forget* to change your ViewPager in your *.xml file with the CustomViewPager Class you just created

good luck :)

Horizontal ScrollView in html page with ViewPager

You will need to implement onInterceptTouchEvent(MotionEvent), determine if the touch is within the horizontal scroll section of your WebView, and then call requestDisallowInterceptTouchEvent(boolean) on the parent of the WebView (assuming that is the ViewPager).

Determining if the touch is within the horizontal scroller may be tricky, but you can probably accomplish that with some JS callbacks that the WebView listens for.

Before all of that, you may want to consider the UX implications of this though. It may be confusing / disorienting to the user to have horizontal scrolling content in a horizontal scrolling view.

WebView horizontal scrolling fails

I just searched this and ran into the same question on SO, check out this answer and see if that helps.

He added the following:

mWebView.getSettings().setUseWideViewPort(true);

Yeah It seems like the ViewPager consumes your horizontal scroll, check out these two solutions. Hopefully they help.

  • Solution 1
  • Solution 2


Related Topics



Leave a reply



Submit