24

In a layout (linear, vertical) hierarchy I've got several views and one of them is a WebView. They all have same parameters:

android:layout_width="fill_parent"
android:layout_height="wrap_content"

For all views, the spacing between elements is correctly handled but for the Webview there is a lot of spaces after the displayed text inside.

I set the (HTML) text of the webview in the "onCreate" method of the activity using the layout.

Is there a way to let the webview resize itself, to match its content size?

By the way, the space left vary from one device to another (emulator vs Milestone vs Hero)

Any idea? Thanks

Puja
  • 192
  • 9

15 Answers15

27

I am sure this is too late but adding the method which worked for me in case somebody else comes here looking for solution.

Once the page finishes loading, I am injecting a javascript method to callback me JS hook. And in this method I am passing the size of .

    private void setupWebView() {
    webView.getSettings().setJavaScriptEnabled(true);
    webView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            webView.loadUrl("javascript:MyApp.resize(document.body.getBoundingClientRect().height)");
            super.onPageFinished(view, url);
        }
    });
    webView.addJavascriptInterface(this, "MyApp");
}
@JavascriptInterface
public void resize(final float height) {
    MyActivity.this.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            webView.setLayoutParams(new LinearLayout.LayoutParams(getResources().getDisplayMetrics().widthPixels, (int) (height * getResources().getDisplayMetrics().density)));
        }
    });
}

The same solution can also be seen here.

Chitranshu Asthana
  • 1,089
  • 8
  • 19
  • 3
    It seems that if your content height is bigger than max visible `WebView` height and you will try to set this bigger height value to `LayoutParams` - your `WebView` will dissappear at all. – Sergii Feb 16 '15 at 13:09
7

It seems that at the moment the Webview only resizes itself if the content is too big. If the webview is bigger then the content it doesn't shrinks to reclaim the space.

Janusz
  • 187,060
  • 113
  • 301
  • 369
6

I had the same problem. I solved by changing the visibility of the WebView.

mWebView.setVisibility(View.GONE);
mWebView.loadData(".....");
mWebView.reload();
mWebView.setVisibility(View.VISIBLE);
Fernwilter
  • 134
  • 1
  • 5
  • 1
    This works for me so far. A little modification I did was to put the showing of visibility into the onPageFinished. – Kalel Wade Apr 01 '14 at 15:17
5

After some hours of searching and trying I found the simplest working solution. Just call this as "repaint":

webView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

For my webview it works perfectly inside a scrollView. If you do not want to use WRAP_CONTENT, you can use your own params.

eJal
  • 51
  • 1
  • 2
  • This was the only solution here that worked with my WebView in a list item. The list item expanded as needed, but never shrank back down. It makes sense though since I was using MATCH_PARENT. After changing the layout param to WRAP_CONTENT, I didn't even have to call invalidate() or requestLayout(). – RickBoyerDev Jun 30 '17 at 16:18
4

I only put the webview inside the ScrollView and set layout_height=wrap_content and it worked.

<ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/web_scrollview"
        android:layout_below="@id/question_title_section"
        android:layout_marginBottom="60dp"
        android:scrollbars="vertical">
        <WebView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/question_content"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:padding="4dp">
        </WebView>
    </ScrollView>

And in my Activity

wvQuestion = (WebView) findViewById(R.id.question_content);
wvQuestion.loadUrl(url);
VuVanLy
  • 69
  • 6
1

You can periodically update WebView height according to javascript body height, like this:

public class WrapContentWebView extends WebView {

    private static final long UPDATE_INTERVAL = 100; // ms

    public WrapContentWebView(Context context) {
        super(context);
        init();
    }

    public WrapContentWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public WrapContentWebView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        addJavascriptInterface(new JavascriptInterface(), "java");
        postDelayed(scheduleRunnable, UPDATE_INTERVAL);
    }

    private Runnable scheduleRunnable = new Runnable() {
        @Override
        public void run() {
            loadUrl("javascript:window.java.onNewHeight(document.body.offsetHeight);");
            postDelayed(scheduleRunnable, UPDATE_INTERVAL);
        }
    };

    private class JavascriptInterface {
        @android.webkit.JavascriptInterface
        public void onNewHeight(String bodyOffsetHeight) {
            if (bodyOffsetHeight == null) {
                return;
            }

            final int newHeight =
                    (int) (Integer.parseInt(bodyOffsetHeight) *
                            getScaleY() *
                            getResources().getDisplayMetrics().density);

            if (getLayoutParams().height != newHeight) {
                ((Activity) getContext()).runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        getLayoutParams().height = newHeight;
                        requestLayout();
                    }
                });
            }
        }
    }
}

layout.xml:

<WrapContentWebView
            android:layout_width="match_parent"
            android:layout_height="0dp">
</WrapContentWebView>

P.S. Checked on Android 5, 4.1, 4.04

caiiiycuk
  • 1,466
  • 14
  • 20
  • Instead of WebView use WrapContentWebView in your layout.xml – caiiiycuk May 24 '16 at 13:37
  • just this?? seriously...i mean i wonder is this simple tag change will solve the problem?? – Gaurav Aggarwal May 25 '16 at 05:52
  • As you can see this tag was supplied with implementation that catch document height changes on javascript side and then update height of webview to match javascript content height. – caiiiycuk May 25 '16 at 21:19
  • Awesome solution, I got a super cranky HTML to show so I had to combine it with https://stackoverflow.com/a/1147768/2413303 and even check `document.body.firstElementChild.offsetHeight` – EpicPandaForce Jun 04 '20 at 09:56
