Sorun rotasyonu izleyen performanstır. WebView, sayfayı yeniden yüklemelidir, bu biraz sıkıcı olabilir.
Her seferinde sayfayı kaynaktan yeniden yüklemeden bir yön değişikliğini işlemenin en iyi yolu nedir?
Sorun rotasyonu izleyen performanstır. WebView, sayfayı yeniden yüklemelidir, bu biraz sıkıcı olabilir.
Her seferinde sayfayı kaynaktan yeniden yüklemeden bir yön değişikliğini işlemenin en iyi yolu nedir?
Yanıtlar:
Web Görünümü'nün yön değişikliklerinde yeniden yüklenmesini istemiyorsanız, Activity sınıfınızda onConfigurationChanged öğesini geçersiz kılmanız yeterlidir:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Ve manifest'te android: configChanges özniteliğini ayarlayın:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
daha fazla bilgi için bkz:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
özellik alt sınıfa eklenir Aktivite 2) Uygulamanız birden çok projeye bağlıysa, configChanges
öznitelik şuradaki birinin bildirimine eklenir. bağımlılık ağacının tepesi (Activity sınıfını içeren proje olmayabilir).
onConfigurationChanged
yöntem geçersiz kılma işe yaramaz.
Düzenleme: Bu yöntem artık belgelerde belirtildiği gibi çalışmıyor
Orijinal cevap:
Bu, onSaveInstanceState(Bundle outState)
etkinliğinizin üzerine yazarak saveState
ve web görünümünden arayarak çözülebilir :
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Elbette web görünümü yeniden şişirildikten sonra bunu onCreate içinde kurtarın:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
Buna en iyi cevap, burada bulunan Android belgelerini takip etmektir Temel olarak bu, Webview'un yeniden yüklenmesini engelleyecektir:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
İsteğe bağlı olarak , onConfigurationChanged
etkinliği geçersiz kılarak anormallikleri (varsa) düzeltebilirsiniz :
@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();
}
}
Ben kullanarak denedim onRetainNonConfigurationInstance (dönen WebView'ı sonra tekrar onu almak,) getLastNonConfigurationInstance sırasında onCreate ve onu yeniden atama.
Henüz işe yaramıyor gibi görünüyor. Yardım edemem ama gerçekten yakın olduğumu düşünüyorum! Şimdiye kadar, boş / beyaz arka planlı bir Web Görünümü görüyorum bunun yerine . Birinin bunu bitiş çizgisini geçmeye yardımcı olabileceği umuduyla buraya yazıyorum.
Belki Web Görünümü'nü geçmemeliyim . Belki WebView içinden bir nesne ?
En sevdiğim değil denediğim diğer yöntem, bunu etkinlikte ayarlamak:
android:configChanges="keyboardHidden|orientation"
... ve sonra burada hemen hemen hiçbir şey yapmayın:
@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.
}
BU işe yarıyor, ancak bir en iyi uygulama olarak kabul edilmeyebilir .
Bu arada, rotasyona bağlı olarak otomatik olarak güncellemek istediğim tek bir ImageView de var . Bu çok kolay çıktı. res
Klasörümün altında , manzara / portre varyasyonlarına sahibim drawable-land
ve drawable-port
bunları tutmak R.drawable.myimagename
için, ImageView kaynağı için kullanıyorum ve Android "doğru olanı yapıyor" - yaşasın!
... yapılandırma değişikliklerini izlemeniz dışında, o zaman izlemez. :(
Bu yüzden anlaşmazlık içindeyim. OnRetainNonConfigurationInstance kullanın ve ImageView rotasyonu çalışır, ancak WebView kalıcılığı kullanılmaz ... veya onConfigurationChanged ve WebView sabit kalır, ancak ImageView güncellenmez. Ne yapalım?
Son bir not: Benim durumumda, yönelimi zorlamak kabul edilebilir bir uzlaşma değil. Gerçekten rotasyonu incelikle desteklemek istiyoruz. Android Tarayıcı uygulamasının yaptığı gibi! ;)
Yön değişikliklerini işlemenin ve Döndürme sırasında Web Görünümünün yeniden yüklenmesini engellemenin en iyi yolu.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Bunu akılda tutarak, onCreate () yönünü her değiştirdiğinizde çağrılmasını önlemek için, android:configChanges="orientation|screenSize" to the AndroidManifest.
ya da sadece ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Manifest dosyanıza aşağıdaki kod satırlarını yazın - başka hiçbir şey yazmayın. Gerçekten çalışıyor:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Bunun biraz geç olduğunu anlıyorum, ancak çözümümü geliştirirken kullandığım cevap şu:
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);
}
}
Yıl 2015 ve birçok kişi hala Jellybean, KK ve Lollipop telefonlarında çalışan bir çözüm arıyor. Çok uğraştıktan sonra , yönünüzü değiştirdikten sonra web görünümünü bozulmadan korumanın bir yolunu buldum. Benim stratejim temelde web görünümünü başka bir sınıfta ayrı bir statik değişkende saklamaktır. Ardından, döndürme meydana gelirse, web görünümünü etkinlikten ayırıyorum, yönlendirmenin bitmesini bekliyorum ve web görünümünü yeniden etkinliğe ekliyorum. Örneğin ... önce bunu MANIFEST'inize koyun (keyboardHidden ve klavye isteğe bağlıdır):
<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>
AYRI BAŞVURU SINIFINDA şunları koyun:
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();
}
}
ACTIVITY'de şunu koyun:
@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);
}
Son olarak, basit 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>
Çözümümde geliştirilebilecek birkaç şey var, ancak çok zaman harcadım, örneğin: Paylaşılan Tercihler depolamasını kullanmak yerine Aktivitenin ilk kez başlatılıp başlatılmadığını doğrulamanın daha kısa bir yolu. Bu yaklaşım, web görünümünüzü bozulmadan (afaik), metin kutularını, etiketlerini, kullanıcı arayüzünü, javascript değişkenlerini ve url tarafından yansıtılmayan gezinme durumlarını korur.
Güncelleme: mevcut strateji, WebView örneğini ayrıldığında parça tutulması yerine Uygulama sınıfına taşımak ve Josh'un yaptığı gibi devam ettirildiğinde yeniden iliştirmektir. Uygulamanın kapanmasını önlemek için, kullanıcı uygulamalar arasında geçiş yaptığında durumu korumak istiyorsanız, ön plan hizmetini kullanmalısınız.
Parçalar kullanıyorsanız, Web Görünümü'nün saklama örneğini kullanabilirsiniz. Web görünümü, sınıfın örnek üyesi olarak korunacaktır. Bununla birlikte, OnCreateView'e web görünümü eklemelisiniz ve onDestroyView'dan önce üst konteyner ile yok edilmesini önlemek için ayırmalısınız.
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 Kredileri kcoppock cevabına gider
'SaveState ()' için artık resmi belgelere göre çalışmamaktadır :
Lütfen bu yöntemin artık bu Web Görünümü için görüntü verilerini depolamadığını unutmayın. RestoreState (Bundle) hiçbir zaman çağrılmadıysa, önceki davranış potansiyel olarak dosyaları sızdırabilirdi.
setRetainInstance
, görünümün (ve dolayısıyla Web Görünümü'nün) hala yok edildiğini unutmayın.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Bu yöntemler herhangi bir etkinlikte geçersiz kılınabilir, temelde bir etkinlik her oluşturulduğunda / yok edildiğinde değerleri kaydetmenize ve geri yüklemenize izin verir, ekran yönü değiştiğinde etkinlik yok edilir ve arka planda yeniden oluşturulur, böylece bu yöntemleri kullanabilirsiniz. değişiklik sırasında geçici saklama / geri yükleme durumlarına.
Aşağıdaki iki yöntemi daha derinlemesine incelemeli ve çözümünüze uyup uymadığını görmelisiniz.
http://developer.android.com/reference/android/app/Activity.html
Bunu önceki Activity
referansı sızdırmadan ve configChanges
.. ayarlamadan yapmak için bulduğum en iyi çözüm bir MutableContextWrapper kullanmaktır .
Bunu burada uyguladım: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Benim için işe yarayan tek şey bu (kaydetme örneği durumunu bile kullandım onCreateView
ama o kadar güvenilir değildi).
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;
}
}
WebView durumu için bir Singleton sahibi yaptım. Durum, başvuru süreci devam ettiği sürece korunur.
DÜZENLEME: loadDataWithBaseURL
Gerekli değildi, sadece
//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());
}
Okumama rağmen, bu mutlaka çerezlerle iyi çalışmıyor.
bunu dene
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;
}
}
}
Bu sayfa sorunumu çözdü, ancak ilk başta küçük bir değişiklik yapmam gerekiyor:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Bu kısmın benim için ufak bir problemi var bu. İkinci yönelim değişikliğinde uygulama boş gösterici ile sonlandırıldı
bunu kullanarak benim için çalıştı:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Bunu denemelisin:
onServiceConnected
yöntemle, WebView'ı almak ve çağrı setContentView
sizin WebView'ı işlemek için yöntem.Test ettim ve çalışıyor ancak XWalkView veya GeckoView gibi diğer Web Görünümleri ile çalışmıyor.
@Override
protected void onSaveInstanceState(Bundle outState )
{
super.onSaveInstanceState(outState);
webView.saveState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
webView.restoreState(savedInstanceState);
}
Bu çözümü beğendim http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Buna göre, aynı WebView örneğini yeniden kullanıyoruz. Yapılandırma değişikliğinde gezinme geçmişini ve kaydırma konumunu kaydetmeye izin verir.