Il problema è rappresentato dalle prestazioni successive alla rotazione. WebView deve ricaricare la pagina, che può essere un po 'noiosa.
Qual è il modo migliore di gestire un cambio di orientamento senza ricaricare la pagina dalla fonte ogni volta?
Il problema è rappresentato dalle prestazioni successive alla rotazione. WebView deve ricaricare la pagina, che può essere un po 'noiosa.
Qual è il modo migliore di gestire un cambio di orientamento senza ricaricare la pagina dalla fonte ogni volta?
Risposte:
Se non si desidera che WebView si ricarichi sulle modifiche all'orientamento, è sufficiente sostituire onConfigurationChanged nella classe Activity:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
E imposta l'attributo android: configChanges nel manifest:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
per maggiori informazioni consultare:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChangesattributo viene aggiunto alla sottoclasse Attività 2) Se l'app dipende da più progetti, l' configChangesattributo viene aggiunto al manifest di quello nella parte superiore dell'albero delle dipendenze (che potrebbe non essere il progetto contenente la classe Activity).
onConfigurationChangedsostituzione del metodo è inutile.
Modifica: questo metodo non funziona più come indicato nei documenti
Risposta originale:
Questo può essere gestito sovrascrivendo la onSaveInstanceState(Bundle outState)tua attività e chiamando saveStatedalla webview:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Quindi ripristinalo nel tuo onCreate dopo che la visualizzazione web è stata gonfiata, ovviamente:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
La migliore risposta a questo sta seguendo Android documentazione trovata qui Fondamentalmente questo impedirà Webview da ricaricare:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
facoltativamente , è possibile correggere eventuali anomalie sostituendo onConfigurationChangedl'attività:
@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();
}
}
Ho provato a utilizzare onRetainNonConfigurationInstance (restituendo WebView ), quindi a recuperarlo con getLastNonConfigurationInstance durante onCreate e a riassegnarlo.
Non sembra funzionare ancora. Non posso fare a meno di pensare che sono davvero vicino però! Finora ho solo uno sfondo bianco / bianco WebView . Pubblicare qui nella speranza che qualcuno possa aiutare a spingere questo oltre il traguardo.
Forse non dovrei passare WebView . Forse un oggetto dall'interno WebView ?
L'altro metodo che ho provato, non il mio preferito, è quello di impostare questo nell'attività:
android:configChanges="keyboardHidden|orientation"
... e quindi non fare praticamente nulla qui:
@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.
}
Funziona, ma potrebbe non essere considerato una buona pratica .
Nel frattempo, ho anche un unico ImageView che voglio aggiornare automagicamente a seconda della rotazione. Questo risulta essere molto semplice. Sotto la mia rescartella, ho drawable-lande drawable-portper contenere le variazioni orizzontale / verticale, quindi uso R.drawable.myimagenameper l'origine di ImageView e Android "fa la cosa giusta" - yay!
... tranne quando osservi le modifiche alla configurazione, quindi non è così. :(
Quindi sono in contrasto. Usa onRetainNonConfigurationInstance e la rotazione di ImageView funziona, ma la persistenza di WebView non ... o usa onConfigurationChanged e WebView rimane stabile, ma il ImageView non si aggiorna. Cosa fare?
Un'ultima nota: nel mio caso, forzare l'orientamento non è un compromesso accettabile. Vogliamo davvero supportare con garbo la rotazione. Piace molto come fa l'app Browser Android! ;)
Un compromesso è quello di evitare la rotazione. Aggiungi questo per correggere l'attività solo per l'orientamento verticale.
android:screenOrientation="portrait"
Il modo migliore per gestire i cambiamenti di orientamento e impedire il ricaricamento di WebView su Ruota.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Con questo in mente, per evitare che onCreate () venga chiamato ogni volta che cambi orientamento, dovresti aggiungere android:configChanges="orientation|screenSize" to the AndroidManifest.
o solo ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Basta scrivere le seguenti righe di codice nel file manifest - nient'altro. Funziona veramente:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Apprezzo che sia un po 'tardi, tuttavia questa è la risposta che ho usato durante lo sviluppo della mia soluzione:
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);
}
}
È il 2015 e molte persone sono alla ricerca di una soluzione che funzioni ancora su telefoni Jellybean, KK e Lollipop. Dopo molte lotte ho trovato un modo per preservare intatta la visualizzazione web dopo aver cambiato orientamento. La mia strategia è fondamentalmente di memorizzare la visualizzazione Web in una variabile statica separata in un'altra classe. Quindi, se si verifica la rotazione, ricollego la visualizzazione Web dall'attività, attendo il completamento dell'orientamento e ricollego la visualizzazione Web all'attività. Ad esempio ... prima metti questo sul tuo MANIFEST (tastiera nascosta e tastiera sono opzionali):
<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>
In una CLASSE DI APPLICAZIONE SEPARATA, inserisci:
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();
}
}
Nell'ATTIVITÀ inserire:
@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);
}
Infine, il semplice 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>
Esistono diverse cose che potrebbero essere migliorate nella mia soluzione, ma ho già speso molto tempo, ad esempio: un modo più breve per convalidare se l'attività è stata avviata per la prima volta invece di utilizzare l'archiviazione SharedPreferences. Questo approccio mantiene intatta la tua visualizzazione web (afaik), le sue caselle di testo, le etichette, l'interfaccia utente, le variabili javascript e gli stati di navigazione che non sono riflessi dall'URL.
Aggiornamento: la strategia attuale è quella di spostare l'istanza di WebView nella classe Applicazione invece del frammento mantenuto quando viene staccato e ricollegarsi al riavvio come fa Josh. Per impedire la chiusura dell'applicazione, è necessario utilizzare il servizio di primo piano, se si desidera mantenere lo stato quando l'utente passa da un'applicazione all'altra.
Se si utilizzano frammenti, è possibile utilizzare l'istanza di conservazione di WebView. La vista Web verrà mantenuta come membro di istanza della classe. Tuttavia, è necessario collegare la vista Web in OnCreateView e scollegarla prima di OnDestroyView per impedirne la distruzione con il contenitore padre.
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();
}
}
I crediti PS vanno alla risposta di kcoppock
Per quanto riguarda 'SaveState ()' non funziona più secondo la documentazione ufficiale :
Si noti che questo metodo non memorizza più i dati di visualizzazione per questo WebView. Il comportamento precedente potrebbe potenzialmente perdere file se restoreState (Bundle) non è mai stato chiamato.
setRetainInstanceconserva il frammento la vista (e quindi il WebView) viene comunque distrutta.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Questi metodi possono essere sovrascritti su qualsiasi attività, in pratica ti permettono semplicemente di salvare e ripristinare i valori ogni volta che un'attività viene creata / distrutta, quando l'orientamento dello schermo cambia l'attività viene distrutta e ricreata in background, quindi puoi usare questi metodi per memorizzare temporaneamente / ripristinare gli stati durante la modifica.
Dovresti dare uno sguardo più approfondito ai due seguenti metodi e vedere se si adatta alla tua soluzione.
http://developer.android.com/reference/android/app/Activity.html
La soluzione migliore che ho trovato per fare questo senza perdere il Activityriferimento precedente e senza impostare il configChanges.. è usare un MutableContextWrapper .
L'ho implementato qui: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Questa è l'unica cosa che ha funzionato per me (ho anche usato lo stato dell'istanza di salvataggio onCreateViewma non era così affidabile).
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;
}
}
Ho creato un supporto Singleton per lo stato di WebView. Lo stato viene conservato finché esiste il processo dell'applicazione.
EDIT: Non loadDataWithBaseURLera necessario, ha funzionato altrettanto bene 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());
}
Anche se ho letto questo non funziona necessariamente bene con i cookie.
prova questo
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;
}
}
}
Questa pagina risolve il mio problema ma devo apportare una leggera modifica a quella iniziale:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Questa parte ha un leggero problema per me questo. Al secondo orientamento modificare l'applicazione terminata con puntatore null
usando questo ha funzionato per me:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Dovresti provare questo:
onServiceConnectedmetodo, ottenere WebView e chiamare il setContentViewmetodo per eseguire il rendering di WebView.L'ho provato e funziona ma non con altre visualizzazioni Web come 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);
}
Mi piace questa soluzione http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Secondo esso riutilizziamo la stessa istanza di WebView. Permette di salvare la cronologia di navigazione e la posizione di scorrimento al cambio di configurazione.