'How to implement hiding the toolbar when scrolling WebView?

I have an Activity with WebView and my own AppBarLayout. I want to implement hiding/showing the AppBarLayout with animation when scrolling the WebView, like in the Google Chrome app.

I tried different ways.

  • ObservableWebView, but it doesn't work correctly, it doesn't always show AppBar, especially if the page is short.

  • ConstraintLayout with animations. Something similar, but still not the same as in Google Chrome.

  • Coordinator layout. This is the best variant, I think. But it have some bugs.

I have wrote the code of layout like this:

<?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:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

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


        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/white"
            app:layout_scrollFlags="scroll|enterAlways">

            <View
                android:id="@+id/linkFrame"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorPrimary"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:visibility="visible" />

            <HorizontalScrollView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="8dp"
                android:layout_marginLeft="8dp"
                android:layout_marginEnd="8dp"
                android:layout_marginRight="8dp"
                android:background="@drawable/frame_link_browser"
                android:padding="8dp"
                app:layout_constraintBottom_toBottomOf="@+id/linkFrame"
                app:layout_constraintEnd_toStartOf="@+id/pauseButton"
                app:layout_constraintStart_toEndOf="@+id/linkDivider"
                app:layout_constraintTop_toTopOf="@+id/linkFrame">

                <TextView
                    android:id="@+id/linkTextView"
                    style="@style/BrowserSmallSecondaryText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:maxLines="1"
                    android:textColor="@color/textPrimary"
                    android:textIsSelectable="true"
                    tools:text="https://vk.com/id118573338"
                    tools:visibility="visible" />
            </HorizontalScrollView>


            <View
                android:id="@+id/linkDivider"
                style="@style/VerticalDivider"
                android:layout_height="24dp"
                android:layout_centerVertical="true"
                android:layout_marginStart="8dp"
                android:layout_marginLeft="8dp"
                android:background="#90FFFFFF"
                android:textColor="@color/textPrimary"
                app:layout_constraintBottom_toBottomOf="@+id/timerTextView"
                app:layout_constraintStart_toEndOf="@+id/timerTextView"
                app:layout_constraintTop_toTopOf="@+id/timerTextView"
                tools:visibility="visible" />

            <TextView
                android:id="@+id/timerTextView"
                style="@style/BrowserMiddlePrimaryText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginStart="8dp"
                android:layout_marginLeft="8dp"
                android:gravity="center_vertical"
                app:layout_constraintBottom_toBottomOf="@+id/linkFrame"
                app:layout_constraintStart_toStartOf="@+id/linkFrame"
                app:layout_constraintTop_toTopOf="@+id/linkFrame"
                tools:text="30" />

            <TextView
                android:id="@+id/noInternetTextView"
                style="@style/BrowserBigPrimaryText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:text="@string/no_internet_connection"
                android:visibility="gone"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/noInternetAdditionalTextView"
                style="@style/BrowserSmallSecondaryText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/no_internet_connection_additional"
                android:visibility="gone"
                app:layout_constraintLeft_toLeftOf="@+id/noInternetTextView"
                app:layout_constraintTop_toBottomOf="@+id/noInternetTextView" />

            <ImageButton
                android:id="@+id/pauseButton"
                android:layout_width="44dp"
                android:layout_height="44dp"
                android:background="@color/transparent"
                android:text="@string/pause"
                app:layout_constraintBottom_toBottomOf="@+id/linkFrame"
                app:layout_constraintEnd_toStartOf="@+id/settingsButton"
                app:layout_constraintTop_toTopOf="@+id/linkFrame"
                app:srcCompat="@drawable/ic_pause_circle_filled_white_36dp"
                tools:visibility="visible" />

            <ImageButton
                android:id="@+id/settingsButton"
                android:layout_width="44dp"
                android:layout_height="44dp"
                android:background="@color/transparent"
                android:text="@string/settings"
                app:layout_constraintBottom_toBottomOf="@+id/linkFrame"
                app:layout_constraintEnd_toEndOf="@+id/linkFrame"
                app:layout_constraintTop_toTopOf="@+id/linkFrame"
                app:srcCompat="@drawable/ic_settings_white_36dp"
                tools:visibility="visible" />

        </android.support.constraint.ConstraintLayout>

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


    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">


        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/white"
                android:orientation="vertical">

                <ProgressBar
                    android:id="@+id/progressBar"
                    style="?android:attr/progressBarStyleHorizontal"
                    android:layout_width="match_parent"
                    android:layout_height="4dp"
                    android:indeterminate="false"
                    android:max="100"
                    android:progress="0"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent" />

                <com.github.ksoichiro.android.observablescrollview.ObservableWebView xmlns:android="http://schemas.android.com/apk/res/android"
                    android:id="@+id/webView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />

            </LinearLayout>

        </android.support.v4.widget.NestedScrollView>
    </android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>

