[android] Disable scrolling in webview?

Until now I have been an iPhone developer only and now I have decided to give Android a whirl. Something I haven't been able to figure out on Android is how to programmatically prevent scrolling in a WebView?

Something similar to iPhones prevention of the onTouchMove event would be great!

The answer is


I haven't tried this as I have yet to encounter this problem, but perhaps you could overrive the onScroll function?

@Override
public void scrollTo(int x, int y){
super.scrollTo(0,y);
}

My dirty, but easy-to-implement and well working solution:

Simply put the webview inside a scrollview. Make the webview to be far too bigger than the possible content (in one or both dimensions, depending on the requirements). ..and set up the scrollview's scrollbar(s) as you wish.

Example to disable the horizontal scrollbar on a webview:

<ScrollView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scrollbars="vertical"
>
    <WebView
        android:id="@+id/mywebview"
        android:layout_width="1000dip"
        android:layout_height="fill_parent"
    />
</ScrollView>

I hope this helps ;)


This should be the complete answer. As suggested by @GDanger . Extend WebView to override the scroll methods and embed the custom webview within layout xml.

public class ScrollDisabledWebView extends WebView {

    private boolean scrollEnabled = false;

    public ScrollDisabledWebView(Context context) {
        super(context);
        initView(context);
    }

    public ScrollDisabledWebView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        initView(context);
    }
    // this is important. Otherwise it throws Binary Inflate Exception.
    private void initView(Context context) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
                                   int scrollRangeX, int scrollRangeY, int maxOverScrollX,
                                   int maxOverScrollY, boolean isTouchEvent) {
        if (scrollEnabled) {
            return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
        }
        return false;
    }

    @Override
    public void scrollTo(int x, int y) {
        if (scrollEnabled) {
            super.scrollTo(x, y);
        }
    }

    @Override
    public void computeScroll() {
        if (scrollEnabled) {
            super.computeScroll();
        }
    }
}

And then embed in layout file as follows

<com.sample.apps.ScrollDisabledWebView
    android:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"
    tools:context="com.sample.apps.HomeActivity"/>

Then in the Activity, use some additional methods for disabling scrollbars too.

ScrollDisabledWebView webView = (ScrollDisabledWebView) findViewById(R.id.webView);
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);

studying the above answers almost worked for me ... but I still had a problem that I could 'fling' the view on 2.1 (seemed to be fixed with 2.2 & 2.3).

here is my final solution

public class MyWebView extends WebView
{
private boolean bAllowScroll = true;
@SuppressWarnings("unused") // it is used, just java is dumb
private long downtime;

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

public void setAllowScroll(int allowScroll)
{
    bAllowScroll = allowScroll!=0;
    if (!bAllowScroll)
        super.scrollTo(0,0);
    setHorizontalScrollBarEnabled(bAllowScroll);
    setVerticalScrollBarEnabled(bAllowScroll);
}

@Override
public boolean onTouchEvent(MotionEvent ev) 
{
    switch (ev.getAction())
    {
    case MotionEvent.ACTION_DOWN:
        if (!bAllowScroll)
            downtime = ev.getEventTime();
        break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:
        if (!bAllowScroll)
        {
            try {
                Field fmNumSamples = ev.getClass().getDeclaredField("mNumSamples");
                fmNumSamples.setAccessible(true);
                Field fmTimeSamples = ev.getClass().getDeclaredField("mTimeSamples");
                fmTimeSamples.setAccessible(true);
                long newTimeSamples[] = new long[fmNumSamples.getInt(ev)];
                newTimeSamples[0] = ev.getEventTime()+250;
                fmTimeSamples.set(ev,newTimeSamples);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        break;
    }
    return super.onTouchEvent(ev);
}

@Override
public void flingScroll(int vx, int vy)
{
    if (bAllowScroll)
        super.flingScroll(vx,vy);
}

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
    if (bAllowScroll)
        super.onScrollChanged(l, t, oldl, oldt);
    else if (l!=0 || t!=0)
        super.scrollTo(0,0);
}

@Override
public void scrollTo(int x, int y)
{
    if (bAllowScroll)
        super.scrollTo(x,y);
}

@Override
public void scrollBy(int x, int y)
{
    if (bAllowScroll)
        super.scrollBy(x,y);
}
}

Set a listener on your WebView:

webView.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                return(event.getAction() == MotionEvent.ACTION_MOVE));
            }
        });

Adding the margin details to the body will prevent scrolling if your content properly wraps, as so:

<body leftmargin="0" topmargin="0" rightmargin="0" bottommargin="0">

Easy enough, and a lot less code + bug overhead :)


I don't know if you still need it or not, but here is the solution:

appView = (WebView) findViewById(R.id.appView); 
appView.setVerticalScrollBarEnabled(false);
appView.setHorizontalScrollBarEnabled(false);

If you subclass Webview, you can simply override onTouchEvent to filter out the move-events that trigger scrolling.

public class SubWebView extends WebView {

    @Override
    public boolean onTouchEvent (MotionEvent ev)    {
        if(ev.getAction() == MotionEvent.ACTION_MOVE) {
            postInvalidate();
            return true;
        }
        return super.onTouchEvent(ev);
    }
    ...

I resolved the flicker and auto-scroll by:

webView.setFocusable(false);
webView.setFocusableInTouchMode(false);

However if it still does not work for some reason, simply make a class that extends WebView, and put this method on it:

// disable scroll on touch
  setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      return (event.getAction() == MotionEvent.ACTION_MOVE);
    }
  });

I am suggesting to extend WebView, in order to provide more effective control, like disable/enable swipes, enable/disable touches, listeners, control transitions, animations, define settings internally so on..


Making the WebView ignore motion events is the wrong way to go about it. What if the WebView needs to hear about these events?

Instead subclass WebView and override the non-private scroll methods.

public class NoScrollWebView extends WebView {
    ...
    @Override
    public boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, 
                                int scrollRangeX, int scrollRangeY, int maxOverScrollX, 
                                int maxOverScrollY, boolean isTouchEvent) {
        return false;
    }

    @Override
    public void scrollTo(int x, int y) {
        // Do nothing
    }

    @Override
    public void computeScroll() {
        // Do nothing
    }
}

If you look at the source for WebView you can see that onTouchEvent calls doDrag which calls overScrollBy.


To Disable scroll use this

webView.setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) 
    {
        return (event.getAction() == MotionEvent.ACTION_MOVE);
    }
});

This may not be the source of your problem but I have spent hours trying to track down why my application that worked fine on the iphone cause the android browser to constantly throw up the vertical/horizontal scrollbars and actually move the page. I had an html body tag with height and width set to 100%, and the body was full of various tags in different locations. No matter what I tried, the android browsers would show their scroll bars and move the page just a little bit, particularly when doing a fast swipe. The solution that worked for me without having to do anything in an APK was to add the following to the body tag:

leftmargin="0" topmargin="0"

It seems the default margins for body tag were being applied on the droid but ignored on the iphone


Just use android:focusableInTouchMode="false" on your webView.