Android WebView: การจัดการการเปลี่ยนแปลงการวางแนว


147

ปัญหาคือประสิทธิภาพการหมุนตาม WebView จะต้องโหลดหน้าซ้ำซึ่งอาจเป็นเรื่องน่าเบื่อ

วิธีที่ดีที่สุดในการจัดการการเปลี่ยนแปลงการวางแนวโดยไม่ต้องโหลดหน้าซ้ำจากแหล่งที่มาในแต่ละครั้งคืออะไร


3
การ "โหลดซ้ำจากแหล่งที่มา" หมายความว่าเป็นการดาวน์โหลดหน้าอีกครั้งหรือเพียงแค่แสดงผลอีกครั้ง
Jeremy Logan

โซลูชันที่อ้างอิงโดย Blackhex แก้ไขปัญหาของฉันได้
Italo Borssatto

6
ฉันสงสัยว่านรกที่ให้พิเศษเกี่ยวกับ app ของฉันที่ไม่มีของเหล่านี้ทำงานคำตอบ ...
RobinJ

คำตอบ:


91

หากคุณไม่ต้องการให้ WebView โหลดการเปลี่ยนแปลงการวางแนวใหม่ก็ให้แทนที่ onConfigurationChanged ในคลาสกิจกรรมของคุณ:

@Override
public void onConfigurationChanged(Configuration newConfig){        
    super.onConfigurationChanged(newConfig);
}

และตั้งค่าคุณสมบัติ android: configChanges ในไฟล์ Manifest:

<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


4
ฉันทดสอบ แต่พบว่าใช้งานไม่ได้ตั้งค่าแอตทริบิวต์ configChanges เหมือนด้านล่างจะใช้งานได้ android: configChanges = "keyboardHidden | ปฐมนิเทศ"
virsir

18
การทำเช่นนั้นเป็นเรื่องท้อแท้จริง ๆ ดังที่ได้กล่าวไว้หลายครั้งก่อน
แมทเธียสช

3
แมทเทียส: นี่ไม่จริงเลย - ดู Javadoc สำหรับ WebView
krtek

สองสิ่งที่ควรทราบเกี่ยวกับการแก้ปัญหานี้: 1) หากคุณมีกิจกรรมที่เป็นนามธรรมกับ WebView ให้configChangesแอตทริบิวต์ได้รับเพิ่มไป subclass กิจกรรมที่ 2) ถ้า app ของคุณขึ้นอยู่กับหลายโครงการที่configChangesแอตทริบิวต์ได้รับเพิ่มไปที่ประจักษ์ของหนึ่งที่ ด้านบนของแผนภูมิการพึ่งพา (ซึ่งอาจไม่ใช่โครงการที่มีคลาสกิจกรรม)
Amanda S

4
onConfigurationChangedวิธีการแทนที่ดังกล่าวไม่มีประโยชน์
Miha_x64

69

แก้ไข: วิธีนี้ใช้ไม่ได้อีกต่อไปตามที่ระบุในเอกสาร


คำตอบเดิม:

สิ่งนี้สามารถจัดการได้โดยการเขียนทับonSaveInstanceState(Bundle outState)ในกิจกรรมของคุณและโทรsaveStateจาก webview:

   protected void onSaveInstanceState(Bundle outState) {
      webView.saveState(outState);
   }

จากนั้นกู้คืนสิ่งนี้ใน onCreate ของคุณหลังจากที่ webview มีการพองตัวซ้ำอีกครั้งแน่นอน:

