Intereting Posts
В управляемом и неуправляемом состоянии App Billing для продукта Получить идентификатор новой записи, вставленной в базу данных из возвращенного Uri Как извлечь атрибут name из массива строк? Изменение цвета фона активированного списка элементов на сотах Освободить память от конкретной деятельности, когда она уничтожена Цвет текста закрытого счетчика Поддерживаемые размеры веб-камеры Не может установитьInterval в onLocationChanged Формат пакета BLE на Android Не найден ресурс, который соответствует указанному имени '@android: style / Theme.Material.Light.DialogWhenLarge.NoActionBar' Подключить Eclipse к эмулятору Android на другой машине Всегда показывать службу в панели уведомлений Как разблокировать телефон Android через ADB Радиотображение Android на 90 градусов приводит к раздавленному изображению. Нужен настоящий поворот между портретом и пейзажем Хранение большого количества изображений в android

Веб-просмотр в Scrollview

Мне нужно разместить WebView в ScrollView. Но я должен поместить некоторые взгляды в тот же список прокрутки перед веб-просмотром. Так выглядит:

<ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:id="@+id/articleDetailPageContentLayout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:id="@+id/articleDetailRubricLine" android:layout_width="fill_parent" android:layout_height="3dip" android:background="@color/fashion"/> <ImageView android:id="@+id/articleDetailImageView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitStart" android:src="@drawable/article_detail_image_test"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="5dip" android:text="PUBLISH DATE"/> <WebView android:id="@+id/articleDetailContentView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@color/fashion" android:isScrollContainer="false"/> </LinearLayout> 

Я получаю некоторую информацию HTML от бэкэнд. Он не имеет никаких тегов тела или головы, просто данные, окруженные <p> или <h4> или некоторые другие теги. Также там есть теги <img> . Иногда картинки слишком широки для текущей ширины экрана. Поэтому я добавил некоторые CSS в начале HTML. Поэтому я загружаю данные в веб-просмотр следующим образом:

 private final static String WEBVIEW_MIME_TYPE = "text/html"; private final static String WEBVIEW_ENCODING = "utf-8"; String viewport = "<head><meta name=\"viewport\" content=\"target-densitydpi=device-dpi\" /></head>"; String css = "<style type=\"text/css\">" + "img {width: 100%;}" + "</style>"; articleContent.loadDataWithBaseURL("http://", viewport + css + articleDetail.getContent(), WEBVIEW_MIME_TYPE, WEBVIEW_ENCODING, "about:blank"); 

Иногда, когда страница загружается, scrollview прокручивается до места, где начинается веб-просмотр. И я не знаю, как это исправить.

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

Иногда scrollbars scrollview начинает дрожать случайно, пока я прокручиваю …

Я знаю, что неправильно размещать webview в scrollview, но похоже, что у меня нет другого выбора. Может ли кто-нибудь предложить простой способ разместить все представления и весь контент HTML в веб-обзоре?

Solutions Collecting From Web of "Веб-просмотр в Scrollview"

Обновление 2014-11-13. Поскольку Android KitKat не работает ни с одним из описанных ниже решений, вам нужно будет искать различные подходы, например , FadingActionBar от Manuel Peinado, который предоставляет прокручивающийся заголовок для WebView.

Обновление 2012-07-08: «Игры Nobu» любезно создали класс TitleBarWebView возвращает ожидаемое поведение на Android Jelly Bean. При использовании на старых платформах он будет использовать скрытый setEmbeddedTitleBar() а при использовании на Jelly Bean или выше он будет имитировать такое же поведение. Исходный код доступен под лицензией Apache 2 в коде google.

Обновление 2012-06-30: похоже, метод setEmbeddedTitleBar() был удален в Android 4.1, а также Jelly Bean 🙁

Оригинальный ответ:

Можно поместить WebView в ScrollView и он действительно работает. Я использую это в GoodNews на устройствах Android 1.6. Главный недостаток заключается в том, что пользователь не может прокручивать «диагональное» значение: если веб-контент превышает ширину экрана, ScrollView отвечает за вертикальную прокрутку в WebView для горизонтальной прокрутки. Поскольку только один из них обрабатывает события касания, вы можете либо прокручивать по горизонтали, либо по вертикали, но не по диагонали.

Кроме того, есть некоторые неприятные проблемы, описанные вами (например, пустое вертикальное пространство при загрузке содержимого меньше предыдущего). Я нашел обходные пути для всех из них в GoodNews, но теперь их не помню, потому что нашел гораздо лучшее решение:

