40

For example I want to change the background-color of www.google.comto red. I have used webview, and my style.cssfile is in assest folder. I want to inject this style.css file to www.google.com. What is wrong with my codes? Please write the correct codes for me. Thanks. My MainActitviy.java file :

package com.example.mysina; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.webkit.WebView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = new WebView(this); setContentView(webView); String html = "<html><head><style> src: url('file:///android_asset/style.css')</style></head></html>"; webView.loadData(html, "text/html", "utf-8"); webView.loadUrl("https://www.google.com"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } 
1
  • I am doing the same you are doing. Just tell me where u add the css in your android project, and what your css files contains? i am stuck. Please help. Commented Jun 26, 2018 at 8:02

5 Answers 5

71

You can't inject CSS directly however you can use Javascript to manipulate page dom.

public class MainActivity extends ActionBarActivity { WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); webView = new WebView(this); setContentView(webView); // Enable Javascript webView.getSettings().setJavaScriptEnabled(true); // Add a WebViewClient webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // Inject CSS when page is done loading injectCSS(); super.onPageFinished(view, url); } }); // Load a webpage webView.loadUrl("https://www.google.com"); } // Inject CSS method: read style.css from assets folder // Append stylesheet to document head private void injectCSS() { try { InputStream inputStream = getAssets().open("style.css"); byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); inputStream.close(); String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP); webView.loadUrl("javascript:(function() {" + "var parent = document.getElementsByTagName('head').item(0);" + "var style = document.createElement('style');" + "style.type = 'text/css';" + // Tell the browser to BASE64-decode the string into your script !!! "style.innerHTML = window.atob('" + encoded + "');" + "parent.appendChild(style)" + "})()"); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } 
Sign up to request clarification or add additional context in comments.

9 Comments

Thanx. I have copied these codes to my project. Now I can change the style of the page. But there is a big problem. It can not load page. it says: Webpage not availabe. It does not open www.google.com . Which code I should change it?
This is one of the best answers I've seen on this subject, but do be warned that there will be a little bit of a page jump if you switch styles. It would be wise to not show content until after the CSS injection is complete.
Use DOM manipulation before loading Url in webview. By doing so you can inject any script or javascript file at runtime and then load the content into webview.I have been doing so in my ePub player and it works like charm.
@Vishal would you mind adding an answer here with what you are talking about?
Why the base64 part?
|
12

I was able to inject css by using "evaluateJavascript" which was added to WebView in API 19 https://developer.android.com/reference/android/webkit/WebView

Example in Kotlin:

private lateinit var webView: WebView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //... webView = findViewById(R.id.your_webview_name) webView.settings.javaScriptEnabled = true webView.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { val css = ".menu_height{height:35px;}.. etc..." //your css as String val js = "var style = document.createElement('style'); style.innerHTML = '$css'; document.head.appendChild(style);" webView.evaluateJavascript(js,null) super.onPageFinished(view, url) } } webView.loadUrl("https://mywepage.com") //webpage you want to load } 

UPDATE: The code above had issues applying all of the injected CSS. After consulting with my web developer, we decided to inject the link to the CSS file instead of the CSS code itself. I changed the values of the css and js variables ->

val css = "https://mywebsite.com/css/custom_app_styles.css" val js = "var link = document.createElement('link'); link.setAttribute('href','$css'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('type','text/css'); document.head.appendChild(link);" 

Comments

2

For Kotlin Users

import this

import android.util.Base64 

and this is onPageFinished code

 override fun onPageFinished(view: WebView?, url: String?) { injectCSS() } 

and this is the code to call

private fun injectCSS() { try { val inputStream = assets.open("style.css") val buffer = ByteArray(inputStream.available()) inputStream.read(buffer) inputStream.close() val encoded = Base64.encodeToString(buffer , Base64.NO_WRAP) webframe.loadUrl( "javascript:(function() {" + "var parent = document.getElementsByTagName('head').item(0);" + "var style = document.createElement('style');" + "style.type = 'text/css';" + // Tell the browser to BASE64-decode the string into your script !!! "style.innerHTML = window.atob('" + encoded + "');" + "parent.appendChild(style)" + "})()" ) } catch (e: Exception) { e.printStackTrace() } } 

Comments

2

Here is the code snippet using Manish solution in Kotlin with a little bit UI/UX improve to avoid webview blinking/flash when inject css

Step 1: Define your css style which encoded to Base64

 companion object { private const val injectCss = """ Your CSS Style Go HERE """ private val styleCss = """ javascript:(function() { var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = window.atob('${stringToBase64(hideHeaderCss)}'); parent.appendChild(style) })() """ private fun stringToBase64(input: String): String { val inputStream: InputStream = ByteArrayInputStream(input.toByteArray(StandardCharsets.UTF_8)) val buffer = ByteArray(inputStream.available()) inputStream.read(buffer) inputStream.close() return android.util.Base64.encodeToString(buffer, android.util.Base64.NO_WRAP) } } 

Step 2: Your webview initialize state should be invisible

 <WebView android:id="@+id/wv_content" android:visibility="invisible" android:layout_width="match_parent" android:layout_height="match_parent"/> 

Step 3: Load webview with injected css, the trick here is only show webview when load complete

@SuppressLint("SetJavaScriptEnabled") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewDataBinding.wvContent.loadUrl("Your URL Go Here") viewDataBinding.wvContent.settings.javaScriptEnabled = true viewDataBinding.wvContent.webViewClient = object : WebViewClient() { override fun onPageCommitVisible(view: WebView?, url: String?) { applyContentWithCSS() super.onPageCommitVisible(view, url) } override fun onPageFinished(view: WebView?, url: String?) { applyContentWithCSS() //Only show when load complete if (viewDataBinding.wvContent.progress == 100) { viewDataBinding.wvContent.smoothShow() } super.onPageFinished(view, url) } } } 

Step 4: Improve UX with a smooth transition to show webview

fun View.smoothShow() { this.apply { alpha = 0f visibility = View.VISIBLE animate() .alpha(1f) .setDuration(300) .setListener(null) } } 

2 Comments

Thank you for the tip for preventing blinking and provide users with a better and seamless experience.
I noticed that inserting applyContentWithCSS in onPageStarted handler makes the page display without blinking on newer Android versions and no animation is required.
0

Actually you can use WebViewClient.shouldInterceptRequest on API 11+. Example: webview shouldinterceptrequest example

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.