WebView не загружается в правильном положении

#android #html #android-layout #scroll #android-webview

#Android #HTML #android-макет #прокрутите #android-webview

Вопрос:

У меня есть приложение, которое загружает HTML-контент в три WebViews . Для простоты давайте назовем их top, middle и bottom.

Пользователь всегда просматривает середину WebView . Когда пользователь достигает верхней части страницы и проводит пальцем вниз, расположение трех веб-просмотров изменяется так, что вид сверху виден. И наоборот, когда пользователь достигает нижней части страницы и проводит пальцем вверх, отображается нижняя страница.

Представьте экран размером 100×100. Координата 0,0 — это верхний левый угол экрана, а 100,100 — нижний правый угол экрана. Вид сверху будет иметь макет с вершиной в -105, так что он недоступен для просмотра, средний вид будет занимать экран, а вид снизу будет иметь макет с вершиной в 105, так что он также недоступен для просмотра.

 topLayout.topMargin = -105;
middleLayout.topMargin = 0;
bottomLayout.topMargin = 105;

top.setLayoutParams(topLayout);
middle.setLayoutParams(middleLayout);
bottom.setLayoutParams(bottomLayout);
  

Содержимое — это книги, поэтому, когда пользователь меняет страницы, содержимое должно проходить логически. При прокрутке назад (вверх) должна отображаться нижняя часть предыдущей страницы. При прокрутке вперед (вниз) должна отображаться верхняя часть следующей страницы. Это достигается путем установки позиций прокрутки WebView с помощью scrollTo(x,y) . Код выглядит следующим образом, где a представляет нижнюю часть содержимого и b представляет верхнюю часть содержимого:

 top.scrollTo(0, a);
middle.scrollTo(0, {a,b}); // a for previous page; b for next page
bottom.scrollTo(0, b);
  

Когда пользователь переходит на предыдущую страницу, верхний WebView макет меняется, чтобы верхняя часть экрана занимала 0; середина меняется на 105, а нижняя — на -105 и загружает другой контент, поэтому приложение будет подготовлено к следующему предыдущему нажатию.


Теперь мы действительно подходим к моему вопросу. Это работает точно так, как задумано, за исключением Android 4.4 (KitKat). В KitKat он работает для двух пролистываний в любом направлении, но затем при третьем и последующем пролистывании в том же направлении содержимое загружается в неправильном положении. При прокрутке назад содержимое начинает загружаться, показывая верхнюю часть. При прокрутке вперед содержимое начинает загружаться, показывая нижнюю часть.

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

Вот где я полностью потерялся. Почему значения прокрутки устанавливаются правильно, а затем волшебным образом меняются перед отображением экрана?

Я уже пробовал использовать an onLayoutCompleteListener , это не сработало. Я буду обновлять список попыток по мере получения ответов и пробовать предложения. Заранее благодарим вас за любую помощь.