public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.blah);
   if (savedInstanceState != null)
      ((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}

26
สิ่งนี้ไม่ทำงานกับการเปลี่ยนการวางแนว - หน้าจอว่างจะปรากฏขึ้น saveState \ restoreState วิธีการที่เรียกว่า แต่มันไม่ได้ช่วย
Oleksii Malovanyi

1
นี่คือตัวอย่างโค้ด onCreate: setContentView (R.layout.webview); mWebView = (WebView) findViewById (R.id.webview); if (saveInstanceState == null) {mWebView.getSettings () setJavaScriptEnabled (จริง); mWebView.setWebViewClient (ใหม่ SimpleWebViewClient ()); mWebView.loadUrl (Consts.URL_AUTHORIZATION_OAUTH); } else {mWebView.restoreState (hiddenInstanceState); }
Oleksii Malovanyi

12
จากเอกสารของ saveState: โปรดทราบว่าวิธีนี้จะไม่เก็บข้อมูลการแสดงผลสำหรับ WebView นี้อีกต่อไป พฤติกรรมก่อนหน้านี้อาจทำให้ไฟล์รั่ว ...
brillenheini

5
"โปรดทราบว่าวิธีนี้จะไม่เก็บข้อมูลการแสดงผลสำหรับ WebView นี้อีกต่อไป" - developer.android.com/reference/android/webkit/WebView.html
Steven Mark Ford

ใครบ้างมีข้อเสนอแนะเกี่ยวกับสิ่งที่ต้องทำในขณะนี้ว่า "วิธีนี้จะไม่เก็บข้อมูลที่แสดงสำหรับ WebView นี้อีกต่อไป" เพื่อป้องกันการโหลดแบบฟอร์ม WebView อีกต่อไป?
ลูคัสพี

24

คำตอบที่ดีที่สุดสำหรับการทำตามเอกสาร 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();
    }
}

6
คุณไม่ต้องการเมธอด onConfigurationChanged () ฉันค่อนข้างแน่ใจว่ามีเพียงแอททริบิวต์เพิ่มเติมในรายการ ฉันเดาว่าการสนับสนุนดั้งเดิมสำหรับ WebView นี้จัดทำขึ้นและนี่คือสาเหตุที่คำตอบนี้ดีที่สุด (เพราะมันไม่ทำงานเมื่อคำถามถูกถาม)
Booger

1
ฉันอนุมัติ @Booger คุณไม่จำเป็นต้องแทนที่เมธอด onConfigurationChanged () สิ่งที่คุณต้องมีก็คือเพิ่ม android: configChanges = "Orient | screenSize" ในไฟล์ manifest.xml
ozanurkan

5

ฉันได้ลองใช้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แหล่งที่มาของและ Android "ทำในสิ่งที่ถูกต้อง" - ใช่!

... ยกเว้นเมื่อคุณดูการเปลี่ยนแปลงการกำหนดค่ามันจะไม่เกิดขึ้น :(

ดังนั้นฉันอยู่ที่อัตราต่อรอง ใช้onRetainNonConfigurationInstanceและการหมุนImageViewทำงานได้ แต่การคงอยู่ของWebViewไม่ได้ ... หรือใช้onConfigurationChangedและWebViewยังคงอยู่ แต่ImageViewไม่อัปเดต จะทำอย่างไร?

หมายเหตุสุดท้ายหนึ่ง: ในกรณีของฉันการบังคับให้วางแนวไม่ใช่การประนีประนอมที่ยอมรับได้ เราต้องการสนับสนุนการหมุนเวียนอย่างแท้จริง ค่อนข้างชอบวิธีที่แอพ Android Browser ทำ! ;)


7
คุณไม่ควรคืนค่ามุมมองใด ๆ จาก onRetainNonConfigurationInstance มุมมองจะแนบไปกับบริบทของกิจกรรมดังนั้นหากคุณบันทึกมุมมองที่คุณพกติดตัวสัมภาระทั้งหมดทุกครั้งที่มีการเปลี่ยนทิศทาง มุมมองคือ "รั่วไหล" (ดูย่อหน้าสุดท้ายที่นี่: developer.android.com/resources/articles/… ) แนวทางปฏิบัติที่ดีที่สุดสำหรับ onRetainNonConfigurationInstance คือการบันทึกข้อมูลที่มุมมองต้องการไม่ใช่มุมมองของตัวเอง
Declan Shanaghy

3

