Let us create a fully custom WebView app with advanced features in Java. Here's a comprehensive implementation:
1. Main Activity (MainActivity.java)
package com.example.customwebview;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.*;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
public class MainActivity extends AppCompatActivity {
private AdvancedWebView webView;
private EditText urlBar;
private ProgressBar progressBar;
private ImageButton btnBack, btnForward, btnRefresh, btnHome;
private SwipeRefreshLayout swipeRefreshLayout;
private LinearLayout bottomNavigation;
private TextView pageTitle;
private static final String HOME_URL = "https://www.google.com";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeViews();
setupWebView();
setupEventListeners();
// Load home page
webView.loadUrl(HOME_URL);
}
private void initializeViews() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
webView = findViewById(R.id.webview);
urlBar = findViewById(R.id.url_bar);
progressBar = findViewById(R.id.progress_bar);
btnBack = findViewById(R.id.btn_back);
btnForward = findViewById(R.id.btn_forward);
btnRefresh = findViewById(R.id.btn_refresh);
btnHome = findViewById(R.id.btn_home);
swipeRefreshLayout = findViewById(R.id.swipe_refresh);
bottomNavigation = findViewById(R.id.bottom_navigation);
pageTitle = findViewById(R.id.page_title);
}
@SuppressLint("SetJavaScriptEnabled")
private void setupWebView() {
WebSettings webSettings = webView.getSettings();
// Enable basic features
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setDatabaseEnabled(true);
webSettings.setAppCacheEnabled(true);
// Enable advanced features
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
webSettings.setSupportZoom(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowContentAccess(true);
// Performance optimizations
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
webSettings.setEnableSmoothTransition(true);
// Security settings
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
// Set WebView client and chrome client
webView.setWebViewClient(new CustomWebViewClient());
webView.setWebChromeClient(new CustomWebChromeClient());
// Enable mixed content for modern websites
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
}
private void setupEventListeners() {
// URL bar listener
urlBar.setOnKeyListener((v, keyCode, event) -> {
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
String url = urlBar.getText().toString().trim();
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
webView.loadUrl(url);
return true;
}
return false;
});
// Navigation buttons
btnBack.setOnClickListener(v -> {
if (webView.canGoBack()) {
webView.goBack();
}
});
btnForward.setOnClickListener(v -> {
if (webView.canGoForward()) {
webView.goForward();
}
});
btnRefresh.setOnClickListener(v -> webView.reload());
btnHome.setOnClickListener(v -> webView.loadUrl(HOME_URL));
// Swipe to refresh
swipeRefreshLayout.setOnRefreshListener(() -> {
webView.reload();
new Handler(Looper.getMainLooper()).postDelayed(() ->
swipeRefreshLayout.setRefreshing(false), 2000);
});
}
private class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
// Handle external URLs (tel, mailto, etc.)
if (url.startsWith("tel:") || url.startsWith("mailto:") ||
url.startsWith("sms:") || url.startsWith("market:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
// Block certain URLs if needed
if (isBlockedUrl(url)) {
view.loadUrl("about:blank");
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
urlBar.setText(url);
updateNavigationButtons();
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
updateNavigationButtons();
// Update page title
String title = view.getTitle();
if (title != null && !title.isEmpty()) {
pageTitle.setText(title);
}
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
progressBar.setVisibility(View.GONE);
// Load custom error page
String errorHtml = getErrorHtml(description, errorCode);
view.loadDataWithBaseURL(null, errorHtml, "text/html", "UTF-8", null);
}
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (request.isForMainFrame()) {
progressBar.setVisibility(View.GONE);
}
}
}
private class CustomWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
progressBar.setProgress(newProgress);
if (newProgress == 100) {
progressBar.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title != null && !title.isEmpty()) {
pageTitle.setText(title);
}
}
// Handle JavaScript alerts
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
// You can implement custom alert dialog here
return super.onJsAlert(view, url, message, result);
}
}
private void updateNavigationButtons() {
btnBack.setEnabled(webView.canGoBack());
btnForward.setEnabled(webView.canGoForward());
// Change button appearance based on state
btnBack.setAlpha(webView.canGoBack() ? 1.0f : 0.5f);
btnForward.setAlpha(webView.canGoForward() ? 1.0f : 0.5f);
}
private boolean isBlockedUrl(String url) {
// Add URLs you want to block
String[] blockedPatterns = {
"malicious.com",
"phishing.site"
};
for (String pattern : blockedPatterns) {
if (url.contains(pattern)) {
return true;
}
}
return false;
}
private String getErrorHtml(String description, int errorCode) {
return "<html>" +
"<body style='text-align:center; padding:50px;'>" +
"<h2>Page Load Error</h2>" +
"<p>Error Code: " + errorCode + "</p>" +
"<p>" + description + "</p>" +
"<button onclick='window.history.back()'>Go Back</button>" +
"<button onclick='location.reload()'>Retry</button>" +
"</body></html>";
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Handle back button
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_clear_cache) {
clearCache();
return true;
} else if (id == R.id.menu_settings) {
// Open settings activity
return true;
} else if (id == R.id.menu_share) {
sharePage();
return true;
} else if (id == R.id.menu_bookmarks) {
// Open bookmarks
return true;
}
return super.onOptionsItemSelected(item);
}
private void clearCache() {
webView.clearCache(true);
CookieManager.getInstance().removeAllCookies(null);
WebStorage.getInstance().deleteAllData();
}
private void sharePage() {
String currentUrl = webView.getUrl();
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, currentUrl);
startActivity(Intent.createChooser(shareIntent, "Share URL"));
}
@Override
protected void onPause() {
super.onPause();
webView.onPause();
}
@Override
protected void onResume() {
super.onResume();
webView.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (webView != null) {
webView.destroy();
}
}
}
2. Advanced WebView Class (AdvancedWebView.java)
package com.example.customwebview;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
public class AdvancedWebView extends WebView {
public AdvancedWebView(Context context) {
super(context);
initialize();
}
public AdvancedWebView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public AdvancedWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
// Additional initialization if needed
}
// Add custom methods here
public void clearAllData() {
clearCache(true);
clearFormData();
clearHistory();
clearMatches();
}
public String getPageTitle() {
return getTitle();
}
public String getCurrentUrl() {
return getUrl();
}
}
3. Layout File (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/page_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="WebView Browser"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
<EditText
android:id="@+id/url_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter URL or search"
android:background="@android:color/white"
android:padding="8dp"
android:textSize="12sp"
android:singleLine="true"
android:imeOptions="actionGo" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
<!-- Progress Bar -->
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="4dp"
android:visibility="gone" />
<!-- Swipe Refresh Layout -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- WebView -->
<com.example.customwebview.AdvancedWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- Bottom Navigation -->
<LinearLayout
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#f5f5f5"
android:padding="8dp">
<ImageButton
android:id="@+id/btn_back"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:src="@android:drawable/ic_media_previous"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Back" />
<ImageButton
android:id="@+id/btn_forward"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:src="@android:drawable/ic_media_next"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Forward" />
<ImageButton
android:id="@+id/btn_refresh"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:src="@android:drawable/ic_menu_rotate"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Refresh" />
<ImageButton
android:id="@+id/btn_home"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:src="@android:drawable/ic_menu_always_landscape_portrait"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Home" />
</LinearLayout>
</LinearLayout>
4. Menu Resource (main_menu.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_share"
android:title="Share"
android:icon="@android:drawable/ic_menu_share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_bookmarks"
android:title="Bookmarks"
android:icon="@android:drawable/ic_menu_agenda"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_clear_cache"
android:title="Clear Cache" />
<item
android:id="@+id/menu_settings"
android:title="Settings" />
</menu>
5. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.customwebview">
<!-- Internet Permission -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Handle URL intents -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>
6. Additional Features Class (WebViewFeatures.java)
package com.example.customwebview;
import android.webkit.WebView;
import android.webkit.JavascriptInterface;
public class WebViewFeatures {
private WebView webView;
public WebViewFeatures(WebView webView) {
this.webView = webView;
setupJavaScriptInterface();
}
private void setupJavaScriptInterface() {
// Add JavaScript interface for bidirectional communication
webView.addJavascriptInterface(new WebAppInterface(), "Android");
}
public class WebAppInterface {
@JavascriptInterface
public void showToast(String message) {
// You can show toast messages from JavaScript
}
@JavascriptInterface
public String getUserAgent() {
return webView.getSettings().getUserAgentString();
}
}
// Inject custom CSS
public void injectCSS(String css) {
String js = "javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var style = document.createElement('style');" +
"style.type = 'text/css';" +
"style.innerHTML = '" + css + "';" +
"parent.appendChild(style)" +
"})()";
webView.loadUrl(js);
}
// Inject custom JavaScript
public void injectJavaScript(String js) {
webView.loadUrl("javascript:" + js);
}
// Enable/disable JavaScript
public void setJavaScriptEnabled(boolean enabled) {
webView.getSettings().setJavaScriptEnabled(enabled);
}
// Set user agent
public void setUserAgent(String userAgent) {
webView.getSettings().setUserAgentString(userAgent);
}
}
Key Features Included:
- Custom Navigation: Back, forward, refresh, and home buttons
- URL Bar: With automatic URL formatting
- Progress Indicator: Shows page loading progress
- Swipe to Refresh: Pull down to refresh current page
- Error Handling: Custom error pages with retry functionality
- Security Features: URL blocking and safe browsing
- Cache Management: Clear cache and cookies
- Share Functionality: Share pages with other apps
- JavaScript Interface: Bidirectional communication
- Customizable Settings: Zoom, JavaScript, cache modes
Usage Instructions:
- Create a new Android project
- Replace the main activity with the provided code
- Add the layout files and menu resources
- Add internet permission to manifest
- Build and run
This implementation provides a solid foundation for a custom WebView browser with advanced features that you can further extend based on your specific requirements.

Top comments (0)