9

The NestedScrollView provided with the support library doesn't work properly with the WebView (related bug report). Because of this I have set out to implement NestedScrollingChild for a WebView and it appears to work fine for the first scroll, collapsing and expanding the AppBarLayout but any subsequent scrolls fail to collapse it.

My WebView:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.WebView;

/**
 * Created by carlos on 2/19/16.
 */
public class MyWebView extends WebView implements NestedScrollingChild {
    private static final String TAG = MyWebView.class.getSimpleName();
    private NestedScrollingChildHelper helper = new NestedScrollingChildHelper(this);

    public MyWebView(Context context) {
        super(context);
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MyWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public MyWebView(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) {
        super(context, attrs, defStyleAttr, privateBrowsing);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {

        int dx = l - oldl;
        int dy = t - oldt;
        if (dy != 0) helper.startNestedScroll(View.SCROLL_AXIS_VERTICAL);
        helper.dispatchNestedPreScroll(dx, dy, null, null);
        if (dy != 0) helper.stopNestedScroll();

        super.onScrollChanged(l, t, oldl, oldt);
    }


    @Override
    public boolean isNestedScrollingEnabled() {
        return helper.isNestedScrollingEnabled();
    }

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        helper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return helper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        helper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return helper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return helper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return helper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return helper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return helper.dispatchNestedPreFling(velocityX, velocityY);
    }
}

My activity:

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebViewClient;

public class ScrollingActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scrolling);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        MyWebView view = (MyWebView) findViewById(R.id.webview);
        view.setNestedScrollingEnabled(true);
        view.setWebViewClient(new WebViewClient());
        WebSettings settings = view.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setAllowContentAccess(true);
        settings.setAppCacheEnabled(true);
        settings.setDatabaseEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
        settings.setJavaScriptEnabled(true);
        settings.setSupportZoom(true);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setBuiltInZoomControls(true);

        settings.setAppCacheEnabled(true);
        settings.setAppCachePath(getCacheDir().getAbsolutePath());
        settings.setDatabaseEnabled(true);
        settings.setSupportMultipleWindows(true);
        settings.setLoadWithOverviewMode(true);
        settings.setUseWideViewPort(true);
        settings.setDomStorageEnabled(true);
        settings.setAllowContentAccess(true);
        settings.setAllowFileAccess(true);
        settings.setSaveFormData(true);
        // findViewById(R.id.nested).setLayerType(View.LAYER_TYPE_HARDWARE, null);
        // view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        view.loadUrl("http://vimeo.com");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_scrolling, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

My styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>

</resources>

My dimensions file:

<resources>
    <dimen name="app_bar_height">180dp</dimen>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="text_margin">16dp</dimen>
</resources>

My activity layout (activity_scrolling.xml):

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.childscrollview.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|snap|enterAlways">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <com.childscrollview.MyWebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        ></com.childscrollview.MyWebView>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email"
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="bottom|end"/>

</android.support.design.widget.CoordinatorLayout>

So what is wrong with my implementation or is this a bug?

Thanks.

EDIT: Google now says the WebView isn't supported like this.

casolorz
  • 8,486
  • 19
  • 93
  • 200

2 Answers2

2

I had tested many WebView, which implements NestedScrollingChild.

However, the only correct implementation, is found at https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java

You can use NestedWebView without any modification. It works very smoothly.

I would also like to share my XML files.

web_view_fragment_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/coordinator_layout"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:elevation="4dp"
        android:elevation="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" >

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"

            app:layout_scrollFlags="scroll|enterAlways|snap"

            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary">

            <!-- Toolbar -->
            <include layout="@layout/toolbar"/>

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <!-- http://stackoverflow.com/questions/14171471/remove-vertical-padding-from-horizontal-progressbar -->
    <!-- http://stackoverflow.com/questions/32464749/horizontal-progress-bar-is-not-visible-if-placed-above-toolbar-for-android-5 -->
    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="8dp"
        android:id="@+id/progress_bar"
        android:layout_gravity="top"
        android:layout_marginBottom="0dp"
        android:layout_marginTop="-4dp"
        android:elevation="4dp"
        android:max="100" />

    <FrameLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:foreground="?attr/headerShadow"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout>

A fragment, contains ViewAnimator (Multiple WebView is added into ViewAnimator and a bottom navigation bar.

The fragment is added into content FragmeLayout

web_view_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/web_view_linear_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal" >

    <ViewAnimator
        android:id="@+id/web_view_view_animator"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
    />

    <LinearLayout
        android:id="@+id/navigation_linear_layout"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="?attr/colorPrimary"
        android:orientation="horizontal" >
        <LinearLayout
            android:id="@+id/left_linear_layout"
            android:layout_width="0dp"
            android:width="0dp"
            android:layout_weight="0.5"
            android:layout_height="match_parent"
            android:gravity="left"
            android:clickable="true"
            android:background="?attr/selectableItemBackground"
            android:theme="@style/Base.ThemeOverlay.AppCompat.Dark" >
            <ImageView
                android:id="@+id/left_image_view"
                android:scaleType="center"
                android:src="@drawable/ic_chevron_left_white_24dp"
                android:layout_width="48dp"
                android:layout_height="match_parent" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/right_linear_layout"
            android:layout_width="0dp"
            android:width="0dp"
            android:layout_weight="0.5"
            android:layout_height="match_parent"
            android:gravity="right"
            android:clickable="true"
            android:background="?attr/selectableItemBackground"
            android:theme="@style/Base.ThemeOverlay.AppCompat.Dark"

            android:orientation="horizontal">
            <TextView
                android:id="@+id/next_text_view"
                android:layout_width="0dp"
                android:width="0dp"
                android:layout_weight="1"
                android:layout_height="match_parent"

                android:gravity="center_vertical|right"
                android:ellipsize="end"
                android:textSize="12sp"
                android:maxLines = "2"
                android:minLines = "1"
                android:lineSpacingMultiplier="1.2"/>

            <ImageView
                android:id="@+id/right_image_view"
                android:scaleType="center"
                android:src="@drawable/ic_chevron_right_white_24dp"
                android:layout_width="48dp"
                android:layout_height="match_parent" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

Here's the outcome

Outcome before scrolling

enter image description here

Outcome after scrolling

enter image description here

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Does it work with sites that keep growing? That was my issue last time I tried this. I think vimeo was the site that was giving me issues. – casolorz Jan 16 '18 at 20:06
  • May I know what do you mean sites that keep growing? I had tested `NestedWebView.java` implementation across many different sites, no issue found so far. The scroll behavior is quite smooth and nice. – Cheok Yan Cheng Jan 16 '18 at 20:08
  • Sites that give you more data when you get to bottom. They have some sort of list of videos, stories, posts, etc, and when you reach the bottom they give you more. The issue I had is that those sites could not detect that the bottom was reached. – casolorz Jan 16 '18 at 20:12
  • If you have such site URL, I can test it for u on my site. – Cheok Yan Cheng Jan 16 '18 at 20:14
-1

What i suggest you Use fragment inside your main xml file .Inside fragment you use webview may be this resolve your problem.

Rahil Ali
  • 957
  • 10
  • 25
  • Do I do that with a `NestedScrollView`? where do I put `@string/appbar_scrolling_view_behavior`? can you give an example? – casolorz Mar 01 '16 at 19:57
  • This does not solve the issue, placing a view inside a fragment does not change the structure of the view hierarchy. – Bryan Aug 21 '17 at 15:23