Le problème est la performance après la rotation. La 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 du source à chaque fois?
Si vous ne souhaitez pas que WebView recharge les modifications d'orientation, remplacez simplement onConfigurationChanged dans votre classe d'activité:
@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
Edit: cette méthode ne fonctionne plus comme indiqué dans docs
Réponse originale:
Ceci peut être géré en écrasant onSaveInstanceState(Bundle outState)
dans votre activité et en appelant saveState
à partir de la vue Web:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Puis récupérez ceci 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 à cette question est de suivre la documentation Android trouvée ici Cela empêchera Webview de recharger:
<activity Android:name=".MyActivity"
Android:configChanges="orientation|screenSize"
Android:label="@string/app_name">
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 (en renvoyant WebView), puis de le récupérer avec getLastNonConfigurationInstance pendant onCreate et de le réaffecter.
Ne semble pas fonctionner pour l'instant. Je ne peux pas m'empêcher de penser que je suis vraiment proche cependant! Jusqu'à présent, je viens d'obtenir un WebView vide/blanc. Publier ici dans l'espoir que quelqu'un puisse aider Poussez celui-ci au-delà de la ligne d'arrivée.
Peut-être que je ne devrais pas passer le WebView. Peut-être un objet à l'intérieur de la WebView?
L’autre méthode que j’ai essayée - et non ma préférée - consiste à définir ceci dans l’activité:
Android:configChanges="keyboardHidden|orientation"
... et ne fais 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 être très facile. Sous mon dossier res
, j'ai drawable-land
et drawable-port
pour contenir les variations paysage/portrait, puis j'utilise R.drawable.myimagename
pour la source de ImageView et Android "fait ce qu'il faut" - yay!
... sauf lorsque vous surveillez les changements de configuration, alors ce n'est pas le cas. :(
Donc, je suis en désaccord. Utilisez onRetainNonConfigurationInstance et la rotation ImageView, mais WebView la persistance ne fonctionne pas ... ou utilisez onConfigurationChanged et la 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 comment le navigateur Android app fait! ;)
Un compromis consiste à éviter la rotation . Ajoutez ceci pour corriger l'activité uniquement pour l'orientation Portrait.
Android:screenOrientation="portrait"
J'apprécie que ce soit un peu tard, mais voici 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);
}
}
Vous pouvez essayer d'utiliser onSaveInstanceState()
et onRestoreInstanceState()
sur votre activité pour appeler saveState(...)
et restoreState(...)
sur votre instance WebView.
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 reconnecter lors de la reprise, comme le fait Josh ... Pour éviter que Application ne se ferme, 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 WebView. La vue Web sera conservée en tant que membre d'instance de la classe. Vous devez toutefois attacher la vue Web dans OnCreateView et la dissocier avant OnDestroyView pour éviter sa destruction 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();
}
}
P.S. Les crédits vont à kcoppock answer </ S>
Quant à 'SaveState ()', il ne fonctionne plus conformément à documentation officielle :
Veuillez noter que cette méthode ne stocke plus les données d'affichage pour cette WebView. Le comportement précédent pourrait potentiellement entraîner la fuite de fichiers si restoreState (Bundle) n'a jamais été appelé.
C'est la seule chose qui a fonctionné pour moi (j'ai même utilisé l'état de l'instance de sauvegarde dans 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 créé un détenteur Singleton pour l'état de la WebView. L'état est préservé tant que le processus de l'application existe.
EDIT: La 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'ai lu cela ne fonctionne pas nécessairement bien avec les cookies.
Écrivez simplement les lignes de code suivantes dans votre fichier manifeste - rien d’autre. Ça marche vraiment:
<activity Android:name=".YourActivity"
Android:configChanges="orientation|screenSize"
Android:label="@string/application_name">
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Ces méthodes peuvent être écrasées sur n'importe quelle activité. Elles vous permettent simplement de sauvegarder 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. stocker/restaurer temporairement les états pendant le changement.
Vous devriez examiner de plus près les deux méthodes suivantes et voir si cela convient à votre solution.
http://developer.Android.com/reference/Android/app/Activity.html
La seule chose à faire est d’ajouter ce code à votre fichier manifeste:
<activity Android:name=".YourActivity"
Android:configChanges="orientation|screenSize"
Android:label="@string/application_name">
Meilleure façon 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 d'appeler onCreate () à chaque fois que vous changez d'orientation, vous devez ajouter Android:configChanges="orientation|screenSize" to the AndroidManifest.
ou juste ..
Android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Vous devriez essayer ceci:
onServiceConnected
, récupérez WebView et appelez la méthode setContentView
pour restituer votre WebView.Je l'ai testé et cela fonctionne, mais pas avec d'autres WebViews comme XWalkView ou GeckoView.
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;
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState )
{
super.onSaveInstanceState(outState);
webView.saveState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
webView.restoreState(savedInstanceState);
}
Cette page résout mon problème mais je dois apporter une légère modification à la première:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Cette partie a un léger problème pour moi cela. Sur la deuxième orientation, modifier l'application terminée par un pointeur nul
en utilisant cela cela a fonctionné pour moi:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}