Masalahnya adalah kinerja setelah rotasi. WebView harus memuat ulang halaman, yang bisa sedikit membosankan.
Apa cara terbaik menangani perubahan orientasi tanpa memuat ulang halaman dari sumber setiap kali?
Masalahnya adalah kinerja setelah rotasi. WebView harus memuat ulang halaman, yang bisa sedikit membosankan.
Apa cara terbaik menangani perubahan orientasi tanpa memuat ulang halaman dari sumber setiap kali?
Jawaban:
Jika Anda tidak ingin WebView memuat ulang pada perubahan orientasi cukup timpa onConfigurationChanged di kelas Aktivitas Anda:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Dan atur atribut android: configChanges di manifes:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
untuk info lebih lanjut, lihat:
http://developer.android.com/guide/topics/resources/runtime-changes.html#MenanganiTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
atribut ditambahkan ke subclass Activity 2) Jika aplikasi Anda bergantung pada beberapa proyek, configChanges
atribut akan ditambahkan ke manifes yang ada di di atas pohon dependensi (yang mungkin bukan proyek yang mengandung kelas Activity).
onConfigurationChanged
metode override tidak berguna.
Sunting: Metode ini tidak lagi berfungsi sebagaimana dinyatakan dalam dokumen
Jawaban asli:
Ini dapat ditangani dengan menulis ulang onSaveInstanceState(Bundle outState)
dalam aktivitas Anda dan menelepon saveState
dari tampilan web:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Kemudian pulihkan ini di onCreate Anda setelah tampilan web dipompa kembali tentu saja:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
Jawaban terbaik untuk ini adalah mengikuti dokumentasi Android yang ditemukan di sini. Pada dasarnya ini akan mencegah Webview dari reload:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Secara opsional , Anda dapat memperbaiki anomali (jika ada) dengan mengesampingkan onConfigurationChanged
dalam aktivitas:
@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();
}
}
Saya sudah mencoba menggunakan onRetainNonConfigurationInstance (mengembalikan WebView ), kemudian mendapatkannya kembali dengan getLastNonConfigurationInstance selama onCreate dan menetapkan ulang.
Sepertinya belum berfungsi. Saya tidak dapat membantu tetapi berpikir saya benar-benar dekat! Sejauh ini, saya hanya mendapatkan WebView kosong / latar belakang putih sebagai gantinya. Posting di sini dengan harapan bahwa seseorang dapat membantu mendorong yang ini melewati garis finish.
Mungkin saya seharusnya tidak melewati WebView . Mungkin objek dari dalam WebView ?
Metode lain yang saya coba - bukan favorit saya - adalah mengaturnya di aktivitas:
android:configChanges="keyboardHidden|orientation"
... dan kemudian tidak melakukan apa-apa di sini:
@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.
}
ITU bekerja, tetapi itu mungkin tidak dianggap sebagai praktik terbaik .
Sementara itu, saya juga memiliki ImageView tunggal yang ingin saya perbarui secara otomatis tergantung pada rotasi. Ini ternyata sangat mudah. Di bawah res
folder saya , saya memiliki drawable-land
dan drawable-port
menahan variasi lanskap / potret, kemudian saya gunakan R.drawable.myimagename
untuk sumber ImageView dan Android "melakukan hal yang benar" - yay!
... kecuali ketika Anda melihat perubahan konfigurasi, maka tidak. :(
Jadi saya berselisih. Gunakan onRetainNonConfigurationInstance dan rotasi ImageView berfungsi, tetapi kegigihan WebView tidak ... atau gunakan onConfigurationChanged dan WebView tetap stabil, tetapi ImageView tidak memperbarui. Apa yang harus dilakukan?
Satu catatan terakhir: Dalam kasus saya, memaksakan orientasi bukanlah kompromi yang dapat diterima. Kami benar-benar ingin mendukung rotasi dengan anggun. Agak suka bagaimana aplikasi Browser Android tidak! ;)
Salah satu kompromi adalah menghindari rotasi. Tambahkan ini untuk memperbaiki aktivitas hanya untuk orientasi Potret.
android:screenOrientation="portrait"
Cara terbaik untuk menangani perubahan orientasi dan Mencegah pemuatan ulang WebView di Rotate.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Dengan mengingat hal itu, untuk mencegah onCreate () dipanggil setiap kali Anda mengubah orientasi, Anda harus menambahkan android:configChanges="orientation|screenSize" to the AndroidManifest.
atau hanya ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Cukup tulis baris kode berikut dalam file Manifest Anda - tidak ada yang lain. Ini benar-benar berfungsi:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Saya menghargai ini agak terlambat, namun ini adalah jawaban yang saya gunakan ketika mengembangkan solusi saya:
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);
}
}
Ini tahun 2015, dan banyak orang mencari solusi yang masih dapat digunakan pada ponsel Jellybean, KK dan Lollipop. Setelah banyak kesulitan saya menemukan cara untuk mempertahankan tampilan web utuh setelah Anda mengubah orientasi. Strategi saya pada dasarnya adalah untuk menyimpan tampilan web dalam variabel statis yang terpisah di kelas lain. Kemudian, jika rotasi terjadi, saya melepaskan tampilan web dari aktivitas, menunggu orientasi selesai, dan pasang kembali tampilan web kembali ke aktivitas. Misalnya ... pertama-tama letakkan ini di MANIFEST Anda (keyboardHidden dan keyboard adalah opsional):
<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>
Dalam KELAS APLIKASI TERPISAH, masukkan:
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();
}
}
Dalam ACTIVITY cantumkan:
@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);
}
Akhirnya, XML sederhana:
<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>
Ada beberapa hal yang dapat ditingkatkan dalam solusi saya, tetapi saya sudah menghabiskan banyak waktu, misalnya: cara yang lebih singkat untuk memvalidasi jika Kegiatan telah diluncurkan untuk pertama kalinya alih-alih menggunakan penyimpanan SharedPreferences. Pendekatan ini menjaga Anda tetap utuh tampilan web (afaik), kotak teks, label, UI, variabel javascript, dan negara navigasi yang tidak tercermin oleh url.
Pembaruan: strategi saat ini adalah untuk memindahkan instance WebView ke kelas Aplikasi alih-alih dipertahankan fragmen ketika dilepaskan dan pasang kembali pada resume seperti yang dilakukan Josh. Untuk mencegah aplikasi menutup, Anda harus menggunakan layanan latar depan, jika Anda ingin mempertahankan status ketika pengguna beralih di antara aplikasi.
Jika Anda menggunakan fragmen, Anda bisa menggunakan instance retensi dari WebView. Tampilan web akan dipertahankan sebagai anggota kelas contoh. Namun Anda harus melampirkan tampilan web di OnCreateView dan lepaskan sebelum OnDestroyView untuk mencegahnya rusak dengan wadah induk.
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();
}
}
Kredit PS pergi ke jawaban kcoppock
Adapun 'SaveState ()' tidak lagi berfungsi sesuai dengan dokumentasi resmi :
Harap perhatikan bahwa metode ini tidak lagi menyimpan data tampilan untuk WebView ini. Perilaku sebelumnya berpotensi bocor file jika restoreState (Bundle) tidak pernah dipanggil.
setRetainInstance
mempertahankan Fragmen tampilan (dan karenanya WebView) masih dihancurkan.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Metode ini dapat ditimpa pada aktivitas apa pun, itu hanya pada dasarnya memungkinkan Anda untuk menyimpan dan mengembalikan nilai setiap kali aktivitas dibuat / dihancurkan, ketika orientasi layar mengubah aktivitas akan dihancurkan dan diciptakan kembali di latar belakang, sehingga Anda dapat menggunakan metode ini untuk menyimpan sementara / mengembalikan negara selama perubahan.
Anda harus melihat lebih dalam pada dua metode berikut dan melihat apakah itu cocok dengan solusi Anda.
http://developer.android.com/reference/android/app/Activity.html
Solusi terbaik yang saya temukan untuk melakukan ini tanpa membocorkan Activity
referensi sebelumnya dan tanpa mengatur configChanges
.. adalah dengan menggunakan MutableContextWrapper .
Saya telah menerapkan ini di sini: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Ini adalah satu-satunya hal yang bekerja untuk saya (saya bahkan menggunakan save instance state onCreateView
tetapi tidak bisa diandalkan).
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;
}
}
Saya membuat pemegang Singleton untuk status WebView. Keadaan dipertahankan selama proses aplikasi ada.
EDIT: Itu loadDataWithBaseURL
tidak perlu, itu berfungsi dengan baik hanya dengan
//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());
}
Meskipun saya membaca ini tidak selalu berhasil dengan cookie.
coba ini
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;
}
}
}
Halaman ini menyelesaikan masalah saya, tetapi saya harus membuat sedikit perubahan di awal:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Bagian ini memiliki sedikit masalah bagi saya ini. Pada perubahan orientasi kedua aplikasi diakhiri dengan null pointer
menggunakan ini bekerja untuk saya:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Anda harus mencoba ini:
onServiceConnected
metode ini, dapatkan WebView dan panggil setContentView
metode tersebut untuk merender WebView Anda.Saya mengujinya dan berfungsi tetapi tidak dengan WebViews lain seperti XWalkView atau 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);
}
Saya suka solusi ini http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Menurutnya kami menggunakan kembali contoh WebView yang sama. Memungkinkan untuk menyimpan riwayat navigasi dan menggulir posisi pada perubahan konfigurasi.