Проблема полягає в продуктивності після обертання. WebView повинен перезавантажити сторінку, що може бути трохи нудно.
Який найкращий спосіб керувати зміною орієнтації, не оновлюючи сторінку з джерела кожного разу?
Проблема полягає в продуктивності після обертання. WebView повинен перезавантажити сторінку, що може бути трохи нудно.
Який найкращий спосіб керувати зміною орієнтації, не оновлюючи сторінку з джерела кожного разу?
Відповіді:
Якщо ви не хочете, щоб WebView перезавантажував зміни орієнтації, просто замініть onConfigurationChanged у вашому класі активності:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
І встановіть атрибут android: configChanges в маніфесті:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
Для отримання додаткової інформації див:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
атрибут додається до підкласу Активність 2) Якщо ваш додаток залежить від декількох проектів, configChanges
атрибут додається до маніфесту цього в вгорі дерева залежностей (що може бути не проектом, що містить клас діяльності).
onConfigurationChanged
спосіб перевизначення марний.
Редагувати: Цей метод більше не працює, як зазначено в документах
Оригінальна відповідь:
Це можна вирішити, переписавши onSaveInstanceState(Bundle outState)
свою діяльність та зателефонувавши saveState
з веб-перегляду:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Потім відновіть це у своєму режимі onCreate після того, як веб-перегляд буде знову надуто, звичайно:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
Найкраща відповідь на це - наступна документація на Android, яку ви знайдете тут. В основному це не дозволить переглядати веб-перегляд:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Необов’язково , ви можете виправити аномалії (якщо такі є), змінивши onConfigurationChanged
активність:
@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();
}
}
Я спробував використовувати onRetainNonConfigurationInstance (повернення WebView ), а потім отримати його назад з getLastNonConfigurationInstance в протягом OnCreate і повторного призначення його.
Здається, це ще не працює. Я не можу допомогти, але думаю, що я справді близько! Поки що я просто отримую натомість порожній / білий фон WebView . Опублікуйте тут з надією, що хтось може допомогти просунути цей повз фінішну лінію.
Можливо, я не повинен проходити WebView . Можливо, об’єкт зсередини WebView ?
Інший метод, який я спробував - не мій улюблений - це встановити це в діяльності:
android:configChanges="keyboardHidden|orientation"
... а потім нічого не робимо тут:
@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.
}
ЦЕ працює, але це може не вважатися найкращою практикою .
Тим часом у мене також є єдиний ImageView, який я хочу автоматично оновлювати залежно від обертання. Це виявляється дуже просто. У моїй res
папці я маю drawable-land
і drawable-port
зберігати варіанти пейзажу / портрета, а потім використовую R.drawable.myimagename
для ImageView «s джерела і Android" робить правильно "- яй!
... за винятком випадків, коли ви спостерігаєте за змінами конфігурації, це не відбувається. :(
Тож я розбіжуся. Використовуйте OnRetainNonConfigurationInstance та обертання ImageView працює, але наполегливість WebView не працює ... або використовуйте onConfigurationChanged і WebView залишається стабільним, але ImageView не оновлюється. Що робити?
Останнє зауваження: У моєму випадку примусовий орієнтація не є прийнятним компромісом. Ми дуже хочемо витончено підтримувати обертання. Начебто подобається, як робить додаток Android Browser! ;)
Один компроміс - уникати обертання. Додайте це, щоб виправити активність лише для орієнтації на портрет.
android:screenOrientation="portrait"
Найкращий спосіб впоратися зі змінами орієнтації та запобігти перезавантаженню WebView під час повороту.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Зважаючи на це, щоб запобігти виклику onCreate () щоразу, коли ви змінюєте орієнтацію, вам доведеться додати android:configChanges="orientation|screenSize" to the AndroidManifest.
або просто ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Просто запишіть у свій файл Manifest наступні рядки коду - нічого іншого. Це дійсно працює:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Я вдячний, що це трохи пізно, але це відповідь, яку я використав при розробці свого рішення:
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);
}
}
Ви можете спробувати скористатись onSaveInstanceState()
і onRestoreInstanceState()
в розділі "Діяльність" для виклику saveState(...)
та restoreState(...)
у своєму екземплярі WebView
Настає 2015 рік, і багато людей шукають рішення, яке все ще працює на телефонах Jellybean, KK та Lollipop. Після великої боротьби я знайшов спосіб зберегти веб-перегляд недоторканим після зміни орієнтації. Моя стратегія полягає в основному для зберігання веб-перегляду в окремій статичній змінній в іншому класі. Потім, якщо відбувається обертання, я відтягую веб-перегляд від активності, чекаю, поки орієнтація закінчиться, і повторно додаю веб-перегляд до активності. Наприклад ... спочатку покладіть це на свій MANIFEST (клавіатураHidden та клавіатура не є обов'язковою):
<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>
В ОДНОГО КЛАСУ ЗАЯВКИ поставте:
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();
}
}
В ДІЯЛЬНІСТЬ поставте:
@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);
}
Нарешті, простий 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>
Є кілька речей, які можна було б покращити в моєму рішенні, але я вже витратив багато часу, наприклад: коротший спосіб перевірити, якщо Діяльність була запущена вперше, замість того, щоб використовувати сховище SharedPreferences. Цей підхід забезпечує збереження веб-перегляду недоторканим (afaik), його текстовими полями, мітками, інтерфейсом користувача, змінними javascript та навігаційними станами, які не відображаються URL-адресою.
Оновлення: поточна стратегія полягає в переміщенні екземпляра WebView до класу Application замість збереженого фрагмента, коли він від'єднується та повторно додається до резюме, як це робить Джош. Щоб запобігти закриттю програми, вам слід скористатися послугою переднього плану, якщо ви бажаєте зберегти стан, коли користувач перемикається між програмами.
Якщо ви використовуєте фрагменти, ви можете використовувати збережений екземпляр WebView. Перегляд веб-сторінки буде збережено як член екземпляра класу. Однак вам слід приєднати веб-перегляд у OnCreateView та від'єднати його перед OnDestroyView, щоб запобігти його знищенню з батьківським контейнером.
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 Кредити переходять до kcoppock відповіді
Що стосується "SaveState ()", він більше не працює відповідно до офіційної документації :
Зверніть увагу, що цей спосіб більше не зберігає відображувані дані для цього WebView. Попередня поведінка може потенційно просочувати файли, якщо RestoState (Bundle) ніколи не викликався.
setRetainInstance
зберігає фрагмент, подання (а отже, і WebView) все ще руйнується.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Ці методи можуть бути замінені для будь-якої діяльності, вона в основному дозволяє зберігати та відновлювати значення щоразу, коли діяльність створюється / знищується, коли орієнтація екрана змінює активність, знищується та відтворюється у фоновому режимі, тому ви можете використовувати ці методи тимчасово зберігати / відновлювати стани під час зміни.
Ви повинні глибше вивчити два наступні способи і побачити, чи відповідає вашому рішенню.
http://developer.android.com/reference/android/app/Activity.html
Найкраще рішення, яке я знайшов, щоб зробити це без витоку попередньої Activity
посилання та без встановлення configChanges
.., - це використовувати MutableContextWrapper .
Я реалізував це тут: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java
Це єдине, що працювало для мене (я навіть використовував стан екземпляра збереження, onCreateView
але це було не так надійно).
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;
}
}
Я зробив власник Singleton для стану WebView. Держава зберігається до тих пір, поки існує процес подання заявки.
EDIT: Це loadDataWithBaseURL
було не потрібно, воно працювало так само добре
//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());
}
Хоча я читаю, це не обов'язково добре працює з файлами cookie.
спробуйте це
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;
}
}
}
Ця сторінка вирішує мою проблему, але я повинен змінити початкові:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Ця порція має для мене невелику проблему. На другу зміну орієнтації додаток закінчується нульовим покажчиком
використовуючи це, це працювало для мене:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Спробуйте це:
onServiceConnected
методі знайдіть WebView і зателефонуйте до setContentView
методу для надання веб-перегляду.Я перевірив це, і він працює, але не з іншими WebView, такими як XWalkView або 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);
}
Мені подобається це рішення http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Згідно з цим ми повторно використовуємо той самий екземпляр WebView. Це дозволяє зберегти історію навігації та положення прокрутки при зміні конфігурації.