Vấn đề là hiệu suất sau khi luân chuyển. WebView phải tải lại trang, có thể hơi tẻ nhạt.
Cách tốt nhất để xử lý thay đổi định hướng mà không cần tải lại trang từ nguồn mỗi lần?
Vấn đề là hiệu suất sau khi luân chuyển. WebView phải tải lại trang, có thể hơi tẻ nhạt.
Cách tốt nhất để xử lý thay đổi định hướng mà không cần tải lại trang từ nguồn mỗi lần?
Câu trả lời:
Nếu bạn không muốn WebView tải lại các thay đổi định hướng, chỉ cần ghi đè onConfigurationChanged trong lớp Activity của bạn:
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Và đặt thuộc tính android: configChanges trong tệp kê khai:
<activity android:name="..."
android:label="@string/appName"
android:configChanges="orientation|screenSize"
để biết thêm thông tin, hãy xem:
http://developer.android.com/guide/topics/resource/r nb-changes.html#HandlingTheChange
https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges
configChanges
thuộc tính sẽ được thêm vào lớp con Hoạt động 2) Nếu ứng dụng của bạn phụ thuộc vào nhiều dự án, configChanges
thuộc tính sẽ được thêm vào tệp kê khai của một tại đầu cây phụ thuộc (có thể không phải là dự án chứa lớp Activity).
onConfigurationChanged
đè phương pháp như vậy là vô ích.
Chỉnh sửa: Phương pháp này không còn hoạt động như đã nêu trong các tài liệu
Câu trả lời gốc:
Điều này có thể được xử lý bằng cách ghi đè onSaveInstanceState(Bundle outState)
trong hoạt động của bạn và gọi saveState
từ webview:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Sau đó phục hồi điều này trong onCreate của bạn sau khi webview đã được bơm lại tất nhiên:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.blah);
if (savedInstanceState != null)
((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
Câu trả lời tốt nhất cho vấn đề này là theo tài liệu Android được tìm thấy ở đây Về cơ bản, điều này sẽ ngăn Webview tải lại:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
android:label="@string/app_name">
Tùy chọn , bạn có thể khắc phục sự bất thường (nếu có) bằng cách ghi đè onConfigurationChanged
trong hoạt động:
@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();
}
}
Tôi đã thử sử dụng onRetainNonConfigurationInstance (trả lại WebView ), sau đó lấy lại bằng getLastNonConfigurationInstance trong khi onCreate và gán lại nó.
Có vẻ như chưa hoạt động. Tôi không thể giúp nhưng nghĩ rằng tôi thực sự gần gũi! Cho đến nay, tôi chỉ nhận được một WebView trống / nền trắng . Đăng ở đây với hy vọng ai đó có thể giúp đẩy cái này qua vạch đích.
Có lẽ tôi không nên vượt qua WebView . Có lẽ một đối tượng từ bên trong WebView ?
Phương pháp khác tôi đã thử - không phải sở thích của tôi - là đặt phương thức này trong hoạt động:
android:configChanges="keyboardHidden|orientation"
... và sau đó không làm gì nhiều ở đây:
@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.
}
THAT hoạt động, nhưng nó có thể không được coi là một thực tiễn tốt nhất .
Trong khi đó, tôi cũng có một ImageView duy nhất mà tôi muốn cập nhật tự động tùy theo vòng quay. Điều này hóa ra rất dễ dàng. Trong res
thư mục của tôi , tôi có drawable-land
và drawable-port
giữ các biến thể ngang / R.drawable.myimagename
dọc , sau đó tôi sử dụng cho nguồn của ImageView và Android "thực hiện đúng" - yay!
... ngoại trừ khi bạn xem các thay đổi cấu hình, thì không. :
Vì vậy, tôi đang bất hòa. Sử dụng onRetainNonConfigurationInstance và xoay ImageView hoạt động, nhưng tính bền vững của WebView không ... hoặc sử dụng onConfigurationChanged và WebView vẫn ổn định, nhưng ImageView không cập nhật. Phải làm sao
Một lưu ý cuối cùng: Trong trường hợp của tôi, việc ép buộc định hướng không phải là một sự thỏa hiệp chấp nhận được. Chúng tôi thực sự muốn duyên dáng hỗ trợ luân chuyển. Giống như cách ứng dụng Trình duyệt Android làm! ;)
Cách tốt nhất để xử lý các thay đổi định hướng và Ngăn chặn tải lại WebView trên Xoay.
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
Với ý nghĩ đó, để ngăn chặn onCreate () được gọi mỗi khi bạn thay đổi hướng, bạn sẽ phải thêm android:configChanges="orientation|screenSize" to the AndroidManifest.
hoặc chỉ ..
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Chỉ cần viết các dòng mã sau vào tệp Manifest của bạn - không có gì khác. Nó thật sự có hiệu quả:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
Tôi đánh giá cao việc này hơi muộn, tuy nhiên đây là câu trả lời mà tôi đã sử dụng khi phát triển giải pháp của mình:
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);
}
}
Đó là năm 2015, và nhiều người đang tìm kiếm một giải pháp vẫn còn hiệu quả trên điện thoại Jellybean, KK và Lollipop. Sau nhiều lần vật lộn, tôi đã tìm ra cách giữ gìn webview nguyên vẹn sau khi bạn thay đổi định hướng. Chiến lược của tôi về cơ bản là lưu trữ webview trong một biến tĩnh riêng biệt trong một lớp khác. Sau đó, nếu xoay vòng xảy ra, tôi bỏ webview khỏi hoạt động, đợi cho hướng kết thúc và gắn lại webview trở lại hoạt động. Ví dụ ... trước tiên hãy đặt cái này lên MANIFEST của bạn (bàn phím và bàn phím là tùy chọn):
<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>
Trong một nhóm ứng dụng SEPARATE, đặt:
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();
}
}
Trong HOẠT ĐỘNG đặt:
@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);
}
Cuối cùng, XML đơn giản:
<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>
Có một số điều có thể được cải thiện trong giải pháp của tôi, nhưng tôi đã dành nhiều thời gian, ví dụ: một cách ngắn hơn để xác thực nếu Hoạt động được khởi chạy lần đầu tiên thay vì sử dụng lưu trữ SharedPreferences. Cách tiếp cận này bảo vệ nguyên vẹn webview của bạn (afaik), hộp văn bản, nhãn, giao diện người dùng, biến javascript và trạng thái điều hướng không được phản ánh bởi url.
Cập nhật: chiến lược hiện tại là chuyển phiên bản WebView sang lớp Ứng dụng thay vì giữ lại đoạn khi nó tách ra và gắn lại vào sơ yếu lý lịch như Josh làm. Để ngăn Ứng dụng đóng, bạn nên sử dụng dịch vụ nền trước, nếu bạn muốn giữ trạng thái khi người dùng chuyển đổi giữa các ứng dụng.
Nếu bạn đang sử dụng các đoạn, bạn có thể sử dụng phiên bản giữ lại của WebView. Chế độ xem web sẽ được giữ lại làm thành viên thể hiện của lớp. Tuy nhiên, bạn nên đính kèm chế độ xem web trong OnCreateView và tách ra trước OnDestroyView để ngăn chặn nó khỏi bị phá hủy với vùng chứa chính.
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();
}
}
Tín dụng PS đi đến câu trả lời kcoppock
Đối với 'SaveState ()', nó không còn hoạt động theo tài liệu chính thức :
Xin lưu ý rằng phương pháp này không còn lưu trữ dữ liệu hiển thị cho WebView này. Hành vi trước đó có khả năng rò rỉ các tệp nếu restoreState (Gói) không bao giờ được gọi.
setRetainInstance
vẫn giữ Fragment, nhưng khung nhìn (và do đó WebView) vẫn bị phá hủy.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
Các phương thức này có thể được ghi đè trên bất kỳ hoạt động nào, về cơ bản nó chỉ cho phép bạn lưu và khôi phục các giá trị mỗi khi một hoạt động được tạo / hủy, khi hướng màn hình thay đổi, hoạt động bị hủy và được tạo lại trong nền, do đó bạn có thể sử dụng các phương thức này để lưu trữ tạm thời / khôi phục trạng thái trong quá trình thay đổi.
Bạn nên có cái nhìn sâu hơn về hai phương pháp sau và xem liệu nó có phù hợp với giải pháp của bạn không.
http://developer.android.com/reference/android/app/Activity.html
Giải pháp tốt nhất tôi đã tìm thấy để làm điều này mà không làm rò rỉ Activity
tham chiếu trước đó và không cần thiết lập configChanges
.. là sử dụng MutableContextWrapper .
Tôi đã thực hiện điều này tại đây: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/web
Đây là điều duy nhất phù hợp với tôi (tôi thậm chí đã sử dụng trạng thái lưu trong trường hợp onCreateView
nhưng nó không đáng tin cậy).
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;
}
}
Tôi đã tạo một chủ sở hữu Singleton cho trạng thái của WebView. Nhà nước được bảo tồn miễn là quá trình của ứng dụng tồn tại.
EDIT: loadDataWithBaseURL
Không cần thiết, nó cũng hoạt động tốt với chỉ
//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());
}
Mặc dù tôi đọc cái này không nhất thiết phải hoạt động tốt với cookie.
thử cái này
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;
}
}
}
Trang này giải quyết vấn đề của tôi nhưng tôi phải thay đổi một chút trong lần đầu tiên:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
Phần này có một vấn đề nhỏ đối với tôi này. Trên hướng thứ hai thay đổi, ứng dụng kết thúc bằng con trỏ null
sử dụng cái này nó làm việc cho tôi:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
Bạn nên thử điều này:
onServiceConnected
phương thức, lấy WebView và gọi setContentView
phương thức để hiển thị WebView của bạn.Tôi đã thử nghiệm nó và nó hoạt động nhưng không hoạt động với các WebView khác như XWalkView hoặc 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);
}
Tôi thích giải pháp này http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/
Theo nó, chúng tôi sử dụng lại ví dụ tương tự của WebView. Nó cho phép lưu lịch sử điều hướng và vị trí cuộn khi thay đổi cấu hình.