1

I found:

(1) there are two height in webview. MeasureHeight and contentHeight. Note that the contentHeight is computed from the webcoreThread. The two height are not consistent any time!

(2) when layout finish.the webview reload content. The contentHeight is consistent with measureHeight.

So I think one solution is: make webview reload content after the layout finished.

My solution:

(1)extends webview . (2)override onlayout(),dispatchDraw(),

@Override
protected void dispatchDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.dispatchDraw(canvas);

    //mchanged  == is parameter in onlayout()
            //finished  == have run the code below !
    if (!mchanged && !finished) {
//                          
        String vHtml =".........." ;//your html content
        this.loadDataWithBaseURL("目录浏览", vHtml, "text/html", "utf-8",                             null);
            finished = true;
    }
}


     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // TODO Auto-generated method stub
    super.onLayout(changed, l, t, r, b);
    // tttttt
    if (!changed) { 
        this.mchanged = changed;//false
             }
        }

(3) webview need init before load every content.

public void intiFlag() {
    mchanged = true;
        finished = false ;
}

Hope this helps.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
0

I also had the problem of the WebView not sizing to fit after adjusting text size via javascript. I solved this by setting the visibility of the WebView to View.GONE until onPageFinished(). After the page loads and I tweak font size etc via javascript, setVisibility(View.VISIBLE) and the WebView sizes to fit perfectly.

webView.setVisibility(View.GONE);
webView.setWebViewClient(new WebViewClient() {  
    @Override  
        public void onPageFinished(WebView view, String url)  
        {  
         int jsSize = (int)fBioTextSize; //a double set in the owning class...
         String sizeTweak = "" ;
         if (jsSize > 0.00)
         {
             sizeTweak = 
                "document.getElementsByTagName('body')[0].style.fontSize = '" + jsSize + "px'; ";
         }
            view.loadUrl("javascript:(function() { " +  
                    "document.getElementsByTagName('body')[0].style.color = '#9e9e9e'; " +
                    "document.getElementsByTagName('body')[0].style.background = 'black'; " +
                    sizeTweak +
                    "})()");  
           view.setVisibility(View.VISIBLE); 

        }  
    });  
Johnny O
  • 587
  • 4
  • 4
0

If you simply remove the view and then add a new one back, problem solved.

Here is an example:

WebView current_view = (WebView) view.getChildAt(i);
CharSequence current_text = current_view.getText();
int index = parent.indexOfChild(current_view);
parent.removeView(current_view);
current_view = new WebView(context);
parent.addView(current_view, index, layoutParams);
current_view.loadDataWithBaseURL( ...however you want... );
view.invalidate();

In summary... it just adds a new WebView just like the old right back to its parent at the same index.

Codeversed
  • 9,287
  • 3
  • 43
  • 42
0

I had similar problem with FrameLayout containing TextView, ProgressBar and WebView. Depends on the return object from server I was hiding views and sometimes when I had to show web page and hide TextView and ProgressBar in onPageFinished() WebView had a hight of ProgressBar. Manage to fix it by this piece of code:

public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            progressBar.setVisibility(View.GONE)
        }
    });
}
Mateusz Jablonski
  • 1,039
  • 9
  • 11
0

Agree with @Fernwilter. But changing the sequence worked for me.

mWebView.setVisibility(View.GONE);
mWebView.reload();
mWebView.setVisibility(View.VISIBLE);
mWebView.loadData(".....");
Bera
  • 145
  • 1
  • 2
  • 11
0

It worked for me.

ViewTreeObserver viewTreeObserver = dataWebView.getViewTreeObserver();
        viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (dataWebView.getMeasuredHeight() > 0) {
                    dataWebView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    ((Activity) getContext()).runOnUiThread(() -> {
                        dataWebView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0));
                        dataWebView.setVisibility(View.GONE);
                        dataWebView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                        dataWebView.setVisibility(View.VISIBLE);

                    });
                }
            }
        });
Tuan Nguyen
  • 2,542
  • 19
  • 29
0

After trying several ways, finally this worked for me: +++first use useState:

const [height, setHeight] = useState(0)

+++declare:

    const webViewScript = `
       setTimeout(function() { window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight);}, 500);
    true;
  `

+++Next we wrap a View into the webview:

<View style={{ width: "100%", height: height }}>
      <WebView
        originWhitelist={["*"]}
        source={{ html: html }}
        onMessage={(event) => {
          setHeight(parseInt(event.nativeEvent.data))
        }}
        javaScriptEnabled={true}
        injectedJavaScript={webViewScript}/>
</View>
Nam_Lifebuoy
  • 11
  • 1
  • 3
-1

An easier solution is to invalidate the webview once loading has finished:

  webView.setWebViewClient(new WebViewClient() {

            @Override
            public void onPageFinished(final WebView view, final String url) {
                super.onPageFinished(view, url);
                webView.invalidate();
            }
        });
-1

A little bit complicated but working ...

private ViewGroup mScrollView //linearlayout encapsuled in a scrollView;
private WebView mWebView;

private Handler mHandler = new Handler(){      
  public void handleMessage(Message msg){
    mWebView.reload();
    mScrollView.addView(mWebView, 0);
  }
};

private updateWebView(){
  //update the content of your web view
  mScrollView.removeView(mWebView);
  mWebView.clearView();
  mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 200);
}
matheeeny
  • 1,714
  • 2
  • 17
  • 32