หนึ่งประนีประนอมคือการหลีกเลี่ยงการหมุน เพิ่มสิ่งนี้เพื่อแก้ไขกิจกรรมสำหรับแนวตั้งเท่านั้น

android:screenOrientation="portrait"

ใช้งานได้จริงในหลายกรณีและเป็นวิธีแก้ปัญหาที่ฉันตัดสินใจใช้ :-)
Abdo

1
นี่คือการเปลี่ยนแปลงความต้องการ
Max Ch

3

วิธีที่ดีที่สุดในการจัดการการเปลี่ยนแปลงการวางแนวและการป้องกันไม่ให้โหลด WebView บน Rotate

@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"`

3

เพียงแค่เขียนบรรทัดโค้ดต่อไปนี้ในไฟล์ Manifest ของคุณไม่มีอะไรอื่น มันได้ผลจริง:

<activity android:name=".YourActivity"
  android:configChanges="orientation|screenSize"
  android:label="@string/application_name">

3

ฉันขอขอบคุณที่นี่สายไปหน่อย แต่นี่เป็นคำตอบที่ฉันใช้เมื่อพัฒนาโซลูชัน:

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);
    }

}

2

คุณสามารถลองใช้onSaveInstanceState()และonRestoreInstanceState()บนกิจกรรมของคุณเพื่อโทรsaveState(...)และrestoreState(...)บนอินสแตนซ์ WebView ของคุณ


2

มันคือปี 2015 และหลายคนกำลังมองหาทางออกที่ยังคงใช้งานได้กับโทรศัพท์ Jellybean, KK และ Lollipop หลังจากนั้นมากดิ้นรนฉันพบวิธีที่จะรักษาหน้าเว็บให้ไม่เปลี่ยนแปลงหลังจากที่คุณเปลี่ยนการวางแนว กลยุทธ์ของฉันใช้เพื่อจัดเก็บ webview ในตัวแปรคงที่แยกต่างหากในคลาสอื่น จากนั้นหากการหมุนเกิดขึ้นฉันกำหนดมุมมองเว็บใหม่จากกิจกรรมรอให้การวางแนวเสร็จสิ้นและแนบมุมมองเว็บกลับไปที่กิจกรรมอีกครั้ง ตัวอย่างเช่น ... วางสิ่งนี้ไว้บน MANIFEST ของคุณ (keyboardHidden และ keyboard เป็นทางเลือก):

<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>

ในคลาสแอปพลิเคชัน SEPARATE ให้ป้อน:

     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), กล่องข้อความ, ป้ายกำกับ, UI, ตัวแปรจาวาสคริปต์และสถานะการนำทางที่ไม่ได้สะท้อนจาก URL


1
ทำไมถึงต้องรำคาญกับการแยกและติดตั้ง WebView ใหม่เมื่อไม่เคยถูกทำลายตั้งแต่แรกเพราะคุณจัดการการเปลี่ยนแปลงการตั้งค่าด้วยตนเอง
Fred

@ เฟร็ดเพราะฉันต้องการรักษาสถานะที่แน่นอนของ Webview และเนื้อหาของมันไว้รวมถึงคุกกี้และสถานะของปุ่มและช่องทำเครื่องหมายที่ใช้กับ HTML5 หรือ JS ภายในเว็บเพจที่โหลดโดย webview การปรับเท่านั้นที่ฉันทำคือปรับขนาด
จอช

เฟร็ดหมายความว่าคุณไม่จำเป็นต้องถอด / ใส่เว็บเพจใหม่ผ่านคลาสแบบคงที่เมื่อคุณประกาศ configChanges ในไฟล์ Manifest ด้วยวิธีนี้มุมมองและกิจกรรมของคุณจะไม่ถูกทำลายระหว่างการหมุนไม่ว่ากรณีใด ๆ
Eugene Kartoyev

2

สิ่งเดียวที่คุณควรทำคือการเพิ่มรหัสนี้ลงในไฟล์รายการของคุณ:

