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
Search in Listview with Edittext
Android Google Maps, How to Make Each Marker Infowindow Open Different Activity
Why a New Viewmodel Is Created in Each Compose Navigation Route
Upgrade Realm in an Android Project
When to Use a Content Provider
How to Secure Android Shared Preferences
How to Draw Text with a Border on a Mapview in Android
How to Use and Style New Alertdialog from Appcompat 22.1 and Above
Differencebetween Fragment and Fragmentactivity
An Android App Remembers Its Data After Uninstall and Reinstall
How to Format Datetime in Flutter
How to Pass Multiple Primitive Parameters to Asynctask
Asynctask and Error Handling on Android
Android Asynctask Testing with Android Test Framework
How to Get the Number of Unread Gmail Mails (On Android)
How to Get the Current Location in Google Maps Android API V2