Webview在滚动查看

我必须将WebView放入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信息。 它没有任何body或head标签,只是由<p><h4>或其他标签包围的数据。 还有在那里<img>标签。 有时图片对于当前屏幕宽度来说太宽。 所以我在HTML的开头添加了一些CSS。 所以我加载数据到webview像这样:

 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会滚动到webview开始的地方。 我不知道如何解决这个问题。

另外,webview内容之后有时会出现巨大的白色空白区域。 我也不知道该怎么做。

有时scrollview的滚动条开始随机抽动,而我滚动…

我知道把webview放到滚动视图是不正确的,但似乎我没有其他的select。 任何人都可以build议将所有视图和所有HTML内容放置到webview的方法吗?

Solutions Collecting From Web of "Webview在滚动查看"

更新2014年11月13日:由于Android KitKat下面描述的解决scheme都没有工作 – 您将需要寻找不同的方法,如Manuel Peinado的FadingActionBar ,它提供了一个WebViews的滚动标题。

更新2012-07-08: “Nobu游戏”好心地创build了一个TitleBarWebView类,将预期的行为带回到Android Jelly Bean。 在较旧的平台上使用时,将使用隐藏的setEmbeddedTitleBar()方法,在Jelly Bean或更高版本上使用时,它将模仿相同的行为。 源代码可以在Google代码的Apache 2许可下获得

更新2012年6月30日:它似乎setEmbeddedTitleBar()方法已被删除在Android 4.1又名果冻豆:-(

原始答案:

可以将一个WebView放入一个ScrollView并且它可以工作。 我在Android 1.6设备上使用GoodNews 。 主要缺点是用户不能滚动“对angular线”的含义:如果网页内容超过了屏幕的宽度, ScrollView器负责在WebView进行水平滚动的垂直滚动。 由于只有其中一个处理触摸事件,您可以水平垂直滚动,但不是对angular线滚动。

进一步说,有一些恼人的问题,如你所描述的(例如,当加载一个小于前一个的内容时,空的垂直空间)。 我在GoodNews中find了所有这些解决方法,但是现在不记得它们了,因为我find了一个更好的解决scheme:

如果您只将WebView放入ScrollView ,将控件放置在Web内容上方,并且您可以仅支持Android 2及更高版本,则可以使用WebView的隐藏内部setEmbeddedTitleBar()方法。 它已经在API级别5中引入,并且(意外地?)正式公开发布(我认为它是3.0)。

此方法允许您将布局embeddedWebView ,将放置在Web内容上方。 这种布局将在垂直滚动时滚动屏幕,但当网页内容水平滚动时,将保持在相同的水平位置。

由于此方法不是由API导出的,因此您需要使用Javareflection来调用它。 我build议派生一个新的class级:

 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()方法,将布局的IDembedded到WebView

使用:

 android:descendantFocusability="blocksDescendants" 

在包装布局上,这将防止WebView跳到它的开始。

使用:

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

每次更改WebView大小以使布局无效时,这将删除空白间距。

这里是包含另一个“标题栏”视图顶部的WebView的实现。

它看起来如何:

红色栏+ 3个button是一个“标题栏”,下面是网页视图,所有的滚动和裁剪在一个矩形。

它干净,简洁,可以从API 8到16以上的所有方式(只需很小的努力,它也可以在API <8)上工作。 它不使用任何隐藏的function,如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(); } } 

用法:将标题栏视图层次结构放置在布局xml中的<WebView>元素内部。 WebViewinheritance了ViewGroup,所以它可以包含子节点,尽pipeADT插件抱怨它不能。 例:

 <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 =“software”的用法,API 11+上的硬件上的WebView在hw图层时没有正确的animation和绘制。

滚动完美,以及点击标题栏,点击网页,select网页中的文字等。

感谢您的问题,@Dmitriy! 还要感谢@sven的答案。

但我希望有一个解决scheme,我们需要将WebView放入ScrollView。 正如sven正确地注意到的主要问题是滚动视图截取可以用于垂直滚动的所有触摸事件。 所以解决方法是显而易见的 – 扩展滚动视图和重写ViewGroup.onInterceptTouchEvent(MotionEvent e)方法的滚动视图变得容忍它的子视图:

  1. 处理可用于垂直滚动的所有触摸事件。
  2. 将它们全部发送给子视图。
  3. 仅用垂直滚动拦截事件(dx = 0,dy> 0)

当然,这个解决scheme只能在适当布局的情况下使用。 我做了可以说明主要思想的样本布局。

  <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> 

所以主要想法本身是通过将所有视图(标题,web视图,页脚)放入垂直线性布局中,避免垂直滚动冲突,并正确地分配触摸事件以允许水平和对angular滚动。 我不确定它是否有用,但我已经创build了TolerantScrollView和布局示例的示例项目,您可以在这里下载它。

更重要的是 – 我已经通过reflection查看了解决scheme,通过访问封闭的API,我猜这是相当困难的。 它也不适合我,因为Android ICS的一些HTC设备上的webview上的灰色矩形工件。 也许有人知道什么是问题,我们如何解决它?

这个答案都不适用于我,所以这里是我的解决scheme。 首先我们需要重写WebView来处理它的滚动。 你可以find如何做到这一点: https : //stackoverflow.com/a/14753235/1285670

然后用两个视图创build你的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放到滚动视图是不正确的,但似乎我没有其他的select。

是的,因为你想要的东西不能可靠地工作。

但是我必须在webview之前将一些视图放到相同的scrollview中。

删除ScrollView 。 将WebViewandroid:layout_height更改为fill_parentWebView将填充LinearLayout所有未被其他小部件占用的剩余空间。