<activity android:name=".YourActivity"
      android:configChanges="orientation|screenSize"
      android:label="@string/application_name">

2

อัปเดต: กลยุทธ์ปัจจุบันคือการย้ายอินสแตนซ์ WebView ไปยังคลาสแอปพลิเคชันแทนที่จะเป็นชิ้นส่วนที่เก็บไว้เมื่อถูกแยกออกและดำเนินการต่อตามที่ Josh ทำ เพื่อป้องกันการปิดแอปพลิเคชันคุณควรใช้บริการเบื้องหน้าหากคุณต้องการรักษาสถานะเมื่อผู้ใช้สลับระหว่างแอปพลิเคชัน

หากคุณใช้แฟรกเมนต์คุณสามารถใช้เก็บอินสแตนซ์ของ 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) จะยังคงถูกทำลาย
เฟรด

1
@Fred คุณสามารถให้แหล่งข้อมูลสำหรับคำสั่งนั้นได้หรือไม่? ฉันได้ทดสอบสิ่งนี้ในแอพขนาดเล็กซึ่งมีส่วนย่อยด้วย setRetainInstance (จริง) ที่แสดงหน้า YouTube และหากฉันเปลี่ยนการวางแนวมุมมองน่าจะถูกเก็บไว้เนื่องจากวิดีโอไม่ถูกขัดจังหวะ ดูเหมือนว่าเว็บวิวกำลังถูกทำลาย
Pinkerton

@ ไรมันเป็นเพียงการอ่านผิดฉันได้แก้ไขคำตอบเล็กน้อยตั้งแต่นั้นมา (มันยังคงต้องการการแก้ไขถ้อยคำ) หากคุณสนใจสถานการณ์ปัจจุบัน - วิธีนี้ใช้ได้ผล อย่างไรก็ตามปัญหาคือ Android ฆ่าแอปพลิเคชันเมื่อหน่วยความจำเหลือน้อย ฉันมีรูปแบบการป้อนข้อมูลที่สำคัญที่ผู้ใช้ต้องอ่านข้อความในแอปพลิเคชันอื่นดังนั้นฉันจึงต้องใช้บริการเบื้องหน้าพร้อมการแจ้งเตือนเพื่อป้องกันไม่ให้กระบวนการถูกฆ่า ทว่า Android ยังทำลายกิจกรรมและทำให้เฟรมคงเดิม แต่อินสแตนซ์ของคลาสแอปพลิเคชันนั้นได้รับการสงวนรักษาไว้ดังนั้นฉันจึงย้าย WebView ไปยังแอปพลิเคชันทันที
Boris Treukhov

1
ฉันยังใช้ MutableContextWrapper เป็นฐานของ WebView ฉันเปลี่ยนบริบทเป็นกิจกรรมที่แนบและแยกออก ปัญหาใหญ่อีกอย่างหนึ่งที่ Chromium สร้างขึ้นในการควบคุมการซูมบันทึกการอ้างอิงถึงกิจกรรมเก่าดังนั้นจึงไม่มีวิธีแก้ปัญหาการซูมด้วย WebView ที่แนบมากับแอปพลิเคชันแทนที่จะเป็นกิจกรรม TL; DR; หากแบบฟอร์มของคุณไม่สำคัญและคุณอาจใช้ชีวิตด้วยความจริงที่ว่าหลังจากที่แอพเปลี่ยนกระบวนการของคุณอาจถูกฆ่าและไม่ได้เรียกคืนมุมมองทางเว็บด้วยโซลูชันเฟรมแบบคงที่ ป.ล. ฉันคิดว่าพนักงานของ Google นั้นห่างไกลจากปัญหาในชีวิตจริงด้วยวิดเจ็ตวิดเจ็ตทั้งหมดของพวกเขา ..
Boris Treukhov

1
@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


1

ทางออกที่ดีที่สุดที่ฉันได้พบการทำเช่นนี้โดยไม่รั่วไหลการActivityอ้างอิงก่อนหน้าและโดยไม่ต้องตั้งค่าconfigChanges.. คือการใช้MutableContextWrapper MutableContextWrapper