In this case the page is not scrolling. I have tried many variants. If in the behavior I put clean webview (without swiperefresh, nestedscrolling etc) it's work correctly, but if I lock and unlock the screen - WebView swells up and on the bottom of page appears white empty place. I have been trying to solve this problem for several days now, but it doesn’t work. Please help make a stable solution to this problem. Thank you very much.

I tried this method: How to Hide ActionBar/Toolbar While Scrolling Down in Webview but if I lock and unlock the screen - WebView swells up and on the bottom of page appears white empty place



Solution 1:[1]

I created WebView with this features and it works perfect:

import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.webkit.WebView;

public class NestedWebView extends WebView implements NestedScrollingChild, Scrollable {
    private int mLastY;
    private final int[] mScrollOffset = new int[2];
    private final int[] mScrollConsumed = new int[2];
    private int mNestedOffsetY;
    private NestedScrollingChildHelper mChildHelper;

    private int mScrollY;
    private ScrollViewCallback callback;
    private boolean firstScroll = true;


    public NestedWebView(Context context) {
        this(context, null);
    }

    public NestedWebView(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.webViewStyle);
    }

    public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
    }

    public void setOnScrollListener(ScrollViewCallback callback) {
        this.callback = callback;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean returnValue = false;

        MotionEvent event = MotionEvent.obtain(ev);
        final int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_DOWN) {
            mNestedOffsetY = 0;
        }
        int eventY = (int) event.getY();
        event.offsetLocation(0, mNestedOffsetY);
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                int deltaY = mLastY - eventY;
                // NestedPreScroll
                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
                    deltaY -= mScrollConsumed[1];
                    mLastY = eventY - mScrollOffset[1];
                    event.offsetLocation(0, -mScrollOffset[1]);
                    mNestedOffsetY += mScrollOffset[1];
                }
                returnValue = super.onTouchEvent(event);

                // NestedScroll
                if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) {
                    event.offsetLocation(0, mScrollOffset[1]);
                    mNestedOffsetY += mScrollOffset[1];
                    mLastY -= mScrollOffset[1];
                }
                break;
            case MotionEvent.ACTION_DOWN:
                returnValue = super.onTouchEvent(event);
                if (firstScroll) {
                    // dispatching first down scrolling properly by making sure that first deltaY will be -ve
                    mLastY = eventY - 5;
                    firstScroll = false;
                } else {
                    mLastY = eventY;
                }
                // start NestedScroll
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            default:
                returnValue = super.onTouchEvent(event);
                break;
        }
        return returnValue;
    }

    // Nested Scroll implements
    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        mChildHelper.setNestedScrollingEnabled(enabled);
    }

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

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

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

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

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

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

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

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


    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        mScrollY = t;
        dispatchOnScrollChanged(mScrollY);
    }

    private void dispatchOnScrollChanged(int scrollY) {
        if (callback != null) {
            callback.onScrollChanged(scrollY);
        }
    }

    // Scrollable implements
    @Override
    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {

    }

    @Override
    public void scrollVerticallyTo(int y) {

    }

    @Override
    public int getCurrentScrollY() {
        return mScrollY;
    }

    @Override
    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {

    }

    public interface ScrollViewCallback {
        void onScrollChanged(int scrollY);
    }


}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1