Índice de contenido
- ¿La clase WebView es un navegador?
- Construyendo Web Apps con WebView
- Habilitar permiso a Internet en el AndroidManifest
- La forma moderna con Jetpack Compose
- Habilitar permiso a Internet en el AndroidManifest
- Creando el Composable para el WebView
- 1. Configuración del Factory
- 2. El bloque Update
- Control de eventos con WebViewClient
- El WebViewClient nos permite definir eventos y comportamientos específicos del cliente. Por ejemplo:
- Usando el WebView en tu Actividad (Método Legacy)
- Cargando la página web en el WebView: Layout y Actividad
- Manejando la navegación de la página
- La clase WebViewClient
- Habilitando el JavaScript
Anteriormente vimos cómo mostrar un video de YouTube en el emulador de Android. Siguiendo esa misma filosofía, ahora aprenderemos a visualizar el contenido de una página web directamente en nuestro dispositivo, funcionando como una especie de navegador integrado.
¿La clase WebView es un navegador?
WebView no es un navegador ya que no cuenta con elementos presentes en navegadores como barra de direcciones u otros controles de navegación; solamente permite mostrar una página web en una aplicación nativa Android.
Aunque con la clase WebView podemos decidir si queremos mostrar el contenido dentro de la aplicación o en un navegador web como veremos más adelante.
Construyendo Web Apps con WebView
Para agregar un WebView en nuestra aplicación Android debemos de realizar una serie de configuraciones que veremos a continuación:
Habilitar permiso a Internet en el AndroidManifest
Una de las cosas que primero debemos hacer es solicitar el permiso necesario en nuestro AndroidManifest para que la aplicación pueda conectarse a Internet y poder descargar el contenido HTML:
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
***
</manifest>La forma moderna con Jetpack Compose
Con la llegada de Jetpack Compose, la forma de construir interfaces de usuario en Android ha cambiado a un enfoque declarativo. Para integrar un WebView en este nuevo mundo, utilizamos el composable AndroidView, que nos permite embeber vistas de Android tradicionales (basadas en XML) dentro de una UI de Compose.
app/src/main/AndroidManifest.xml
Habilitar permiso a Internet en el AndroidManifest
Al igual que en el método tradicional, es indispensable solicitar el permiso de INTERNET en el fichero AndroidManifest.xml:
<manifest ... >
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>Creando el Composable para el WebView
Ahora, creamos una función Composable que encapsula el WebView. Esto nos permite reutilizarlo fácilmente en cualquier parte de nuestra aplicación.
1. Configuración del Factory
Utilizamos AndroidView para inyectar la vista clásica de Android en el entorno de Compose. Especificamos que ocupe todo el tamaño de la pantalla mediante un Modifier.
JavaScript: Es fundamental habilitar JavaScript (settings.javaScriptEnabled = true) para que la página funcione correctamente si tiene elementos interactivos o diseños dependientes de scripts.
Carga de URL: Usamos la función loadUrl pasándole el parámetro que recibe nuestro Composable.
package com.example.myproyectandroid
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.myproyectandroid.ui.theme.MyProyectAndroidTheme
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyProyectAndroidTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) {
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
MyWebView(url = "https://www.desarrollolibre.net/")
}
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier.padding(8.dp)
)
}
@Composable
fun MyWebView(url: String) {
AndroidView(
modifier = Modifier.fillMaxSize(), // Ocupa toda la pantalla
factory = { context ->
WebView(context).apply {
// Configuración necesaria
settings.javaScriptEnabled = true // Permite que funcionen sitios modernos
webViewClient =
WebViewClient() // Abre los enlaces dentro de la app, no en el navegador
loadUrl(url)
}
},
update = { webView ->
// Si la URL cambia, la cargamos aquí
webView.loadUrl(url)
}
)
}2. El bloque Update
El parámetro update es muy útil (aunque opcional). Si en tu aplicación la URL cambia dinámicamente, este bloque se encargará de actualizar el contenido automáticamente sin necesidad de recrear toda la vista.
update = { webView ->
// Si la URL cambia, la cargamos aquí
webView.loadUrl(url)
}Control de eventos con WebViewClient
El WebViewClient nos permite definir eventos y comportamientos específicos del cliente. Por ejemplo:
- Navegación interna: Evitar que, al hacer clic en un enlace, el sistema abra el navegador externo del teléfono, manteniendo al usuario dentro de nuestra app.
- Ciclo de carga: Ejecutar acciones cuando la página comienza a cargar o cuando termina (ideal para mostrar un loading o barra de progreso).
- Gestión de errores: Manejar fallos de conexión o bloquear el acceso a URLs específicas mediante verificaciones personalizadas.
1. Evitar que los enlaces se abran en Chrome
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
// Retornar 'false' significa: "Yo me encargo, cárgalo aquí mismo"
return false
}
}2. Mostrar un "Cargando" (ProgressBar)
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
// Aquí podrías poner: isLoading = true
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// Aquí podrías poner: isLoading = false
}
}3. Manejar errores de conexión
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
// Aquí puedes cargar un archivo HTML local de error:
view?.loadUrl("file:///android_asset/error.html")
}4. Bloquear URLs específicas
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
return if (url.contains("misitio.com")) {
false // Permitir
} else {
true // Bloquear (no hace nada al hacer clic)
}
}⚙️ Configuraciones Avanzadas (Settings)
A través de settings, puedes personalizar la experiencia de navegación según tus necesidades:
- Vista de escritorio: Forzar la versión "Desktop" del sitio.
- Ajuste de pantalla: Escalar el contenido para que ocupe exactamente el ancho del móvil.
- Control de Zoom: Habilitar o quitar los efectos de "pinza" para hacer zoom in/out, u ocultar los botones de control de zoom.
- Caché y Almacenamiento: Habilitar el almacenamiento local en caché para mejorar la velocidad.
- Seguridad: Evitar que scripts abran ventanas emergentes (pop-ups) de forma automática.
1. Optimización de Visualización y Zoom
settings.apply {
// Hace que la página se vea como en un navegador de escritorio si es muy grande
useWideViewPort = true
// Ajusta el contenido al ancho de la pantalla del móvil automáticamente
loadWithOverviewMode = true
// Habilita los controles de zoom (los botones + y -)
builtInZoomControls = true
// Oculta esos botones de zoom feos pero permite el gesto de "pinza" con los dedos
displayZoomControls = false
}2. Almacenamiento y Rendimiento (Cache)
settings.apply {
// Habilita el almacenamiento local (como las bases de datos de las webs modernas)
domStorageEnabled = true
// Permite que la web guarde archivos en el dispositivo (cache)
databaseEnabled = true
// Define cómo se comporta el cache (cargar desde internet o desde el disco)
cacheMode = WebSettings.LOAD_DEFAULT
}3. Seguridad y Privacidad
settings.apply {
// Bloquea que la web cargue imágenes desde sitios "http" si la web es "https"
mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
// Permite o deniega que el JavaScript abra ventanas nuevas (pop-ups) automáticamente
javaScriptCanOpenWindowsAutomatically = false
}4. Acceso a Archivos y Cámara
settings.apply {
// Permite que el JS de la web acceda a archivos dentro de la app (Cuidado con esto)
allowFileAccess = true
// Permite que el contenido cargado desde un archivo acceda a otros archivos
allowContentAccess = true
}Ejemplo de configuración “Todo Terreno”
AndroidView(
factory = { context ->
WebView(context).apply {
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true // Clave para sitios modernos como Spotify o YouTube
loadWithOverviewMode = true
useWideViewPort = true
builtInZoomControls = true
displayZoomControls = false
setSupportZoom(true)
mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
webViewClient = WebViewClient()
}
},
update = { webView -> webView.loadUrl(url) }
)Usando el WebView en tu Actividad (Método Legacy)
Vamos a conocer la forma con XML que es la de legado. Llamamos a nuestro composable WebViewPage desde nuestra MainActivity o cualquier otra pantalla de nuestra aplicación.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WebViewPage(url = "https://desarrollolibre.net")
}
}
}
Explicación del código:
- AndroidView: Es el composable clave que nos permite usar una Vista de Android tradicional dentro de Compose.
- factory: Este bloque se ejecuta una sola vez para crear la WebView. Aquí configuramos sus parámetros iniciales, como el layoutParams, el webViewClient y la habilitación de JavaScript.
- update: Este bloque se llama cada vez que el composable se recompone. Lo usamos para actualizar la vista, en este caso, para cargar una nueva URL si ha cambiado.
- WebViewClient: Al igual que en el enfoque tradicional, es crucial para manejar la navegación dentro del WebView y evitar que se abran navegadores externos.
Con estos pasos, ya tienes un WebView funcional en tu aplicación de Jetpack Compose, siguiendo las prácticas modernas de desarrollo de Android.
Cargando la página web en el WebView: Layout y Actividad
Una vez solicitado el permiso agregamos el tag WebView en el layout de la actividad de Android:
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>Finalmente podemos agregar la URL de la página web que deseemos mostrar a través del método loadUrl():
WebView webView = (WebView) this.findViewById(R.id.webview);
webView.loadUrl("");En nuestra aplicación agregaremos la siguiente URL referenciada en el código:
webView.loadUrl("http://desarrollolibre.net/blog/javascript/como-hacer-una-sencilla-galeria-con-css-y-6-lineas-de-javascript");Manejando la navegación de la página
Ya en este punto podemos ejecutar nuestra aplicación Android; pero al ejecutar la misma veremos una pantalla como la siguiente:

