如何在 Android 中改变 Webview 的字体面貌?

我想改变默认字体的 webview 为自定义字体。我正在使用 webview 为 Android 开发一个双语浏览器应用程序。

我尝试通过在资产中放置自定义字体来获得自定义字体的实例。但仍然无法将 webview 的默认字体设置为我的字体。

这就是我试过的:

Typeface font = Typeface.createFromAsset(getAssets(), "myfont.ttf");
private WebView webview;
WebSettings webSettings = webView.getSettings();
webSettings.setFixedFontFamily(font);

有没有人可以纠正这个问题,或者建议其他方法把 webview 的默认字体改成自定义字体?

谢谢!

112084 次浏览

You can do it by using CSS. I did it with an app but it won't work in Android 2.1 as there is an known bug with Android browser 2.1.

It can be done in Android. I took three days to solve this issue. But now it seems very easy. Follow these steps to set custom font for Webview

1.Add your font to assets folder
2.Copy the font to application's files directory

private boolean copyFile(Context context,String fileName) {
boolean status = false;
try {
FileOutputStream out = context.openFileOutput(fileName, Context.MODE_PRIVATE);
InputStream in = context.getAssets().open(fileName);
// Transfer bytes from the input file to the output file
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// Close the streams
out.close();
in.close();
status = true;
} catch (Exception e) {
System.out.println("Exception in copyFile:: "+e.getMessage());
status = false;
}
System.out.println("copyFile Status:: "+status);
return status;
}

3.You have to call above function only once (you have to find some logic for this).

copyFile(getContext(), "myfont.ttf");

4.Use the below code to set value for your webview. Here I am using CSS to set font.

private String getHtmlData(Context context, String data){
String head = "<head><style>@font-face {font-family: 'verdana';src: url('file://"+ context.getFilesDir().getAbsolutePath()+ "/verdana.ttf');}body {font-family: 'verdana';}</style></head>";
String htmlData= "<html>"+head+"<body>"+data+"</body></html>" ;
return htmlData;
}

5.You can call the above function as below

webview.loadDataWithBaseURL(null, getHtmlData(activity,htmlData) , "text/html", "utf-8", "about:blank");

Even simpler, you can use the Asset URL format to reference the font. No programming needed!

@font-face {
font-family: 'myface';
src: url('file:///android_asset/fonts/myfont.ttf');
}