Если вы только поместите WebView в ScrollView для размещения элементов управления над веб-контентом, и вы в порядке, чтобы поддерживать только Android 2 и выше, вы можете использовать скрытый внутренний setEmbeddedTitleBar() для WebView . Он был введен в API уровня 5 и (случайно?) Стал общедоступным для ровно одного выпуска (я думаю, это было 3.0).

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

Поскольку этот метод не экспортируется API, вам нужно использовать отражения Java для его вызова. Я предлагаю получить следующий класс:

 public final class WebViewWithTitle extends ExtendedWebView { private static final String LOG_TAG = "WebViewWithTitle"; private Method setEmbeddedTitleBarMethod = null; public WebViewWithTitle(Context context) { super(context); init(); } public WebViewWithTitle(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { try { setEmbeddedTitleBarMethod = WebView.class.getMethod("setEmbeddedTitleBar", View.class); } catch (Exception ex) { Log.e(LOG_TAG, "could not find setEmbeddedTitleBar", ex); } } public void setTitle(View view) { if (setEmbeddedTitleBarMethod != null) { try { setEmbeddedTitleBarMethod.invoke(this, view); } catch (Exception ex) { Log.e(LOG_TAG, "failed to call setEmbeddedTitleBar", ex); } } } public void setTitle(int resId) { setTitle(inflate(getContext(), resId, null)); } } 

Затем в вашем файле макета вы можете включить это, используя

 <com.mycompany.widget.WebViewWithTitle xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/content" android:layout_width="fill_parent" android:layout_height="fill_parent" /> 

И где-то еще в вашем коде вы можете вызвать метод setTitle() с идентификатором макета, который будет встроен в WebView .

использовать:

 android:descendantFocusability="blocksDescendants" 

На макете обертки это предотвратит WebView к его началу.

использовать:

 webView.clearView(); mNewsContent.requestLayout(); 

Каждый раз, когда вы изменяете размер WebView для WebView макета, это приведет к удалению пустого интервала.

Ниже приведена реализация WebView, содержащая еще один вид «Строка заголовка» поверх нее.

Как это выглядит:

Красная панель + 3 кнопки – это «Строка заголовка», ниже – веб-представление, все прокручивается и скрепляется в одном прямоугольнике.

Он чистый, короткий, работает от API 8 до 16 и выше (с небольшими усилиями он может работать и на API <8). Он не использует никаких скрытых функций, таких как WebView.setEmbeddedTitleBar.

 public class TitleWebView extends WebView{ public TitleWebView(Context context, AttributeSet attrs){ super(context, attrs); } private int titleHeight; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); // determine height of title bar View title = getChildAt(0); titleHeight = title==null ? 0 : title.getMeasuredHeight(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev){ return true; // don't pass our touch events to children (title bar), we send these in dispatchTouchEvent } private boolean touchInTitleBar; @Override public boolean dispatchTouchEvent(MotionEvent me){ boolean wasInTitle = false; switch(me.getActionMasked()){ case MotionEvent.ACTION_DOWN: touchInTitleBar = (me.getY() <= visibleTitleHeight()); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: wasInTitle = touchInTitleBar; touchInTitleBar = false; break; } if(touchInTitleBar || wasInTitle) { View title = getChildAt(0); if(title!=null) { // this touch belongs to title bar, dispatch it here me.offsetLocation(0, getScrollY()); return title.dispatchTouchEvent(me); } } // this is our touch, offset and process me.offsetLocation(0, -titleHeight); return super.dispatchTouchEvent(me); } /** * @return visible height of title (may return negative values) */ private int visibleTitleHeight(){ return titleHeight-getScrollY(); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt){ super.onScrollChanged(l, t, oldl, oldt); View title = getChildAt(0); if(title!=null) // undo horizontal scroll, so that title scrolls only vertically title.offsetLeftAndRight(l - title.getLeft()); } @Override protected void onDraw(Canvas c){ c.save(); int tH = visibleTitleHeight(); if(tH>0) { // clip so that it doesn't clear background under title bar int sx = getScrollX(), sy = getScrollY(); c.clipRect(sx, sy+tH, sx+getWidth(), sy+getHeight()); } c.translate(0, titleHeight); super.onDraw(c); c.restore(); } } 

Использование: поместите иерархию представления заголовка внутри элемента <WebView> в макет xml. WebView наследует ViewGroup, поэтому он может содержать детей, несмотря на то, что плагин ADT жалуется, что он не может. Пример:

 <com.test.TitleWebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" android:layerType="software" > <LinearLayout android:id="@+id/title_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#400" > <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> <Button android:id="@+id/button5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> </com.test.TitleWebView> 

Обратите внимание, что использование layerType = «программное обеспечение», WebView в аппаратных средствах API 11+ неправильно анимируется и рисуется, когда у него есть hw-слой.

Прокрутка работает отлично, а также клики по строке заголовка, клики по сети, выбор текста в Интернете и т. Д.

Спасибо за ваш вопрос @Dmitriy! И также большое спасибо @sven за ответ.

Но я надеюсь, что есть обходной путь для случаев, когда нам нужно поместить WebView внутри ScrollView. Как правильно заметил sven, основная проблема заключается в том, что просмотр прокрутки перехватывает все сенсорные события, которые можно использовать для вертикальной прокрутки. Таким образом, обходной путь очевидным – расширение прокрутки и переопределение ViewGroup.onInterceptTouchEvent(MotionEvent e) таким образом, просмотр прокрутки становится толерантным к его дочерним представлениям:

  1. Обрабатывать все события касания, которые можно использовать для вертикальной прокрутки.
  2. Отправьте их всем взглядам ребенка.
  3. Перехватывать события только с вертикальной прокруткой (dx = 0, dy> 0)

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

  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.rus1f1kat0r.view.TolerantScrollView android:id="@+id/scrollView1" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_centerVertical="true" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" /> <WebView android:id="@+id/webView1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView android:id="@+id/textView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" /> </LinearLayout> </com.rus1f1kat0r.view.TolerantScrollView> </RelativeLayout> 

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

Более того, я рассматривал решение с доступом к закрытому API через размышления, и я думаю, что это довольно хреново. Это также не работает для меня из-за серых прямоугольных артефактов над веб-браузером на некоторых устройствах HTC с Android ICS. Может быть, кто-то знает, в чем проблема и как мы можем это решить?

Ни один из этих ответов не работает для меня, так что вот мое решение. Прежде всего, нам нужно переопределить WebView чтобы иметь возможность обрабатывать прокрутку. Вы можете найти, как это сделать здесь: https://stackoverflow.com/a/14753235/1285670

Затем создайте свой xml с двумя представлениями, где находится WebView а другой – ваш макет заголовка.

Это мой xml-код:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.project.ObservableWebView android:id="@+id/post_web_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" /> <LinearLayout android:id="@+id/post_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" android:orientation="vertical" android:paddingLeft="@dimen/common_ui_space" android:paddingRight="@dimen/common_ui_space" android:paddingTop="@dimen/common_ui_space" > ////some stuff </LinearLayout> </FrameLayout> 

Затем обработайте прокрутку WebView и переместите заголовок, подходящий для прокрутки. Здесь вы должны использовать NineOldAndroids .

 @Override public void onScroll(int l, int t, int oldl, int oldt) { if (t < mTitleLayout.getHeight()) { ViewHelper.setTranslationY(mTitleLayout, -t); } else if (oldt < mTitleLayout.getHeight()) { ViewHelper.setTranslationY(mTitleLayout, -mTitleLayout.getHeight()); } } 

И вы должны добавить дополнение к вашему содержимому WebView , чтобы он не накладывал заголовок:

 v.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @SuppressLint("NewApi") @Override public void onGlobalLayout() { if (mTitleLayout.getHeight() != 0) { if (Build.VERSION.SDK_INT >= 16) v.getViewTreeObserver().removeOnGlobalLayoutListener(this); else v.getViewTreeObserver().removeGlobalOnLayoutListener(this); } mWebView.loadDataWithBaseURL(null, "<div style=\"padding-top: " + mTitleLayout.getHeight() / dp + "px\">" + mPost.getContent() + "</div>", "text/html", "UTF8", null); } }); 

Я знаю, что неправильно размещать webview в scrollview, но похоже, что у меня нет другого выбора.

Да, потому что то, что вы хотите, не будет работать надежно.

Но я должен поместить некоторые взгляды в тот же список прокрутки перед веб-просмотром.

Удалите ScrollView . Измените android:layout_height вашего WebView на fill_parent . WebView заполнит все оставшееся пространство в LinearLayout не будет потреблено другими виджетами.