Le problème est la performance après la rotation. Le WebView doit recharger la page, ce qui peut être un peu fastidieux.
Quelle est la meilleure façon de gérer un changement d'orientation sans recharger la page à partir de la source à chaque fois?
Le problème est la performance après la rotation. Le WebView doit recharger la page, ce qui peut être un peu fastidieux.
Quelle est la meilleure façon de gérer un changement d'orientation sans recharger la page à partir de la source à chaque fois?
Réponses:
Si vous ne souhaitez pas que WebView se recharge lors des changements d'orientation, remplacez simplement onConfigurationChanged dans votre classe Activity:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Et définissez l'attribut android: configChanges dans le manifeste:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
pour plus d'informations, voir:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
attribut est ajouté à la sous-classe Activité 2) Si votre application dépend de plusieurs projets, l' configChanges
attribut est ajouté au manifeste de celui au en haut de l'arborescence des dépendances (qui peut ne pas être le projet contenant la classe Activity).
onConfigurationChanged
remplacement de méthode est inutile.
Edit: cette méthode ne fonctionne plus comme indiqué dans la documentation
Réponse originale:
Cela peut être géré en écrasant onSaveInstanceState(Bundle outState)
votre activité et en appelant à saveState
partir de la vue Web:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Ensuite, récupérez-le dans votre onCreate après que la vue Web ait été regonflée bien sûr:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
La meilleure réponse à cela est de suivre la documentation Android trouvée ici Fondamentalement, cela empêchera Webview de se recharger:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Vous pouvez éventuellement corriger les anomalies (le cas échéant) en remplaçant onConfigurationChanged
dans l'activité:
@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();
}
}
J'ai essayé d'utiliser onRetainNonConfigurationInstance (retournant le WebView ), puis de le récupérer avec getLastNonConfigurationInstance pendant onCreate et de le réattribuer.
Cela ne semble pas encore fonctionner. Je ne peux pas m'empêcher de penser que je suis vraiment proche! Jusqu'à présent, j'obtiens juste une WebView vierge / fond blanc à la place. Publier ici dans l'espoir que quelqu'un puisse aider à pousser celui-ci au-delà de la ligne d'arrivée.
Peut-être que je ne devrais pas passer le WebView . Peut-être un objet de la WebView ?
L'autre méthode que j'ai essayée - pas ma préférée - est de définir ceci dans l'activité:
android:configChanges="keyboardHidden|orientation"
... et puis ne faites pratiquement rien ici:
@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.
}
CELA fonctionne, mais cela pourrait ne pas être considéré comme une meilleure pratique .
En attendant, j'ai également un seul ImageView que je souhaite mettre à jour automatiquement en fonction de la rotation. Cela s'avère très facile. Dans mon res
dossier, j'ai drawable-land
et je dois drawable-port
contenir des variations paysage / portrait, puis j'utilise R.drawable.myimagename
pour la source ImageView et Android "fait ce qu'il faut" - yay!
... sauf quand vous regardez les changements de configuration, alors ce n'est pas le cas. :(
Donc je suis en désaccord. Utilisez onRetainNonConfigurationInstance et les ImageView travaux de rotation, mais WebView persistance ne pas ... ou l' utilisation onConfigurationChanged et WebView reste stable, mais le ImageView ne met pas à jour. Que faire?
Une dernière remarque: dans mon cas, forcer l'orientation n'est pas un compromis acceptable. Nous voulons vraiment soutenir gracieusement la rotation. Un peu comme le fait l'application Navigateur Android! ;)
Un compromis est d'éviter la rotation. Ajoutez ceci pour corriger l'activité pour l'orientation Portrait uniquement.
android:screenOrientation="portrait"
Meilleur moyen de gérer les changements d'orientation et d'empêcher le rechargement de WebView lors de la rotation.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Dans cet esprit, pour éviter que onCreate () ne soit appelé à chaque fois que vous changez d'orientation, vous devrez ajouter android:configChanges="orientation|screenSize" to the AndroidManifest.
ou juste ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Écrivez simplement les lignes de code suivantes dans votre fichier Manifest - rien d'autre. Ça marche vraiment:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
J'apprécie que ce soit un peu tard, mais c'est la réponse que j'ai utilisée lors du développement de ma solution:
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);
}
}
Nous sommes en 2015 et de nombreuses personnes recherchent une solution qui fonctionne toujours sur les téléphones Jellybean, KK et Lollipop. Après beaucoup de difficultés, j'ai trouvé un moyen de préserver la vue Web intacte après avoir changé d'orientation. Ma stratégie consiste essentiellement à stocker la vue Web dans une variable statique distincte dans une autre classe. Ensuite, si une rotation se produit, je détache la vue Web de l'activité, attend la fin de l'orientation et rattache la vue Web à l'activité. Par exemple ... mettez d'abord ceci sur votre MANIFEST (keyboardHidden et keyboard sont facultatifs):
<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>
Dans une CLASSE D'APPLICATION SÉPARÉE, mettez:
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();
}
}
Dans l'activité mettez:
@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);
}
Enfin, le simple XML:
<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>
Il y a plusieurs choses qui pourraient être améliorées dans ma solution, mais j'ai déjà passé beaucoup de temps, par exemple: un moyen plus court de valider si l'activité a été lancée pour la toute première fois au lieu d'utiliser le stockage SharedPreferences. Cette approche préserve votre vue Web intacte (afaik), ses zones de texte, ses étiquettes, son interface utilisateur, ses variables javascript et ses états de navigation qui ne sont pas reflétés par l'URL.
Mise à jour: la stratégie actuelle consiste à déplacer l'instance WebView vers la classe Application au lieu du fragment conservé lorsqu'elle est détachée et de la rattacher au redémarrage comme le fait Josh. Pour empêcher l'application de se fermer, vous devez utiliser le service de premier plan si vous souhaitez conserver l'état lorsque l'utilisateur bascule entre les applications.
Si vous utilisez des fragments, vous pouvez utiliser une instance de conservation de WebView. La vue Web sera conservée comme membre d'instance de la classe. Vous devez cependant attacher la vue Web dans OnCreateView et la détacher avant OnDestroyView pour l'empêcher de se détruire avec le conteneur parent.
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();
}
}
Les crédits PS vont à la réponse de kcoppock
Quant à 'SaveState ()', il ne fonctionne plus selon la documentation officielle :
Veuillez noter que cette méthode ne stocke plus les données d'affichage pour cette WebView. Le comportement précédent pouvait potentiellement fuir des fichiers si restoreState (Bundle) n'était jamais appelé.
setRetainInstance
le fragment conserve le fragment, la vue (et donc la WebView) est toujours détruite.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Ces méthodes peuvent être remplacées sur n'importe quelle activité, cela vous permet simplement d'enregistrer et de restaurer des valeurs chaque fois qu'une activité est créée / détruite, lorsque l'orientation de l'écran change, l'activité est détruite et recréée en arrière-plan, vous pouvez donc utiliser ces méthodes aux états de stockage / restauration temporaires pendant la modification.
Vous devriez examiner plus en détail les deux méthodes suivantes et voir si cela correspond à votre solution.
http://developer.android.com/reference/android/app/Activity.html
La meilleure solution que j'ai trouvée pour faire cela sans divulguer la Activity
référence précédente et sans définir le configChanges
.. est d'utiliser un MutableContextWrapper .
J'ai implémenté ceci ici: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
C'est la seule chose qui a fonctionné pour moi (j'ai même utilisé l'état de sauvegarde de l'instance onCreateView
mais ce n'était pas aussi fiable).
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;
}
}
J'ai fait un support Singleton pour l'état de la WebView. L'état est conservé tant que le processus de l'application existe.
EDIT: Ce loadDataWithBaseURL
n'était pas nécessaire, cela fonctionnait aussi bien avec juste
//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());
}
Bien que j'aie lu, cela ne fonctionne pas nécessairement bien avec les cookies.
essaye ça
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;
}
}
}
Cette page résout mon problème mais je dois faire un léger changement dans la première:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Cette partie a un léger problème pour moi cela. Lors du deuxième changement d'orientation, l'application s'est terminée avec un pointeur nul
en utilisant cela, cela a fonctionné pour moi:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Vous devriez essayer ceci:
onServiceConnected
méthode, récupérez WebView et appelez la setContentView
méthode pour rendre votre WebView.Je l'ai testé et cela fonctionne mais pas avec d'autres WebViews comme XWalkView ou 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);
}
J'aime cette solution http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Selon lui, nous réutilisons la même instance de WebView. Il permet de sauvegarder l'historique de navigation et de faire défiler la position lors du changement de configuration.