Scrollview中的Webview

时间:2012-03-15 10:54:34

标签: android webview scrollview

我必须将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信息。它没有任何正文或标题,只有<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内容后会出现巨大的空白空间。我也不知道该怎么做。

滚动时,滚动条的滚动条有时会​​随机抽搐......

我知道将webview放入scrollview是不对的,但似乎我没有别的选择。有人能建议将所有视图和所有HTML内容放到webview上吗?

6 个答案:

答案 0 :(得分:18)

使用:

android:descendantFocusability="blocksDescendants"

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

使用:

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

每次更改WebView尺寸以使布局无效时,都会删除空间距。

答案 1 :(得分:16)

更新2014-11-13:由于Android KitKat下面描述的解决方案都不起作用 - 您需要寻找不同的方法,例如: Manuel Peinado's FadingActionBar,为WebViews提供滚动标题。

更新2012-07-08:“Nobu游戏”亲切地创建了一个TitleBarWebView类,将预期的行为带回Android Jelly Bean。在旧平台上使用时,它将使用隐藏的setEmbeddedTitleBar()方法,当在Jelly Bean或更高版本上使用时,它将模仿相同的行为。源代码在Apache 2许可证at google code

下可用

更新2012-06-30:似乎在Android 4.1又名Jelly Bean中删除了setEmbeddedTitleBar()方法: - (

原始回答:

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

此外,您所描述的还有一些烦人的问题(例如,当加载的内容小于前一个时,空的垂直空间)。我在GoodNews中找到了所有这些的解决方法,但现在不记得了,因为我找到了一个更好的解决方案:

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

此方法允许您将布局嵌入到WebView中,该布局将放置在Web内容上方。当垂直滚动时,此布局将滚出屏幕,但当Web内容水平滚动时,将保持在相同的水平位置。

由于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()的布局ID调用WebView方法。

答案 2 :(得分:9)

以下是WebView的实现,其中包含另一个“标题栏”视图。

外观如何:

红色栏+ 3个按钮是“标题栏”,下面是网页视图,所有都滚动并剪切在一个矩形中。

干净,简洁,从API 8到16及以上都可以使用(只需很少的努力,它也可以在API&lt; 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();
   }
}

用法:将标题栏视图层次结构放在&lt; WebView&gt;中布局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 =“software”,API 11+中的硬件中的WebView在具有hw图层时没有正确设置动画和绘制。

滚动效果很好,点击标题栏,点击网页,在网页上选择文字等等。

答案 3 :(得分:5)

感谢您的提问,@ Dmitriy! 并且非常感谢@sven的答案。

但我希望有一个解决方法,我们需要将WebView放在ScrollView中。正如sven正确注意到的主要问题是滚动视图拦截了可用于垂直滚动的所有触摸事件。所以解决方法很明显 - 扩展滚动视图并覆盖ViewGroup.onInterceptTouchEvent(MotionEvent e)方法,滚动视图可以容忍它的子视图:

  1. 处理可用于垂直滚动的所有触摸事件。
  2. 将所有这些内容发送到子视图。
  3. 仅拦截垂直滚动事件(dx = 0,dy> 0)
  4. 当然,此解决方案只能与适当的布局配合使用。 我已经制作了可以说明主要想法的样本布局。

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

    因此,主要思想本身是通过将所有视图(标题,webview,页脚)放在垂直线性布局中,并正确调度触摸事件以允许水平和对角滚动来避免垂直滚动冲突。 我不确定它是否有用,但我已经使用TolerantScrollView和布局示例创建了示例项目,您可以下载它here

    更重要的是 - 我通过反思访问了封闭的API,看到了解决方案,我觉得这很糟糕。由于在带有Android ICS的某些HTC设备上的webview上的灰色矩形工件,它也不适用于我。也许有人知道问题是什么以及我们如何解决它?

答案 4 :(得分:2)

这些答案都不适合我,所以这是我的解决方案。首先,我们需要覆盖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);
        }
});

答案 5 :(得分:0)

  

我知道将webview放入scrollview是不对的,但似乎我没有别的选择。

是的,你这样做,因为你想要的东西不会可靠地运作。

  

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

删除ScrollView。将android:layout_height的{​​{1}}更改为WebViewfill_parent将填充其他小部件未使用的WebView中的所有剩余空间。