La idea de una crear una Web App con la clase WebView no es que simplemente abramos el contenido en alguno de los navegadores web que tengamos instalado en el dispositivo Android, si no que se ejecute dentro de nuestra aplicación; para esto veremos el siguiente bloque.
La clase WebViewClient
Como comentamos anteriormente; por defecto Android lanza una aplicación (como los navegadores web) que maneje la URL anteriormente establecida; pero podemos personalizar este comportamiento para que la maneje internamente nuestra aplicación; en otras palabras, que la página web se ejecute directamente en nuestra aplicación.
Sencillamente creamos una instancia de la clase WebViewClient y es lo mínimo que necesitamos para mostrar una página web dentro de nuestra aplicación:
webView.setWebViewClient(new WebViewClient());Pero si deseamos más control podemos crear nuestra propia clase que extienda de WebViewClient y sobrescribir ciertos métodos:
private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return true; } }Habilitando el JavaScript
El JavaScript se encuentra deshabilitado por defecto en el WebView, lo cual puede ser un problema según el tipo de página web que se desee cargar; es decir, si nuestra página a cargar a través del método loadUrl() necesita JavaScript; podemos habilitar el Internet fácilmente a través del método setJavaScriptEnabled(); veamos como usarlo:
Primero creamos un objeto WebSettings que permite realizar configuraciones variadas, aunque en nuestro caso, simplemente nos interesa habilitar el JavaScript:
WebSettings webSettings = myWebView.getSettings();Ahora si podemos emplear el método setJavaScriptEnabled() pasando como parámetro el booleano true.
webSettings.setJavaScriptEnabled(true);Al ejecutar el código:

Siguiente paso, crea una notificación en Android.