nailer
nailer

Reputation: 81

Placing HTML content into two columns inside WebView

I use WebView for displaying html content in my application for reading ebooks but developing it I was faced with the following problem.
I parse ebook and conclude parsed content in html string, after the webview loads this html-string.

  myView.setVerticalScrollBarEnabled(false);
  myView.setHorizontalScrollBarEnabled(false);
  myView.getSettings().setDisplayZoomControls(false);

  myView.getSettings().setJavaScriptEnabled(true);
  if (Build.VERSION.SDK_INT >= 11) myView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);

  myView.loadDataWithBaseURL("file:///android_asset/Text/", ebookContent, "text/html", "UTF-8", null);

I move through pages horizontally by 'myView.scrollTo(width of myView * countPages, 0)' and the horizontal placement of content is achieved by using html formatting/adding styles is proposed by Uday Sravan K. Thank to him:

  myView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        view.loadUrl(url);
        return true;
    }
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        final String varMySheet = "var mySheet = document.styleSheets[0];";
        final String varCSSIndex = "var ruleIndex = mySheet.cssRules.length;";

        final float widthPage = view.getMeasuredWidth();
        final float heightPage = view.getMeasuredHeight();
        final float density_ = getResources().getDisplayMetrics().density;
        final float gap = (int) (widthPage * .012f) * 2;

        view.loadUrl("javascript:" + varMySheet);
        view.loadUrl("javascript:" + varCSSIndex);

        if (areThereTwoColumns) {
            view.loadUrl("javascript:(function(){mySheet.insertRule('html { height: " + 
            heightPage / density_ + "px; -webkit-column-count: 2; -webkit-column-gap: " + 
            gap / density_ + "px;" +
            " font-size: " + textSize +
            "; }', ruleIndex)})()");
            } else {
            view.loadUrl("javascript:(function(){mySheet.insertRule('html { height: " +
            heightPage / density_ +
            "px; -webkit-column-gap: 0px; -webkit-column-width: " +
            widthPage / density_ + "px;" + " font-size: " + textSize +
            "; }', ruleIndex)})()");
         }
       }
    });

I split up the content inside vebview on chunks sized width of webview ('.getMeasuredWidth()'). When html-content placed in one column, it looks good into any emulators, for any page orientation. However, when I place html in two columns, the width of the resulting page can be slightly different from the width WebView. This happens when the density of device is not an integer.

Flipping pages in case two columns is 'myWebView.scrollTo((width of myWebView + gap) * countPages, 0)' and bit by bit this different accumulate while some displacement becomes shown as artifacts from one of the sides of the page. This is because the width of the displaying page is no longer coincides with the width of WebView. The edge of the neighbor page should not be visible, but it enters the displayed region.

I take, for example, Nexus 7 has 1280x800pxs, 7 inches, tvdpi (Genymotion emulator): in landscape mode my loaded in WebView web-content has length equal 360276px (returned by 'computeHorizontalScrollRange()' of WebView) and displaying layout has width equal 1238px.

note: px = (int) (dp * density + .5f)

xml-layout:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   xmlns:tools="http://schemas.android.com/tools"
   tools:context=".MainActivity"
   android:background="@color/colorBackgroundView"
   android:id="@+id/container">

   <LinearLayout 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"
       android:orientation="vertical"
       android:layout_alignParentTop="true"
       android:layout_marginLeft="16dp"
       android:layout_marginTop="10dp"
       android:layout_marginRight="16dp">

       <com.o....www.android.reader.MyWebView
           android:id="@+id/webView"
           android:layout_width="match_parent"
           android:layout_height="0dp"
           android:layout_weight="1" />

       <com.o....www.android.reader.PageCountShow
           android:id="@+id/pager"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />
   </LinearLayout>

 </RelativeLayout>

Html-content returned by 'document.body.scrollWidth' equal 270630px. Density is 1.33. In this orientation device, all of the pages are shown accurately, although the fraction 'the entire length of webview / layout width of webview' is not round.
When placed in two columns, it looks a lot worse. The WebView web-content has length equal 267453px and html-content has one equal 200904px (the proportion retained), displaying width of webview is 1238px and gap is 28px (distance of webview scrolling is 1266px).
In this case, the edge of neighbor page becomes visible, this area increases as I flip through the pages. I tried to resize html-content by reload html-content with adding in style of body-tag 'zoom' property, but it gives unexpected result: as a consequence, may change the display font size, as if all the content is scaled, and the desired page size cannot be obtained again.

Is there a way to precisely position the html-page inside webview layout? Maybe I should split the page in two columns any other way?

Upvotes: 5

Views: 1099

Answers (1)

nailer
nailer

Reputation: 81

Css Specification defines that the dimensions of the elements may be expressed in fractional numbers, but it seems that webkit requires only integer.
If WebView is scrolled at a distance defined as below, the pages look neatly.

 if (areThereTwoColumns) {
     view.loadUrl("javascript:(function(){mySheet.insertRule('html{ " +
                 "height: " + heightPage / density_ + "px;" +
                 " -webkit-column-width: "+ (widthPage - gap) / 2 / density_ + "px; " +
                 "-webkit-column-gap: " + gap / density_ + "px;" +
                 " font-size: " + textSize + ";}', ruleIndex)})()");
    // distance of scrolling:
    scrollDistance = 2 * ((int)((widthPage - gap) / 2 / density_)  * density_  +
                        (int)(gap / density_) * density_ );

 } else { ......  }


 // myView.scrollTo((int)(scrollDistance * countPages), 0) etc

I'm not sure that the found solution is the best, but it works for me. I would greatly appreciate if someone proposed something better.


it works well only for SDK 16 (tvdpi):

 areThereTwoColumns = areThereTwoColumns && getApplicationContext()
       .getResources().getConfiguration()
          .orientation == Configuration.ORIENTATION_LANDSCAPE; 
 // ............omitted
 if (Build.VERSION.SDK_INT == 16 && getResources().getDisplayMetrics()
                              .densityDpi == DisplayMetrics.DENSITY_TV)
   {
      scrollDistance = 2 * ((int)((widthPage - gap) / 2 / density_ +.5f)  * density_ +
                            (int)(gap / density_+.5f) * density_ );
   }
 // .............omitted

, In other cases it is necessary to use anything else, because, even in the case of one column, the displaying page can is distorted (tvdpi !) par example :

int countGates = Math.round((float)myView.computeHorizontalScrollRange() / (widthPage + ((areThereTwoColumns)?gap:0)));
scrollDistance = (float) lengthMyView / countGates;

Upvotes: 0

Related Questions