web-dev-qa-db-ja.com

ScrollviewのWebview

WebViewをScrollViewに配置する必要があります。しかし、webviewの前に同じスクロールビューにいくつかのビューを配置する必要があります。そのため、次のようになります。

<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コンテンツの後に巨大な白い空きスペースが表示される場合があります。私もそれをどうするかわかりません。

Scrollviewのスクロールバーは、スクロール中にランダムにTwitchを開始することがあります...

Webviewをscrollviewに配置するのは正しくないことは知っていますが、他に選択肢はないようです。誰もがすべてのビューとすべてのHTMLコンテンツをWebViewに配置するための正しい方法を提案できますか?

29
Dmitriy

2014-11-13の更新: Android KitKat以降、以下で説明するソリューションはいずれも機能しません。たとえば、さまざまなアプローチを探す必要があります。 Manuel PeinadoのFadingActionBar は、WebViewのスクロールヘッダーを提供します。

2012-07-08の更新:「Nobu games」が TitleBarWebView クラスを親切に作成し、Android Jelly Beanに期待される動作を戻しました。古いプラットフォームで使用される場合、隠されたsetEmbeddedTitleBar()メソッドを使用し、Jelly Bean以上で使用される場合、同じ動作を模倣します。ソースコードは、Apache 2ライセンスの下で利用可能です at google code

2012-06-30の更新: Android 4.1別名Jelly beanでsetEmbeddedTitleBar()メソッドが削除されたようです:-(

元の回答:

WebViewScrollViewに入れることはis可能であり、機能します。私はこれをAndroid 1.6デバイスの GoodNews で使用しています。主な欠点は、ユーザーが「対角線」の意味でスクロールできないことです。Webコンテンツが画面の幅を超える場合、ScrollViewは水平スクロールのWebViewで垂直スクロールを行います。そのうちの1つだけがタッチイベントを処理するため、水平方向にスクロールすることができますor.

さらに、あなたが説明したように、いくつかの迷惑な問題があります(たとえば、前のコンテンツよりも小さいコンテンツをロードするときの空の垂直スペース)。 GoodNewsでそれらすべての回避策を見つけましたが、はるかに優れた解決策を見つけたので、今はそれらを思い出せません。

WebViewのみをScrollViewに入れて、コントロールaboveをWebコンテンツに配置し、Android 2以降のみをサポートする場合は、 WebViewの隠し内部setEmbeddedTitleBar()メソッドを使用できます。 APIレベル5で導入され、(偶然?)ちょうど1つのリリースで公開されました(3.0だったと思います)。

このメソッドを使用すると、Webコンテンツの上に配置されるWebViewにレイアウトを埋め込むことができます。このレイアウトは、垂直にスクロールすると画面をスクロールアウトしますが、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"
        />
_

コード内のどこかで、WebViewに埋め込むレイアウトのIDを使用してsetTitle()メソッドを呼び出すことができます。

16
sven

使用する:

Android:descendantFocusability="blocksDescendants"

折り返しレイアウトでは、これはWebViewがその先頭にジャンプするのを防ぎます。

使用する:

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

WebViewサイズを変更してレイアウトを無効にするたびに、空のスペースが削除されます。

18
Evgeni Roitburg

次に、上部に別の「タイトルバー」ビューを含むWebViewの実装を示します。

見た目:

赤いバーと3つのボタンは「タイトルバー」で、下はWebビューです。すべてスクロールして1つの長方形にクリップされます。

それはきれいで、短く、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();
   }
}

使用法:レイアウトxmlの<WebView>要素内にタイトルバービュー階層を配置します。 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"の使用に注意してください。API11+のハードウェアでのWebViewは、hwレイヤーがある場合、適切にアニメーション化および描画されません。

スクロールは完璧に機能し、タイトルバーのクリック、Webのクリック、Web内のテキストの選択なども機能します。

9
Pointer Null

ご質問ありがとうございます、@ 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>

主なアイデア自体は、すべてのビュー(ヘッダー、Webビュー、フッター)を垂直線形レイアウト内に配置することで垂直スクロールの競合を回避し、水平および斜めスクロールを可能にするためにタッチイベントを正しくディスパッチすることです。役に立つかどうかはわかりませんが、TolerantScrollViewとレイアウトサンプルを使用してサンプルプロジェクトを作成しました。ダウンロードできるのは here です。

さらに、リフレクションを介して閉じたAPIにアクセスすることで解決策を確認しましたが、それはかなり気難しいと思います。また、Android ICS。一部のHTCデバイスのWebビューに灰色の長方形のアーティファクトがあるため、私にとっては機能しません。誰かが問題とその解決方法を知っているかもしれません。

5
rus1f1kat0R

これらの答えはどれも私には役に立たないので、ここに私の解決策があります。まず、スクロールを処理できるようにするには、WebViewをオーバーライドする必要があります。ここでそれを行う方法を見つけることができます: https://stackoverflow.com/a/14753235/128567

次に、2つのビューでxmlを作成します。1つはWebViewで、もう1つはタイトルレイアウトです。

これは私の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 scrollで、スクロール値に適したタイトルを移動します。ここでは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);
        }
});
2
rstk

Webviewをscrollviewに配置するのは正しくないことは知っていますが、他に選択肢はないようです。

はい、あなたがしたいのは、あなたが望むものが確実に動作しないからです。

しかし、webviewの前に同じスクロールビューにいくつかのビューを配置する必要があります。

ScrollViewを削除します。変更 Android:layout_heightWebViewからfill_parentWebViewは、他のウィジェットによって消費されないLinearLayoutの残りのスペースをすべて埋めます。

0
CommonsWare