ฉันได้ใช้สิ่งนี้ที่นี่: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.js


มันทำงานได้อย่างน่าเชื่อถือโดยไม่มีผลข้างเคียง? ฉันไม่พบข้อมูลมากมายเกี่ยวกับวิธีนี้
craigrs84

1

นี่เป็นสิ่งเดียวที่ทำงานได้สำหรับฉัน (ฉันยังใช้สถานะอินสแตนซ์บันทึกใน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());
    }

แม้ว่าฉันจะอ่านมันไม่ได้ทำงานได้ดีกับคุกกี้


การใช้ซิงเกิลตันเป็นปัญหาเนื่องจากคุณมีอินสแตนซ์เดียวเท่านั้นที่คุณต้องใช้
นักพัฒนา Android

@androiddeveloper ah ถ้าไม่เป็นความจริงในกรณีของคุณนั่นก็เป็นปัญหา ฉันมีเพียงหนึ่งอินสแตนซ์ WebView ดังนั้นคำถามนี้ไม่ได้เกิดขึ้นกับฉัน
EpicPandaForce

1

ลองนี้

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;
        }
    }

}

0

หน้านี้แก้ปัญหาของฉัน แต่ฉันต้องทำการเปลี่ยนแปลงเล็กน้อยในครั้งแรก:

    protected void onSaveInstanceState(Bundle outState) {
       webView.saveState(outState);
       }

ส่วนนี้มีปัญหาเล็กน้อยสำหรับฉันในเรื่องนี้ ในการวางแนวที่สองเปลี่ยนแอปพลิเคชันที่ยกเลิกด้วยตัวชี้โมฆะ

ใช้มันทำงานได้สำหรับฉัน:

    @Override
protected void onSaveInstanceState(Bundle outState ){
    ((WebView) findViewById(R.id.webview)).saveState(outState);
}

น่าจะเหมือนกันทุกประการ ทำไมคนที่สองถึงทำงานในขณะที่คนก่อนหน้าไม่ได้?
นักพัฒนา Android

0

คุณควรลองสิ่งนี้:

  1. สร้างบริการภายในบริการนั้นสร้าง WebView ของคุณ
  2. เริ่มบริการจากกิจกรรมของคุณและผูกไว้กับมัน
  3. ในonServiceConnectedวิธีรับ WebView และเรียกsetContentViewวิธีการแสดง WebView ของคุณ

ฉันทดสอบแล้วและใช้งานได้ แต่ไม่ใช่กับ WebViews อื่น ๆ เช่น XWalkView หรือ GeckoView


Hi! คุณช่วยอธิบายเพิ่มเติมได้ไหม ขอบคุณ
Jaco

1
เอารูปแบบเว็บวิวของคุณมาเป็นกิจกรรมแล้วพยายามอ้างอิงถึงบริการที่กำลังทำงานอยู่เบื้องหลัง ในครั้งต่อไปที่คุณเปิดกิจกรรมนั้นให้ลองดู webview ที่บันทึกไว้ก่อนแล้วจึงสร้างมันขึ้นมา
Ramdane Oualitsen

0
@Override
    protected void onSaveInstanceState(Bundle outState )
    {
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    }

คุณสามารถเพิ่มคำอธิบายลงในคำตอบของคุณได้ไหม?
yacc

-1

ฉันชอบวิธีนี้ http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

จากนั้นเราจะใช้อินสแตนซ์เดียวกันของ WebView อีกครั้ง อนุญาตให้บันทึกประวัติการนำทางและตำแหน่งการเลื่อนเมื่อเปลี่ยนแปลงการกำหนดค่า


ลิงค์ด้านบนนำไปสู่หน้าที่ไม่สามารถดูได้โดยไม่ติดตั้ง 'ปลั๊กอินความปลอดภัย' บางส่วน; หลีกเลี่ยงการ
Allan Bazinet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.