Вот краткое изложение того, что я делаю для изменения страниц:

 public class MyWebView extends WebView {

    // Assume this is instantiated; it is by the time it is needed
    private List<MyWebView> viewArray = new List<MyWebView>(3); 

    private class CustomGestureListener extends 
        GestureDetector.SimpleOnGestureListener {

        private static final String DEBUG_TAG = "Gestures";
        private int scrollYOnTouch;
        private int scrollYOnRelease;

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, 
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: "   event1.toString() 
                  event2.toString());
            Log.i(DEBUG_TAG, "onFling: vX["   velocityX 
                  "], vY["   velocityY   "]");

            scrollYOnRelease = getScrollY();
            int bottomOfPage = scrollYOnTouch   getMeasuredHeight();
            int endOfContent = (int) Math.floor(getContentHeight() * getScale());
            int proximity = endOfContent - bottomOfPage;
            boolean atBottom = proximity <= 1;
            Log.i(DEBUG_TAG, "atBottom = ("   proximity   " <= 1)");

            if ((velocityY > VELOCITY_THRESHOLD) 
                amp;amp; (scrollYOnRelease <= 0) amp;amp; (scrollYOnTouch == 0)) {
                // User flung down while at the top of the page.
                // Go to the previous page.
                changePages(PREVIOUS_PAGE);
            } else if ((velocityY < -VELOCITY_THRESHOLD) 
                amp;amp; (scrollYOnRelease >= scrollYOnTouch) amp;amp; atBottom) {
                // User flung up while at the bottom of the page.
                // Go to the next page.
                changePages(NEXT_PAGE);
            }
            return true;
        }
    } // end of CustomGestureListener

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Send the event to our gesture detector
        // If it is implemented, there will be a return value
        this.mDetector.onTouchEvent(event);
        // If the detected gesture is unimplemented, send it to the superclass
        return super.onTouchEvent(event);
    }

    @Override
    public void changePages(int direction) {

        int screenWidth = getScreenWidth();
        int screenHeight = getScreenHeight();

        VerticalPagingWebView previous;
        VerticalPagingWebView current;
        VerticalPagingWebView next;

        if (direction == NEXT_PAGE) {
            // Rearrange elements in webview array
            // Next page becomes current page,
            // current becomes previous,
            // previous becomes next.
            Collections.swap(viewArray, 0, 1);
            Collections.swap(viewArray, 1, 2);

            previous = viewArray.get(0);
            current = viewArray.get(1);
            next = viewArray.get(2);

            // Prepare the next page
            next.loadData(htmlContent, "text/html", null);

        } else if (direction == PREVIOUS_PAGE) {
            // Rearrange elements in webview array
            // Previous page becomes current page,
            // current becomes next,
            // next becomes previous.
            Collections.swap(viewArray, 1, 2);
            Collections.swap(viewArray, 0, 1);

            previous = viewArray.get(0);
            current = viewArray.get(1);
            next = viewArray.get(2);

            // Prepare the previous page
            previous.loadData(htmlContent, "text/html", null);
        }

        LayoutParams previousLayout = (LayoutParams) previous.getLayoutParams();
        previousLayout.leftMargin = LEFT_MARGIN;
        previousLayout.topMargin = -screenHeight - TOP_MARGIN;
        previous.setLayoutParams(previousLayout);

        LayoutParams currentLayout = (LayoutParams) current.getLayoutParams();
        currentLayout.leftMargin = LEFT_MARGIN;
        currentLayout.topMargin = 0;
        current.setLayoutParams(currentLayout);

        LayoutParams nextLayout = (LayoutParams) next.getLayoutParams();
        nextLayout.leftMargin = LEFT_MARGIN;
        nextLayout.topMargin = screenHeight   TOP_MARGIN;
        next.setLayoutParams(nextLayout);

        previous.scrollToBottom();
        next.scrollToTop();

        // I'm unsure if this is needed, but it works on everything but KitKat
        if (direction == NEXT_PAGE) {
            current.scrollToTop();
        } else {
            current.scrollToBottom();
        }
    } // end of changePages

    public void scrollToPageStart() {
        scrollTo(0,0);
    }

    public void scrollToPageBottom() { 
        // I know getScale() is deprecated; I take care of it.
        // This method works fine.
        int endOfContent = (int) Math.floor(getContentHeight() * getScale());
        int webViewHeight = getMeasuredHeight();
        scrollTo(0, endOfContent - webViewHeight);
    }
}
  

Ответ №1:

Вероятно, вы прокручиваете WebView до того, как он закончил загрузку содержимого. Проблема в том, что в какой-то момент во время загрузки страницы WebView сбрасывает прокрутку (это сделано намеренно, когда вы перемещаетесь между страницами, вы не хотите, чтобы смещение прокрутки сохранялось), и иногда этот сброс происходит после вашего вызова scrollTo .

Чтобы исправить это, у вас есть два варианта:

  1. прокрутка из JavaScript (в window.onload или что-то в этом роде), это гарантирует, что прокрутка произойдет после завершения загрузки содержимого WebView,
  2. дождитесь загрузки содержимого перед прокруткой. Это сложнее, поскольку WebView не имеет надежных обратных вызовов ( onPageFinished не будет работать надежно). Одним из вариантов может быть опрос, когда webview.getContentHeight() метод возвращает высоту вашего содержимого перед выполнением прокрутки.

Комментарии:

1. Это помогло мне найти некоторые другие ресурсы, но у меня все еще возникают проблемы. Содержимое загружается задолго до фактического показа его пользователю. Когда пользователь прокручивает страницы, единственный вид, загружающий содержимое, находится внутри макета, который выводит его за пределы области просмотра, следовательно, невидим для пользователя. Когда содержимое готово к показу, макет настраивается таким образом, чтобы он находился в области просмотра. В идеале операция прокрутки должна выполняться после применения макета, но до вывода представления на экран. Я добавлю фрагмент кода, чтобы он был более понятным.