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に配置するための正しい方法を提案できますか?
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()
メソッドが削除されたようです:-(
元の回答:
WebView
をScrollView
に入れることは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()
メソッドを呼び出すことができます。
使用する:
Android:descendantFocusability="blocksDescendants"
折り返しレイアウトでは、これはWebView
がその先頭にジャンプするのを防ぎます。
使用する:
webView.clearView();
mNewsContent.requestLayout();
WebView
サイズを変更してレイアウトを無効にするたびに、空のスペースが削除されます。
次に、上部に別の「タイトルバー」ビューを含む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内のテキストの選択なども機能します。
ご質問ありがとうございます、@ Dmitriy!また、答えを@svenに感謝します。
しかし、WebViewをScrollView内に配置する必要がある場合には、回避策があることを願っています。 svenが正しく気付いたように、主な問題は、スクロールビューが垂直スクロールに使用できるすべてのタッチイベントをインターセプトすることです。回避策は明らかです-スクロールビューを拡張し、ViewGroup.onInterceptTouchEvent(MotionEvent e)
メソッドをオーバーライドして、スクロールビューが子ビューに対して許容されるようにします。
もちろん、このソリューションは適切なレイアウトのカップルでのみ使用できます。主なアイデアを説明できるサンプルレイアウトを作成しました。
<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ビューに灰色の長方形のアーティファクトがあるため、私にとっては機能しません。誰かが問題とその解決方法を知っているかもしれません。
これらの答えはどれも私には役に立たないので、ここに私の解決策があります。まず、スクロールを処理できるようにするには、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);
}
});
Webviewをscrollviewに配置するのは正しくないことは知っていますが、他に選択肢はないようです。
はい、あなたがしたいのは、あなたが望むものが確実に動作しないからです。
しかし、webviewの前に同じスクロールビューにいくつかのビューを配置する必要があります。
ScrollView
を削除します。変更 Android:layout_height
のWebView
からfill_parent
。 WebView
は、他のウィジェットによって消費されないLinearLayout
の残りのスペースをすべて埋めます。