El problema es el rendimiento después de la rotación. WebView tiene que volver a cargar la página, lo que puede ser un poco tedioso.
¿Cuál es la mejor manera de manejar un cambio de orientación sin volver a cargar la página desde el origen cada vez?
El problema es el rendimiento después de la rotación. WebView tiene que volver a cargar la página, lo que puede ser un poco tedioso.
¿Cuál es la mejor manera de manejar un cambio de orientación sin volver a cargar la página desde el origen cada vez?
Respuestas:
Si no desea que WebView se vuelva a cargar en los cambios de orientación, simplemente anule onConfigurationChanged en su clase de Actividad:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Y configure el atributo android: configChanges en el manifiesto:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
Para obtener más información, consulte:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChangesatributo se agrega a la subclase Actividad 2) Si su aplicación depende de múltiples proyectos, el configChangesatributo se agrega al manifiesto del que está en el parte superior del árbol de dependencias (que puede no ser el proyecto que contiene la clase Actividad).
onConfigurationChangedanulación de método es inútil.
Editar: este método ya no funciona como se indica en los documentos
Respuesta original:
Esto se puede manejar sobrescribiendo onSaveInstanceState(Bundle outState)su actividad y llamando saveStatedesde la vista web:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Luego recupere esto en su onCreate después de que la vista web se haya vuelto a inflar, por supuesto:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
La mejor respuesta a esto es seguir la documentación de Android que se encuentra aquí. Básicamente, esto evitará que Webview se vuelva a cargar:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Opcionalmente , puede corregir anomalías (si las hay) anulando onConfigurationChangedla actividad:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
Intenté usar onRetainNonConfigurationInstance (devolver el WebView ) y luego recuperarlo con getLastNonConfigurationInstance durante onCreate y reasignarlo.
Parece que aún no funciona. Sin embargo, no puedo evitar pensar que estoy muy cerca. Hasta ahora, acabo de obtener un WebView en blanco / fondo blanco en . Publicar aquí con la esperanza de que alguien pueda ayudar a superar esta meta.
Quizás no debería pasar el WebView . Quizás un objeto desde dentro de WebView ?
El otro método que probé, no es mi favorito, es configurar esto en la actividad:
android:configChanges="keyboardHidden|orientation"
... y luego no hago casi nada aquí:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// We do nothing here. We're only handling this to keep orientation
// or keyboard hiding from causing the WebView activity to restart.
}
ESO funciona, pero podría no considerarse una mejor práctica .
Mientras tanto, también tengo un único ImageView que quiero actualizar automáticamente según la rotación. Esto resulta ser muy fácil. Debajo de mi rescarpeta, tengo drawable-landy tengo drawable-portvariaciones de paisaje / retrato, luego las uso R.drawable.myimagenamepara la fuente de ImageView y Android "hace lo correcto" - ¡sí!
... excepto cuando observa cambios de configuración, entonces no lo hace. :(
Entonces estoy en desacuerdo. Use onRetainNonConfigurationInstance y la rotación ImageView funciona, pero la persistencia de WebView no ... o use onConfigurationChanged y WebView se mantiene estable, pero ImageView no se actualiza. ¿Qué hacer?
Una última nota: en mi caso, forzar la orientación no es un compromiso aceptable. Realmente queremos apoyar con gracia la rotación. ¡Un poco como lo hace la aplicación del navegador de Android! ;)
La mejor manera de manejar los cambios de orientación y evitar la recarga de WebView en Rotate.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Con eso en mente, para evitar que se llame a onCreate () cada vez que cambie de orientación, deberá agregar android:configChanges="orientation|screenSize" to the AndroidManifest.
o solo ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Simplemente escriba las siguientes líneas de código en su archivo de Manifiesto, nada más. Realmente funciona:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Aprecio que esto sea un poco tarde, sin embargo, esta es la respuesta que utilicé al desarrollar mi solución:
AndroidManifest.xml
<activity
android:name=".WebClient"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" <--- "screenSize" important
android:label="@string/title_activity_web_client" >
</activity>
WebClient.java
public class WebClient extends Activity {
protected FrameLayout webViewPlaceholder;
protected WebView webView;
private String WEBCLIENT_URL;
private String WEBCLIENT_TITLE;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_client);
initUI();
}
@SuppressLint("SetJavaScriptEnabled")
protected void initUI(){
// Retrieve UI elements
webViewPlaceholder = ((FrameLayout)findViewById(R.id.webViewPlaceholder));
// Initialize the WebView if necessary
if (webView == null)
{
// Create the webview
webView = new WebView(this);
webView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
webView.getSettings().setSupportZoom(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.setScrollbarFadingEnabled(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setPluginState(android.webkit.WebSettings.PluginState.ON);
webView.getSettings().setLoadsImagesAutomatically(true);
// Load the URLs inside the WebView, not in the external web browser
webView.setWebViewClient(new SetWebClient());
webView.setWebChromeClient(new WebChromeClient());
// Load a page
webView.loadUrl(WEBCLIENT_URL);
}
// Attach the WebView to its placeholder
webViewPlaceholder.addView(webView);
}
private class SetWebClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.web_client, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}else if(id == android.R.id.home){
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
return;
}
// Otherwise defer to system default behavior.
super.onBackPressed();
}
@Override
public void onConfigurationChanged(Configuration newConfig){
if (webView != null){
// Remove the WebView from the old placeholder
webViewPlaceholder.removeView(webView);
}
super.onConfigurationChanged(newConfig);
// Load the layout resource for the new configuration
setContentView(R.layout.activity_web_client);
// Reinitialize the UI
initUI();
}
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
// Save the state of the WebView
webView.saveState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
// Restore the state of the WebView
webView.restoreState(savedInstanceState);
}
}
Es 2015, y muchas personas están buscando una solución que todavía funcione en teléfonos Jellybean, KK y Lollipop. Después de mucho esfuerzo, encontré una manera de preservar la vista web intacta después de cambiar la orientación. Mi estrategia es básicamente almacenar la vista web en una variable estática separada en otra clase. Luego, si se produce rotación, desconecto la vista web de la actividad, espero a que termine la orientación y vuelvo a conectar la vista web a la actividad. Por ejemplo ... primero ponga esto en su MANIFEST (keyboardHidden y keyboard son opcionales):
<application
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="com.myapp.abc.app">
<activity
android:name=".myRotatingActivity"
android:configChanges="keyboard|keyboardHidden|orientation">
</activity>
En una CLASE DE APLICACIÓN SEPARADA, ponga:
public class app extends Application {
public static WebView webview;
public static FrameLayout webviewPlaceholder;//will hold the webview
@Override
public void onCreate() {
super.onCreate();
//dont forget to put this on the manifest in order for this onCreate method to fire when the app starts: android:name="com.myapp.abc.app"
setFirstLaunch("true");
}
public static String isFirstLaunch(Context appContext, String s) {
try {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
return prefs.getString("booting", "false");
}catch (Exception e) {
return "false";
}
}
public static void setFirstLaunch(Context aContext,String s) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(aContext);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("booting", s);
editor.commit();
}
}
En la ACTIVIDAD poner:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(app.isFirstLaunch.equals("true"))) {
app.setFirstLaunch("false");
app.webview = new WebView(thisActivity);
initWebUI("www.mypage.url");
}
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
restoreWebview();
}
public void restoreWebview(){
app.webviewPlaceholder = (FrameLayout)thisActivity.findViewById(R.id.webviewplaceholder);
if(app.webviewPlaceholder.getParent()!=null&&((ViewGroup)app.webview.getParent())!=null) {
((ViewGroup) app.webview.getParent()).removeView(app.webview);
}
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
app.webview.setLayoutParams(params);
app.webviewPlaceholder.addView(app.webview);
app.needToRestoreWebview=false;
}
protected static void initWebUI(String url){
if(app.webviewPlaceholder==null);
app.webviewPlaceholder = (FrameLayout)thisActivity.findViewById(R.id.webviewplaceholder);
app.webview.getSettings().setJavaScriptEnabled(true); app.webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
app.webview.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
app.webview.getSettings().setSupportZoom(false);
app.webview.getSettings().setBuiltInZoomControls(true);
app.webview.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
app.webview.setScrollbarFadingEnabled(true);
app.webview.getSettings().setLoadsImagesAutomatically(true);
app.webview.loadUrl(url);
app.webview.setWebViewClient(new WebViewClient());
if((app.webview.getParent()!=null)){//&&(app.getBooting(thisActivity).equals("true"))) {
((ViewGroup) app.webview.getParent()).removeView(app.webview);
}
app.webviewPlaceholder.addView(app.webview);
}
Finalmente, el XML simple:
<RelativeLayout 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"
tools:context=".myRotatingActivity">
<FrameLayout
android:id="@+id/webviewplaceholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
Hay varias cosas que podrían mejorarse en mi solución, pero ya pasé mucho tiempo, por ejemplo: una forma más corta de validar si la Actividad se inició por primera vez en lugar de usar el almacenamiento de Preferencias Compartidas. Este enfoque conserva su vista web intacta (afaik), sus cuadros de texto, etiquetas, IU, variables de JavaScript y estados de navegación que no se reflejan en la URL.
Actualización: la estrategia actual es mover la instancia de WebView a la clase Aplicación en lugar del fragmento retenido cuando se separa y volver a colocar en el currículum como lo hace Josh. Para evitar que la aplicación se cierre, debe usar el servicio en primer plano, si desea conservar el estado cuando el usuario cambia de una aplicación a otra.
Si está utilizando fragmentos, puede usar la instancia de retención de WebView. La vista web se conservará como miembro de instancia de la clase. Sin embargo, debe adjuntar la vista web en OnCreateView y desconectarla antes de OnDestroyView para evitar que se destruya con el contenedor principal.
class MyFragment extends Fragment{
public MyFragment(){ setRetainInstance(true); }
private WebView webView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = ....
LinearLayout ll = (LinearLayout)v.findViewById(...);
if (webView == null) {
webView = new WebView(getActivity().getApplicationContext());
}
ll.removeAllViews();
ll.addView(webView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return v;
}
@Override
public void onDestroyView() {
if (getRetainInstance() && webView.getParent() instanceof ViewGroup) {
((ViewGroup) webView.getParent()).removeView(webView);
}
super.onDestroyView();
}
}
PD Créditos ir a kcoppock respuesta
En cuanto a 'SaveState ()' ya no funciona de acuerdo con la documentación oficial :
Tenga en cuenta que este método ya no almacena los datos de visualización para este WebView. El comportamiento anterior podría potencialmente filtrar archivos si no se llama a restoreState (Bundle).
setRetainInstanceconserva el Fragmento, la vista (y, por lo tanto, la Vista web) aún se destruye.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Estos métodos se pueden anular en cualquier actividad, básicamente solo le permite guardar y restaurar valores cada vez que se crea / destruye una actividad, cuando la orientación de la pantalla cambia, la actividad se destruye y se recrea en segundo plano, por lo que podría usar estos métodos almacenar temporalmente / restaurar estados durante el cambio.
Debe tener una mirada más profunda a los dos métodos siguientes y ver si se ajusta a su solución.
http://developer.android.com/reference/android/app/Activity.html
La mejor solución que he encontrado para hacer esto sin filtrar la Activityreferencia anterior y sin configurar el configChanges... es usar un MutableContextWrapper .
He implementado esto aquí: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Esto es lo único que funcionó para mí (incluso utilicé el estado de guardar instancia onCreateViewpero no fue tan confiable).
public class WebViewFragment extends Fragment
{
private enum WebViewStateHolder
{
INSTANCE;
private Bundle bundle;
public void saveWebViewState(WebView webView)
{
bundle = new Bundle();
webView.saveState(bundle);
}
public Bundle getBundle()
{
return bundle;
}
}
@Override
public void onPause()
{
WebViewStateHolder.INSTANCE.saveWebViewState(myWebView);
super.onPause();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.inject(this, rootView);
if(WebViewStateHolder.INSTANCE.getBundle() == null)
{
StringBuilder stringBuilder = new StringBuilder();
BufferedReader br = null;
try
{
br = new BufferedReader(new InputStreamReader(getActivity().getAssets().open("start.html")));
String line = null;
while((line = br.readLine()) != null)
{
stringBuilder.append(line);
}
}
catch(IOException e)
{
Log.d(getClass().getName(), "Failed reading HTML.", e);
}
finally
{
if(br != null)
{
try
{
br.close();
}
catch(IOException e)
{
Log.d(getClass().getName(), "Kappa", e);
}
}
}
myWebView
.loadDataWithBaseURL("file:///android_asset/", stringBuilder.toString(), "text/html", "utf-8", null);
}
else
{
myWebView.restoreState(WebViewStateHolder.INSTANCE.getBundle());
}
return rootView;
}
}
Hice un titular Singleton para el estado de WebView. El estado se conserva mientras exista el proceso de la aplicación.
EDITAR: No loadDataWithBaseURLfue necesario, funcionó tan bien con solo
//in onCreate() for Activity, or in onCreateView() for Fragment
if(WebViewStateHolder.INSTANCE.getBundle() == null) {
webView.loadUrl("file:///android_asset/html/merged.html");
} else {
webView.restoreState(WebViewStateHolder.INSTANCE.getBundle());
}
Aunque leí esto, no necesariamente funciona bien con las cookies.
prueba esto
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends AppCompatActivity {
private WebView wv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wv = (WebView) findViewById(R.id.webView);
String url = "https://www.google.ps/";
if (savedInstanceState != null)
wv.restoreState(savedInstanceState);
else {
wv.setWebViewClient(new MyBrowser());
wv.getSettings().setLoadsImagesAutomatically(true);
wv.getSettings().setJavaScriptEnabled(true);
wv.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
wv.loadUrl(url);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
wv.saveState(outState);
}
@Override
public void onBackPressed() {
if (wv.canGoBack())
wv.goBack();
else
super.onBackPressed();
}
private class MyBrowser extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
}
Esta página resuelve mi problema, pero tengo que hacer un ligero cambio en la inicial:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Esta porción tiene un pequeño problema para mí esto. En la segunda orientación, cambie la aplicación terminada con un puntero nulo
usando esto funcionó para mí:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Deberías probar esto:
onServiceConnectedmétodo, obtenga el WebView y llame al setContentViewmétodo para representar su WebView.Lo probé y funciona, pero no con otros WebViews como XWalkView o GeckoView.
@Override
protected void onSaveInstanceState(Bundle outState )
{
super.onSaveInstanceState(outState);
webView.saveState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
webView.restoreState(savedInstanceState);
}
Me gusta esta solucion http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Según esto, reutilizamos la misma instancia de WebView. Permite guardar el historial de navegación y la posición de desplazamiento en el cambio de configuración.