Das Problem ist die Leistung nach der Rotation. Das WebView muss die Seite neu laden, was etwas mühsam sein kann.
Was ist der beste Weg, um eine Orientierungsänderung zu handhaben, ohne die Seite jedes Mal von der Quelle neu zu laden?
Das Problem ist die Leistung nach der Rotation. Das WebView muss die Seite neu laden, was etwas mühsam sein kann.
Was ist der beste Weg, um eine Orientierungsänderung zu handhaben, ohne die Seite jedes Mal von der Quelle neu zu laden?
Antworten:
Wenn Sie nicht möchten, dass WebView bei Änderungen der Ausrichtung neu geladen wird, überschreiben Sie einfach onConfigurationChanged in Ihrer Aktivitätsklasse:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Und setzen Sie das Attribut android: configChanges im Manifest:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
Weitere Informationen finden Sie unter:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
Attribut der Unterklasse hinzugefügt. Aktivität 2) Wenn Ihre App von mehreren Projekten abhängt, wird das configChanges
Attribut dem Manifest des Projekts am hinzugefügt oben im Abhängigkeitsbaum (möglicherweise nicht das Projekt, das die Aktivitätsklasse enthält).
onConfigurationChanged
Methodenüberschreibung ist nutzlos.
Bearbeiten: Diese Methode funktioniert nicht mehr wie in den Dokumenten angegeben
Ursprüngliche Antwort:
Dies kann durch Überschreiben onSaveInstanceState(Bundle outState)
Ihrer Aktivität und Aufrufen saveState
aus der Webansicht erfolgen:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Stellen Sie dies dann in Ihrem onCreate wieder her, nachdem die Webansicht natürlich wieder aufgeblasen wurde:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
Die beste Antwort darauf ist die folgende Android-Dokumentation, die hier zu finden ist. Grundsätzlich verhindert dies, dass Webview neu geladen wird:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Optional können Sie Anomalien (falls vorhanden) beheben, indem Sie onConfigurationChanged
die Aktivität überschreiben :
@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();
}
}
Ich habe versucht, onRetainNonConfigurationInstance zu verwenden (das WebView zurückzugeben ), es dann mit getLastNonConfigurationInstance während onCreate zurückzugewinnen und neu zuzuweisen .
Scheint noch nicht zu funktionieren. Ich kann nicht anders als zu denken, dass ich wirklich nah dran bin! Bisher bekomme ich stattdessen nur ein WebView mit leerem / weißem Hintergrund . Poste hier in der Hoffnung, dass jemand helfen kann, diesen über die Ziellinie zu schieben.
Vielleicht sollte ich das WebView nicht weitergeben . Möglicherweise ein Objekt aus dem WebView ?
Die andere Methode, die ich ausprobiert habe - nicht meine Lieblingsmethode - besteht darin, dies in der Aktivität festzulegen:
android:configChanges="keyboardHidden|orientation"
... und dann so ziemlich nichts hier machen:
@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.
}
DAS funktioniert, aber es kann nicht als Best Practice angesehen werden .
Mittlerweile habe ich auch eine einzelne ImageView , die ich je nach Rotation automatisch aktualisieren möchte. Dies stellt sich als sehr einfach heraus. Unter meinem res
Ordner habe drawable-land
und drawable-port
halte ich Quer- / Hochformatvarianten, die ich dann R.drawable.myimagename
für die ImageView verwende -Quelle und Android "macht das Richtige" - yay!
... außer wenn Sie auf Konfigurationsänderungen achten, dann nicht. :((
Also bin ich uneins. Verwenden Sie onRetainNonConfigurationInstance und die ImageView- Rotation funktioniert, aber die WebView- Persistenz funktioniert nicht ... oder verwenden Sie onConfigurationChanged und die WebView bleibt stabil, aber die ImageView wird nicht aktualisiert. Was ist zu tun?
Ein letzter Hinweis: In meinem Fall ist das Erzwingen der Orientierung kein akzeptabler Kompromiss. Wir möchten die Rotation wirklich elegant unterstützen. Ein bisschen wie die Android Browser App! ;)
Ein Kompromiss besteht darin, eine Rotation zu vermeiden. Fügen Sie dies hinzu, um die Aktivität nur für die Porträtausrichtung zu korrigieren.
android:screenOrientation="portrait"
Der beste Weg, um Orientierungsänderungen zu handhaben und das erneute Laden von WebView beim Drehen zu verhindern.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Um zu verhindern, dass onCreate () jedes Mal aufgerufen wird, wenn Sie die Ausrichtung ändern, müssen Sie hinzufügen android:configChanges="orientation|screenSize" to the AndroidManifest.
oder nur ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Schreiben Sie einfach die folgenden Codezeilen in Ihre Manifest-Datei - sonst nichts. Es funktioniert wirklich:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Ich schätze, dass dies etwas spät ist, aber dies ist die Antwort, die ich bei der Entwicklung meiner Lösung verwendet habe:
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 ist 2015 und viele Menschen suchen nach einer Lösung, die immer noch auf Jellybean-, KK- und Lollipop-Telefonen funktioniert. Nach vielem kämpfen , fand ich einen Weg , um die Webansicht intakt zu erhalten , nachdem Sie Orientierung ändern. Meine Strategie besteht im Wesentlichen darin, die Webansicht in einer separaten statischen Variablen in einer anderen Klasse zu speichern. Wenn dann eine Drehung auftritt, entferne ich die Webansicht von der Aktivität, warte, bis die Ausrichtung abgeschlossen ist, und füge die Webansicht wieder der Aktivität hinzu. Zum Beispiel ... setzen Sie dies zuerst auf Ihr MANIFEST (KeyboardHidden und Keyboard sind optional):
<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>
Geben Sie in einer SEPARATEN ANWENDUNGSKLASSE Folgendes ein:
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();
}
}
In die AKTIVITÄT setzen:
@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);
}
Zum Schluss das einfache 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>
Es gibt einige Dinge, die in meiner Lösung verbessert werden könnten, aber ich habe bereits zu viel Zeit aufgewendet, zum Beispiel: eine kürzere Methode, um zu überprüfen, ob die Aktivität zum ersten Mal gestartet wurde, anstatt den SharedPreferences-Speicher zu verwenden. Bei diesem Ansatz bleiben die Webansicht (afaik), ihre Textfelder, Beschriftungen, Benutzeroberflächen, Javascript-Variablen und Navigationszustände erhalten, die nicht in der URL wiedergegeben werden.
Update: Die aktuelle Strategie besteht darin, die WebView-Instanz in die Anwendungsklasse zu verschieben, anstatt das Fragment beizubehalten, wenn es getrennt wird, und beim Fortsetzen erneut anzuhängen, wie es Josh tut. Um zu verhindern, dass die Anwendung geschlossen wird, sollten Sie den Vordergrunddienst verwenden, wenn Sie den Status beibehalten möchten, wenn der Benutzer zwischen Anwendungen wechselt.
Wenn Sie Fragmente verwenden, können Sie die Instanz von WebView beibehalten verwenden. Die Webansicht bleibt als Instanzmitglied der Klasse erhalten. Sie sollten jedoch die Webansicht in OnCreateView anhängen und vor OnDestroyView trennen, um zu verhindern, dass sie mit dem übergeordneten Container zerstört wird.
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();
}
}
PS Credits gehen an kcoppock Antwort
'SaveState ()' funktioniert laut offizieller Dokumentation nicht mehr :
Bitte beachten Sie, dass diese Methode die Anzeigedaten für diese WebView nicht mehr speichert. Das vorherige Verhalten kann möglicherweise Dateien verlieren, wenn restoreState (Bundle) nie aufgerufen wurde.
setRetainInstance
die Ansicht (und damit die WebView) weiterhin zerstört wird , während das Fragment beibehalten wird.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Diese Methoden können für jede Aktivität überschrieben werden. Sie können lediglich Werte speichern und wiederherstellen, wenn eine Aktivität erstellt / zerstört wird. Wenn sich die Bildschirmausrichtung ändert, wird die Aktivität zerstört und im Hintergrund neu erstellt. Daher können Sie diese Methoden verwenden während der Änderung Zustände vorübergehend speichern / wiederherstellen.
Sie sollten sich die beiden folgenden Methoden genauer ansehen und prüfen, ob sie zu Ihrer Lösung passen.
http://developer.android.com/reference/android/app/Activity.html
Die beste Lösung, die ich gefunden habe, um dies zu tun, ohne die vorherige Activity
Referenz configChanges
zu verlieren und ohne das .. zu setzen, ist die Verwendung eines MutableContextWrapper .
Ich habe dies hier implementiert: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Dies ist das einzige, was für mich funktioniert hat (ich habe sogar den Status "Instanz speichern" verwendet, onCreateView
aber er war nicht so zuverlässig).
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;
}
}
Ich habe einen Singleton-Halter für den Status von WebView erstellt. Der Status bleibt erhalten, solange der Prozess der Anwendung vorhanden ist.
EDIT: Das loadDataWithBaseURL
war nicht nötig, es hat genauso gut mit just funktioniert
//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());
}
Obwohl ich gelesen habe, funktioniert dies nicht unbedingt gut mit Cookies.
Versuche dies
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;
}
}
}
Diese Seite löst mein Problem, aber ich muss die erste geringfügig ändern:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Dieser Teil hat für mich ein kleines Problem. Bei der zweiten Ausrichtung wird die Anwendung mit dem Nullzeiger beendet
Damit hat es bei mir funktioniert:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Sie sollten dies versuchen:
onServiceConnected
Verfahren, das WebView zu erhalten und die ruft setContentView
Methode , um Ihre WebView zu machen.Ich habe es getestet und es funktioniert, aber nicht mit anderen WebViews wie XWalkView oder 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);
}
Ich mag diese Lösung http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Demnach verwenden wir dieselbe Instanz von WebView wieder. Es ermöglicht das Speichern des Navigationsverlaufs und des Bildlaufs bei Konfigurationsänderungen.