Проблема заключается в производительности после ротации. WebView должен перезагрузить страницу, что может быть немного утомительно.
Каков наилучший способ обработки изменения ориентации без перезагрузки страницы из источника каждый раз?
Проблема заключается в производительности после ротации. WebView должен перезагрузить страницу, что может быть немного утомительно.
Каков наилучший способ обработки изменения ориентации без перезагрузки страницы из источника каждый раз?
Ответы:
Если вы не хотите, чтобы WebView перезагружался при изменении ориентации, просто переопределите onConfigurationChanged в своем классе Activity:
@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
атрибут добавляется в манифест одного из вершина дерева зависимостей (это может быть не проект, содержащий класс Activity).
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, найденная здесь. В основном это предотвратит перезагрузку Webview:
<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! ;)
Лучший способ обрабатывать изменения ориентации и предотвращать перезагрузку 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"`
Просто напишите следующие строки кода в вашем файле манифеста - больше ничего. Это действительно работает:
<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);
}
}
Это 2015 год, и многие люди ищут решение, которое все еще работает на телефонах Jellybean, KK и Lollipop. После долгих попыток я нашел способ сохранить веб-вид без изменений после изменения ориентации. Моя стратегия заключается в том, чтобы хранить веб-представление в отдельной статической переменной в другом классе. Затем, если происходит ротация, я отсоединяю веб-представление от действия, ожидаю завершения ориентации и снова присоединяю веб-представление к действию. Например ... сначала поместите это в ваш манифест (клавиатура скрытая и клавиатура необязательны):
<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>
В моем решении есть несколько вещей, которые можно улучшить, но я уже потратил много времени, например: более короткий способ проверки, был ли Activity запущен в первый раз вместо использования хранилища SharedPreferences. Этот подход сохраняет веб-просмотр без изменений (afaik), его текстовые поля, метки, пользовательский интерфейс, переменные javascript и состояния навигации, которые не отражаются в URL.
Обновление: текущая стратегия состоит в том, чтобы переместить экземпляр WebView в класс приложения вместо сохраненного фрагмента, когда он отсоединен, и снова присоединить его к резюме, как это делает Джош. Чтобы предотвратить закрытие приложения, вы должны использовать службу переднего плана, если вы хотите сохранить состояние, когда пользователь переключается между приложениями.
Если вы используете фрагменты, вы можете использовать сохранить экземпляр 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. Предыдущее поведение может привести к утечке файлов, если restoreState (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. Состояние сохраняется до тех пор, пока существует процесс приложения.
РЕДАКТИРОВАТЬ: 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());
}
Хотя я читаю, это не обязательно хорошо работает с куки.
попробуй это
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.Я протестировал его, и он работает, но не с другими WebViews, такими как 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. Это позволяет сохранить историю навигации и положение прокрутки при изменении конфигурации.