body {
font-family: 'myface', serif;

...

There's a working example of this in this project. It boils down to:

  1. In your assets/fonts folder, place the desired OTF or TTF font (here MyFont.otf)
  2. Create a HTML file that you'll use for the WebView's content, inside the assets folder (here inside assets/demo/my_page.html):

    <html>
    <head>
    <style type="text/css">
    @font-face {
    font-family: MyFont;
    src: url("file:///android_asset/fonts/MyFont.otf")
    }
    body {
    font-family: MyFont;
    font-size: medium;
    text-align: justify;
    }
    </style>
    </head>
    <body>
    Your text can go here! Your text can go here! Your text can go here!
    </body>
    </html>
    
  3. Load the HTML into the WebView from code:

    webview.loadUrl("file:///android_asset/demo/my_page.html");
    

Take note that injecting the HTML through loadData() is not permitted. As per the documentation:

Note that JavaScript's same origin policy means that script running in a page loaded using this method will be unable to access content loaded using any scheme other than 'data', including 'http(s)'. To avoid this restriction, use loadDataWithBaseURL() with an appropriate base URL.

As @JaakL suggests in the comment below, for loading HTML from a string, you should instead provide the base URL pointing to your assets:

webView.loadDataWithBaseURL("file:///android_asset/", htmlData);

When referencing the font in htmlData, you may then simply use /fonts/MyFont.otf (omitting the base URL).

This is how do you load htmlData in a webview:

webview.loadDataWithBaseURL(null, getHtmlData(activity,**htmlData**) , "text/html", "utf-8", "about:blank");

where getHtmlData(activity,**htmlData**) returns a string of html code.

The issue is it needs to be in a folder, try putting "./myfont.ttf" instead, if not put the font inside a folder in assets like "fonts/myfont.ttf" that will work for sure.

I am using this code :

wv = (WebView) findViewById(R.id.webView1);
String pish = "<html><head><style type=\"text/css\">@font-face {font-family: MyFont;src: url(\"file:///android_asset/font/BMitra.ttf\")}body {font-family: MyFont;font-size: medium;text-align: justify;}</style></head><body>";
String pas = "</body></html>";
String myHtmlString = pish + YourTxext + pas;
wv.loadDataWithBaseURL(null,myHtmlString, "text/html", "UTF-8", null);

Many of the above answers work lie a charm if you have your content in your assets folder.

However, if you like me want to download and save all your assets from a server to your internal storage you can instead use loadDataWithBaseURL and use a reference to your internal storage as the baseUrl. Then all files there will be relative to the loaded html and are found and used correctly.

In my internal storage I have saved the following files:

  • IndigoAntiqua2Text-Regular.ttf
  • style.css
  • image.png

Then I use the following code:

WebView web = (WebView)findViewById(R.id.webby);
//For avoiding a weird error message
web.setLayerType(View.LAYER_TYPE_SOFTWARE, null);


String webContent = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><link rel=\"stylesheet\" href=\"style.css\"></head>"
+ "<body><img src='image.png' width=\"100px\"><div class=\"running\">I am a text rendered with INDIGO</div></body></html>";


String internalFilePath = "file://" + getFilesDir().getAbsolutePath() + "/";
web.loadDataWithBaseURL(internalFilePath, webContent, "text/html", "UTF-8", "");

The CSS file used in the above html (style.css):

@font-face {
font-family: 'MyCustomRunning';
src: url('IndigoAntiqua2Text-Regular.ttf')  format('truetype');
}


.running{
color: #565656;
font-family: "MyCustomRunning";
font-size: 48px;
}

I have only tried this while targeting minSdkVersion 19 (4.4) so I have no idea if it works on other versions

i done this by upper answers with this additions:

webView.loadDataWithBaseURL("file:///android_asset/",
WebClient.getStyledFont(someText),
"text/html; charset=UTF-8", null, "about:blank");

and then use src: url("file:///android_asset/fonts/YourFont...

public static String getStyledFont(String html) {
boolean addBodyStart = !html.toLowerCase().contains("<body>");
boolean addBodyEnd = !html.toLowerCase().contains("</body");
return "<style type=\"text/css\">@font-face {font-family: CustomFont;" +
"src: url(\"file:///android_asset/fonts/Brandon_reg.otf\")}" +
"body {font-family: CustomFont;font-size: medium;text-align: justify;}</style>" +
(addBodyStart ? "<body>" : "") + html + (addBodyEnd ? "</body>" : "");
}


thanks to everybody:)

 webview= (WebView) findViewById(R.id.webview);
String myCustomStyleString="<style type=\"text/css\">@font-face {font-family: MyFont;src: url(\"file:///android_asset/fonts/iranian_sans.ttf\")}body,* {font-family: MyFont; font-size: 13px;text-align: justify;}img{max-width:100%;height:auto; border-radius: 8px;}</style>";
webview.loadDataWithBaseURL("", myCustomStyleString+"<div style=\"direction:rtl\">"+intentpost.getStringExtra("content")+"</div>", "text/html", "utf-8", null);

You need not use local ttf file to set webview font for android, It will increase the whole app size.

you can also use online fonts like google's font... It will help to reduce your apk size.

For example: visit the below URL https://gist.github.com/karimnaaji/b6c9c9e819204113e9cabf290d580551#file-googlefonts-txt

You will found necessary info to use this font. then create a String like below

String font = "@font-face {font-family: MyFont;src: url(\"here you will put the url of the font which you get previously\")}body {font-family: MyFont;font-size: medium;text-align: justify;}";

then load the webview like this

webview.loadDataWithBaseURL("about:blank", "<style>" +font+</style>" + "your html contnet here", "text/html", null, null);

done. You will able to load the font from the external source in this way.

Now whats about the app font, it doesn't make sense to use 1 font for the webview and different font for the whole app. So you can use Downloadable Fonts in your app which also uses external sources for loading fonts in the app.

test it, work for me like a charm :

   private void setResult() {


String mimeType = "text/html;charset=UTF-8";
String encoding = "utf-8";
String htmlText = htmlPrivacy;


String text = "<html><head>"
+ "<style type=\"text/css\">@font-face {font-family: MyFont;src: url(\"file:///android_asset/fonts/iy_reg.ttf\")}body{font-family: MyFont;color: #666666}"
+ "</style></head>"
+ "<body>"
+ htmlText
+ "</body></html>";


webView.loadDataWithBaseURL(null, text, mimeType, encoding, null);
}

If you are putting fonts under res/font like me, then you can change the dir to the following:-

@font-face {
font-family: yourfont;
src: url("file:///android_res/font/yourfont.ttf")
}
        

body {
font-family: yourfont;
}

Use https://github.com/delight-im/Android-AdvancedWebView library.

in html data :

<!doctype html>
<html>


<head>
<style type="text/css">
@font-face {
font-family: MyFont;
src: url("file:///android_asset/fonts/font1.ttf")
}
body {
font-family: MyFont;
font-size: medium;
text-align: justify;
}
</style>
<meta http-equiv="Content-Type" content="text/html;  charset=utf-8">
<title>Title</title>
</head>


<body>


------ YOUR_CONTENT ------


</body>


</html>

in xml :

<im.delight.android.webview.AdvancedWebView
android:id="@+id/advanced_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />

in java :

advancedWebView = findViewById(R.id.advanced_web_view);
advancedWebView.loadHtml(htmlData, "text/html; charset=utf-8", "utf-8");

Response compatible with Android 11 (API 30) or above.

I saw a lot of answers, but none of them was working on Android 11+. For me, the solution was: WebAssetLoader.

val assetLoader = WebViewAssetLoader.Builder()
.addPathHandler(
"/res/",
WebViewAssetLoader.ResourcesPathHandler(context))
.build()

In my case, the font is in res/font folder.

webView.webViewClient = object : WebViewClient() {
...
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
return assetLoader.shouldInterceptRequest(request.url)
}
}

Load the HTML content using loadDataWithBaseURL.

webView.loadDataWithBaseURL(
"https://appassets.androidplatform.net",
getStyledHtmlContent(),
"text/html",
"UTF-8",
null
)

The getStyledHtmlContent returns your HTML content.

private fun getStyledHtmlContent() =
"""
<html>
<head>
<style>
@font-face {
font-family: 'AnyNameYouWant';
src: url('https://appassets.androidplatform.net/res/font/yourfont.otf');
}
body {
font-family: AnyNameYouWant;
line-height: 1.5;
text-align: justify;
}
</style>
</head>
<body>Content of your page</body>
</html>
""".trimIndent()