Прокрутка HTML-событий в Android WebView, которая находится внутри ScrollView

У меня есть веб-страница для тестирования (https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html), которая просто печатает счетчик события прокрутки, который html обнаружил в фиксированный заголовок

Когда Android WebView является единственным прокручиваемым элементом во фрагменте, все в порядке, и WebView отправляет события прокрутки на страницу

Если я хочу добавить собственные элементы выше и ниже WebView, тогда все становится намного сложнее.

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="SOMETHING ABOVE THE WEBVIEW" />

        <WebView
            android:id="@+id/webview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="SOMETHING BELOW THE WEBVIEW" />

    </LinearLayout>
</ScrollView>

Я знаю, что не хорошо иметь WebView внутри ScrollView, но я должен предоставить единый прокручиваемый опыт с гибридным контентом и соответствующими прокручивающими событиями в html-документе. Я нашел много вопросов по этому вопросу, но я смог создать полное сквозное решение


Кроме того, я знаю, что у lint есть официальная проверка для этого:

NestedScrolling --------------- Описание: Вложенные виджеты прокрутки

Приоритет: 7/10 Уровень важности: Предупреждение Категория: Правильность

Виджет прокрутки, такой как ScrollView, не должен содержать вложенных прокрутки виджеты, поскольку это имеет различные проблемы удобства использования

И все же я не могу реализовать контент веб-представления в native, поэтому мне нужен альтернативный способ сделать это

Ответ 1

Чтобы сохранить Webview внутри scrollview здесь, вам нужно измерить высоту веб-представления и установить его в параметрах макета.

Здесь я попытался дать ответ для прокручиваемого веб-представления.

<ScrollView
        android:layout_width="fill_parent" android:background="#FF744931"
        android:layout_height="fill_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            tools:ignore="WebViewLayout">

            <TextView
                android:id="@+id/txtVoiceSeachQuery"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#FF0000"
                android:textSize="26sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="SOMETHING ABOVE THE WEBVIEW" />

            <com.example.ScrollableWebView
                android:id="@+id/webview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:isWebViewInsideScroll="true" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="SOMETHING BELOW THE WEBVIEW" />

        </LinearLayout>
    </ScrollView>

RES/значение/attrs.xml

Чтобы добавить атрибут для пользовательского элемента управления

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollableWebView">
    <attr name="isWebViewInsideScroll" format="boolean"></attr>
</declare-styleable>
</resources>

ScrollableWebView

public class ScrollableWebView extends WebView {
    private boolean webViewInsideScroll = true;
    public static final String RESOURCE_NAMESPACE = "http://schemas.android.com/apk/res-auto";

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

    public ScrollableWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWebViewInsideScroll(attrs.getAttributeBooleanValue
                (RESOURCE_NAMESPACE, "isWebViewInsideScroll", true));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isWebViewInsideScroll()) {
            int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();
            setLayoutParams(params);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    public boolean isWebViewInsideScroll() {
        return webViewInsideScroll;
    }

    public void setWebViewInsideScroll(boolean webViewInsideScroll) {
        this.webViewInsideScroll = webViewInsideScroll;
    }
}

Чтобы извлечь значение атрибута, вы также можете использовать Stylable, но здесь я сделал, не используя его.

ScrollableWebView webview = (ScrollableWebView) findViewById(R.id.webview);
        webview.loadUrl("https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html");

Ниже приведена ссылка выхода

Если вы не хотите создавать файл атрибута и добавлять пользовательские атрибуты в res/values ​​/attrs.xml, вы можете игнорировать этот файл и проверить этот pastebin здесь я дал без какого-либо пользовательского атрибута, например isWebViewInsideScroll. вы также можете удалить его из макета xml. Дайте мне знать, если что-нибудь.

Ответ 2

если вы разместите веб-просмотр внутри scrollview, вы не получите эффект прокрутки html, потому что ваш контент в веб-браузере не будет прокручиваться (он будет размещен на полной длине внутри scrollview.)
Чтобы удовлетворить ваши потребности в размещении элементов выше и ниже, вы можете прослушивать прокрутку веб-просмотра и использовать DragViewHelper или nineoldandroids для перемещения header и footer, поэтому пользователь будет думать, что они представляют собой один элемент (вам не требуется прокрутка).

webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
  @Override
  public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

      ViewHelper.setTranslationY(headerTextView, -event.getY());

  }
});


public class ObservableWebView extends WebView {
private OnScrollChangeListener onScrollChangeListener;

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

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

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

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if (onScrollChangeListener != null) {
        onScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
    }
}

public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {
    this.onScrollChangeListener = onScrollChangeListener;
}

public OnScrollChangeListener getOnScrollChangeListener() {
    return onScrollChangeListener;
}

public interface OnScrollChangeListener {
    /**
     * Called when the scroll position of a view changes.
     *
     * @param v          The view whose scroll position has changed.
     * @param scrollX    Current horizontal scroll origin.
     * @param scrollY    Current vertical scroll origin.
     * @param oldScrollX Previous horizontal scroll origin.
     * @param oldScrollY Previous vertical scroll origin.
     */
    void onScrollChange(WebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY);
}
}

Этот пример поможет вам скрыть header, я использовал nineoldandroid для него

Ответ 3

на самом деле это не так хорошо, чтобы прокрутить представление в другое. Попытайтесь использовать это:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

И

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:layout_weight="1"/>

Ответ 4

Кажется, самый элегантный способ, который я мог найти, чтобы справиться с этим, заключается в следующем: - Прослушивание свитков SrollView: вы можете использовать ObservableScrollView или вызвать setOnScrollChangeListener() из уровня API 23 - Рассчитать сдвиг Y сдвига в пикселях - Вызовите WebView.evaluateJavascript() - Передайте все детали события прокрутки Итак, общие понятия проходят: "$ (Документ).trigger( 'свитка');" как первый параметр оценитьJavascript

Я все еще проверяю детали и разрабатываю кинки, но это похоже на лучший способ, я попытаюсь отредактировать этот ответ с дополнительной информацией, поскольку я решаю этот

Если в любом случае есть лучшее решение, я бы хотел его услышать.

Ответ 5

У меня такая же проблема в последнее время, и я нашел ваши сообщения здесь:)

У меня есть WebView, вложенный в ScrollVIew. И страница, которую я загрузил в WebView, нужно вызвать функцию JS, когда она прокручивается до конца, но в режиме прокрутки веб-страница window.onscroll = functionXXX() {} никогда не вызывается.

Наконец, я должен установить OnScrollListener в ScrollVIew и вызвать функцию JS вручную по коду ниже

@Override
public void onScroll(int scrollY) {   
    if (!scrollView.canScrollVertically(scrollY)) {
        mWebView.loadUrl("javascript:functionXXX();");
    }
}

Возможно, наши ситуации разные, но я надеюсь, что это даст вам некоторое вдохновение:)