2011-07-28 18 views
24

एंड्रॉइड 2.3+ में ब्राउज़र एक अभिविन्यास पर सामग्री की स्क्रॉल की स्थिति को बनाए रखने के लिए एक अच्छा काम करते हैं।अभिविन्यास परिवर्तन पर वेबव्यू सामग्री स्क्रॉल स्थिति बनाए रखें

मैं एक वेबव्यू के लिए एक ही चीज़ प्राप्त करने की कोशिश कर रहा हूं जिसे मैं एक गतिविधि के भीतर प्रदर्शित करता हूं लेकिन सफलता के बिना।

मैंने एक रोटेशन पर गतिविधि मनोरंजन से बचने के लिए मैनिफेस्ट चेंज (एंड्रॉइड: configChanges = "ओरिएंटेशन | कीबोर्ड हैडन") का परीक्षण किया है, हालांकि स्क्रॉल स्थिति अलग-अलग पहलू अनुपातों के कारण जहां मैं चाहता हूं वहां सेट नहीं होता है। इसके अलावा यह मेरे लिए एक समाधान है क्योंकि मुझे चित्र और परिदृश्य में अलग-अलग लेआउट की आवश्यकता है।

मैंने वेबव्यू स्टेटस को सहेजने और इसे बहाल करने की कोशिश की है लेकिन सामग्री में यह resuls शीर्ष पर प्रदर्शित किया जा रहा है।

onPageFinished में scrollTo का उपयोग करके स्क्रॉल करने का प्रयास करने से काम नहीं करता है, भले ही वेबव्यू की ऊंचाई इस बिंदु पर शून्य न हो।

कोई विचार? अग्रिम में धन्यवाद। पीटर।

आंशिक समाधान:

मेरे सहयोगी एक जावास्क्रिप्ट समाधान के माध्यम से स्क्रॉल काम कर पाने में कामयाब रहे। समान लंबवत स्थिति में सरल स्क्रॉलिंग के लिए, वेबव्यू की 'वाई' स्क्रॉल स्थिति onSaveInstanceState स्थिति में सहेजी जाती है। निम्नलिखित तो onPageFinished में जोड़ा जाता है:

public void onPageFinished(final WebView view, final String url) { 
    if (mScrollY > 0) { 
     final StringBuilder sb = new StringBuilder("javascript:window.scrollTo(0, "); 
     sb.append(mScrollY); 
     sb.append("/ window.devicePixelRatio);"); 
     view.loadUrl(sb.toString()); 
    } 
    super.onPageFinished(view, url); 
} 

आप मामूली झिलमिलाहट मिल के रूप में सामग्री नई पुस्तक स्थिति के लिए शुरू से ही कूदता है, लेकिन यह मुश्किल से नजर है। अगला चरण प्रतिशत आधारित विधि (ऊंचाई में मतभेदों के आधार पर) का प्रयास करें और वेबव्यू को अपने राज्य को सहेजने और पुनर्स्थापित करने की जांच भी करें।

उत्तर

37

वेबव्यू की वर्तमान स्थिति को पुनर्स्थापित करने के लिए अभिविन्यास परिवर्तन मुझे डर है कि आपको इसे मैन्युअल रूप से करना होगा।

मैं इस विधि का इस्तेमाल किया:

  1. onRetainNonConfigurationInstance
  2. में WebView
  3. में पुस्तक का वास्तविक प्रतिशत की गणना यह सहेजें WebView की स्थिति बहाल जब यह

क्योंकि निर्मित है वेबव्यू की चौड़ाई और ऊंचाई पोर्ट्रेट और लैंडस्केप मोड में समान नहीं है, मैं उपयोगकर्ता स्क्रॉल स्थिति का प्रतिनिधित्व करने के लिए प्रतिशत का उपयोग करता हूं।कदम से

कदम:

1) WebView

// Calculate the % of scroll progress in the actual web page content 
private float calculateProgression(WebView content) { 
    float positionTopView = content.getTop(); 
    float contentHeight = content.getContentHeight(); 
    float currentScrollPosition = content.getScrollY(); 
    float percentWebview = (currentScrollPosition - positionTopView)/contentHeight; 
    return percentWebview; 
} 

2 में पुस्तक का वास्तविक प्रतिशत की गणना) onRetainNonConfigurationInstance

सहेजें में यह प्रगति रोकें सिर्फ उन्मुखीकरण बदलने से पहले

@Override 
public Object onRetainNonConfigurationInstance() { 
    OrientationChangeData objectToSave = new OrientationChangeData(); 
    objectToSave.mProgress = calculateProgression(mWebView); 
    return objectToSave; 
} 

// Container class used to save data during the orientation change 
private final static class OrientationChangeData { 
    public float mProgress; 
} 

3) की स्थिति को पुनर्स्थापित करें WebView जब यह निर्मित होने के बाद अपने पेज लेती है तो

उन्मुखीकरण परिवर्तन डेटा

private boolean mHasToRestoreState = false; 
private float mProgressToRestore; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.main); 

    mWebView = (WebView) findViewById(R.id.WebView); 
    mWebView.setWebViewClient(new MyWebViewClient()); 
    mWebView.loadUrl("http://stackoverflow.com/"); 

    OrientationChangeData data = (OrientationChangeData) getLastNonConfigurationInstance(); 
    if (data != null) { 
     mHasToRestoreState = true; 
     mProgressToRestore = data.mProgress; 
    } 
} 

से प्रगति हासिल वर्तमान स्थिति आप (पेज पुनः लोड किया जा करने के लिए इंतजार करने के लिए इस विधि समस्याएं खड़ी कर सकता होगा पुनर्स्थापित करने के लिए लोड करने के लिए एक लंबे समय के)

private class MyWebViewClient extends WebViewClient { 
    @Override 
    public void onPageFinished(WebView view, String url) { 
     if (mHasToRestoreState) { 
      mHasToRestoreState = false; 
      view.postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        float webviewsize = mWebView.getContentHeight() - mWebView.getTop(); 
        float positionInWV = webviewsize * mProgressToRestore; 
        int positionY = Math.round(mWebView.getTop() + positionInWV); 
        mWebView.scrollTo(0, positionY); 
       } 
      // Delay the scrollTo to make it work 
      }, 300); 
     } 
     super.onPageFinished(view, url); 
    } 
} 

अपने परीक्षण मैं मुठभेड़ है कि आप एक छोटे से प्रतीक्षा करने के लिए के बाद onPageFinished विधि पुस्तक काम कर रहे बनाने के लिए कहा जाता है की जरूरत के दौरान। 300 एमएम ठीक होना चाहिए। यह देरी प्रदर्शन को फ्लिक करने के लिए बनाती है (पहले स्क्रॉल 0 पर प्रदर्शित करें और फिर सही स्थिति पर जाएं)।

शायद ऐसा करने का एक और बेहतर तरीका है लेकिन मुझे पता नहीं है।

+0

@ ol_v-er जानकारी के लिए धन्यवाद। मैं देरी को शुरू करने का प्रयास करने में रूचि रखता हूं क्योंकि आप 'स्क्रॉल टू' काम देखने के लिए वर्णन करते हैं (मुझे आश्चर्य है कि देरी की आवश्यकता है लेकिन वैसे भी ...)। अंततः एक जावास्क्रिप्ट समाधान (जिसे मैं जल्द ही पोस्ट करूंगा) के माध्यम से काम करने के लिए स्क्रॉलिंग प्राप्त कर रहा हूं, मुझे प्रतिशत स्क्रॉलिंग का विचार था जो मुझे परिणाम देखने के लिए अभी तक प्रयास करने के लिए नहीं मिला है। – PJL

+0

वास्तव में जब ऑनपेजफिश किए गए कॉलबैक को कॉल किया जाता है, तो वेबव्यू बस ड्राइंग शुरू कर देता है। देरी यह सुनिश्चित करना है कि वेबव्यू ने ड्राइंग समाप्त कर दी है (और सही ऊंचाई है)। अपने जावास्क्रिप्ट समाधान के लिए तत्पर हैं। –

+0

धन्यवाद, मेरे जावास्क्रिप्ट समाधान जोड़ा गया। – PJL

1

ऑनपेज परिभाषित नहीं किया जा सकता है क्योंकि आप पृष्ठ को पुनः लोड नहीं कर रहे हैं, आप केवल उन्मुखीकरण को बदल रहे हैं, यह सुनिश्चित नहीं है कि यह एक रीलोड का कारण बनता है या नहीं।

ऑन-कॉन्फ़िगरेशन में स्क्रॉल का उपयोग करने का प्रयास करें अपनी गतिविधि का परिवर्तन विधि।

+0

मुझे अभिविन्यास परिवर्तन पर 'ऑनपेजफिनिश' मिल रहा है लेकिन 'वेबव्यू :: स्क्रॉलटॉ' को कॉल करने पर कोई प्रभाव नहीं पड़ता है। – PJL

+0

इसके अलावा, शायद यह समाधान कुछ लोगों के लिए काम करेगा हालांकि मैं गतिविधि को अभिविन्यास परिवर्तन पर पुनरारंभ करना चाहता हूं। – PJL

1

पहलू परिवर्तन हमेशा आपके वेबव्यू में वर्तमान स्थान को बाद में स्क्रॉल करने के लिए सही स्थान नहीं होने का कारण बनता है। आप स्नीकी हो सकते हैं और वेबव्यू में सबसे ज्यादा दिखाई देने वाले तत्व को निर्धारित कर सकते हैं और स्रोत में उस बिंदु पर एक ओरिएंटेशन चेंज इम्प्लांट को anchor पर लागू कर सकते हैं और उपयोगकर्ता को इसे रीडायरेक्ट कर सकते हैं ...

+0

धन्यवाद, मैं यह महसूस करने में मदद नहीं कर सकता कि इस समस्या के लिए एक सरल/अंतर्निहित समाधान होना चाहिए। – PJL

0

वेबव्यू के सही प्रतिशत की गणना करने के लिए, mWebView.getScale() भी गिनना महत्वपूर्ण है। असल में, का वापसी मूल्य getScrollY() क्रमशः mWebView.getContentHeight() * mWebView.getScale() के साथ है।

0

जिस तरह से हमने इसे हमारे टुकड़ों के भीतर WebView के स्थानीय संदर्भ के लिए प्राप्त करने में कामयाब रहा है।

/** 
* WebView reference 
*/ 
private WebView webView; 

फिर setRetainInstancetrue को onCreate

@Override 
public void onCreate(final Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setRetainInstance(true); 

    // Argument loading settings 
} 

में फिर जब onCreateView कहा जाता है, WebView की मौजूदा स्थानीय उदाहरण लौटने के लिए, अन्यथा सामग्री और अन्य सेटिंग को सेट करना एक नई प्रतिलिपि का दृष्टांत। माता-पिता को हटाने के लिए WebView को दोबारा जोड़ने पर वह महत्वपूर्ण कदम है जिसे आप नीचे दिए गए खंड में देख सकते हैं।

@Override 
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, 
     final Bundle savedInstanceState) { 

    final View view; 
    if (webView == null) { 

     view = inflater.inflate(R.layout.webview_dialog, container); 

     webView = (WebView) view.findViewById(R.id.dialog_web_view); 

     // Setup webview and content etc... 
    } else { 
     ((ViewGroup) webView.getParent()).removeView(webView); 
     view = webView; 
    } 

    return view; 
} 
+0

मुझे दूसरी बार एक शून्य सूचक मिलता है जब मैं उस खंड में लौटता हूं जिसमें वेबदृश्य होता है। वेबव्यू के साथ टुकड़ा 1 भार। मैं एक स्थिति में स्क्रॉल करता हूं, खंड 2 पर जाता हूं, खंड में वापस जाता हूं 1. सब कुछ ठीक है। हालांकि, मैं फिर खंड 2 पर जाता हूं और फिर खंड 1 पर वापस जाता हूं और ऐप एक शून्य सूचक के साथ दुर्घटनाग्रस्त हो जाता है। कोई विचार? – piratemurray

+0

यह कामकाज एक स्मृति रिसाव का कारण बनता है क्योंकि आपकी वेबव्यू नई गतिविधि से जुड़ी हुई मूल गतिविधि के संदर्भ को रखेगी। इससे बचें – BladeCoder

0

मैं आंशिक समाधान मूल पोस्ट में वर्णित इस्तेमाल किया, लेकिन इसके बजाय, बजाय onPageFinished की() onPageStarted() के अंदर जावास्क्रिप्ट टुकड़ा डाल दिया। कोई कूद नहीं के साथ मेरे लिए काम करने लगता है।

मेरा उपयोग मामला थोड़ा अलग है: उपयोगकर्ता पृष्ठ को रीफ्रेश करने के बाद क्षैतिज स्थिति रखने की कोशिश कर रहा हूं।

लेकिन मैं अपने समाधान का उपयोग कर लिया है और यह मेरे :)

0

मूल पोस्ट में वर्णित आंशिक समाधान में के लिए पूरी तरह से काम करता है, तो आप समाधान सुधार कर सकते हैं, तो निम्न कोड

private float calculateProgression(WebView content) { 
    float contentHeight = content.getContentHeight(); 
    float currentScrollPosition = content.getScrollY(); 
    float percentWebview = currentScrollPosition/ contentHeight; 
    return percentWebview; 
} 
बदल

घटक की शीर्ष स्थिति के कारण, जब आप content.getTop() का उपयोग कर रहे हैं; तो यह क्या करता है स्क्रॉल वाई स्थिति की वास्तविक स्थिति में जोड़ता है जहां घटक इसके माता-पिता के संबंध में स्थित होता है और सामग्री को चलाता है। मुझे उम्मीद है कि मेरी व्याख्या स्पष्ट है।

0

तुम क्यों स्वीकार किए जाते हैं जवाब में इस सवाल का जवाब पर विचार करना चाहिए:

स्वीकृत जवाब सभ्य प्रदान करता है और सरल तरह से स्थिति स्क्रॉल को बचाने के लिए, लेकिन यह सही से दूर है। उस दृष्टिकोण के साथ समस्या यह है कि कभी-कभी घूर्णन के दौरान आप घूर्णन से पहले स्क्रीन पर देखे गए किसी भी तत्व को भी नहीं देख पाएंगे। स्क्रीन के शीर्ष पर मौजूद तत्व अब घूर्णन के बाद नीचे हो सकता है। स्क्रॉल के प्रतिशत के माध्यम से स्थिति को सहेजना बहुत सटीक नहीं है और बड़े दस्तावेजों पर यह गलतता बढ़ सकती है।

तो यहां एक और तरीका है: यह तरीका अधिक जटिल है, लेकिन यह लगभग गारंटी देता है कि घूर्णन से पहले आपने घूर्णन के बाद बिल्कुल वही तत्व दिखाई देगा। मेरी राय में, यह विशेष रूप से एक बड़े दस्तावेज़ों पर, एक बेहतर उपयोगकर्ता अनुभव की ओर जाता है।

======

सबसे पहले, हम जावास्क्रिप्ट के माध्यम से वर्तमान पुस्तक स्थिति को ट्रैक करेगा। यह हमें यह जानने की अनुमति देगा कि वर्तमान में स्क्रीन के शीर्ष पर कौन सा तत्व मौजूद है और यह कितना स्क्रॉल किया गया है।

सबसे पहले, यह सुनिश्चित करें कि जावास्क्रिप्ट अपने WebView के लिए सक्षम किया गया है:

public class WebScrollListener { 

    private String element; 
    private int margin; 

    @JavascriptInterface 
    public void onScrollPositionChange(String topElementCssSelector, int topElementTopMargin) { 
     Log.d("WebScrollListener", "Scroll position changed: " + topElementCssSelector + " " + topElementTopMargin); 
     element = topElementCssSelector; 
     margin = topElementTopMargin; 
    } 

} 

फिर हम इस वर्ग जोड़ें:

webView.getSettings().setJavaScriptEnabled(true); 

इसके बाद, हम जावा वर्ग है कि जावास्क्रिप्ट के भीतर से जानकारी को स्वीकार करेंगे बनाने की जरूरत वेबव्यू पर:

scrollListener = new WebScrollListener(); // save this in an instance variable 
webView.addJavascriptInterface(scrollListener, "WebScrollListener"); 

अब हमें HTML पृष्ठ में जावास्क्रिप्ट कोड डालने की आवश्यकता है। यह स्क्रिप्ट जावा के लिए स्क्रॉल डेटा भेज देंगे (यदि आप पीढ़ी एचटीएमएल हैं, बस इस स्क्रिप्ट संलग्न हैं अन्यथा आपको webView.loadUrl("javascript:document.write(" + script + ")"); के माध्यम से document.write() बुला का सहारा की जरूरत हो सकती):

<script> 
    // We will find first visible element on the screen 
    // by probing document with the document.elementFromPoint function; 
    // we need to make sure that we dont just return 
    // body element or any element that is very large; 
    // best case scenario is if we get any element that 
    // doesn't contain other elements, but any small element is good enough; 
    var findSmallElementOnScreen = function() { 
     var SIZE_LIMIT = 1024; 
     var elem = undefined; 
     var offsetY = 0; 
     while (!elem) { 
      var e = document.elementFromPoint(100, offsetY); 
      if (e.getBoundingClientRect().height < SIZE_LIMIT) { 
       elem = e; 
      } else { 
       offsetY += 50; 
      } 
     } 
     return elem; 
    }; 

    // Convert dom element to css selector for later use 
    var getCssSelector = function(el) { 
     if (!(el instanceof Element)) 
      return; 
     var path = []; 
     while (el.nodeType === Node.ELEMENT_NODE) { 
      var selector = el.nodeName.toLowerCase(); 
      if (el.id) { 
       selector += '#' + el.id; 
       path.unshift(selector); 
       break; 
      } else { 
       var sib = el, nth = 1; 
       while (sib = sib.previousElementSibling) { 
        if (sib.nodeName.toLowerCase() == selector) 
         nth++; 
       } 
       if (nth != 1) 
        selector += ':nth-of-type('+nth+')'; 
      } 
      path.unshift(selector); 
      el = el.parentNode; 
     } 
     return path.join(' > ');  
    }; 

    // Send topmost element and its top offset to java 
    var reportScrollPosition = function() { 
     var elem = findSmallElementOnScreen(); 
     if (elem) { 
      var selector = getCssSelector(elem); 
      var offset = elem.getBoundingClientRect().top; 
      WebScrollListener.onScrollPositionChange(selector, offset); 
     } 
    } 

    // We will report scroll position every time when scroll position changes, 
    // but timer will ensure that this doesn't happen more often than needed 
    // (scroll event fires way too rapidly) 
    var previousTimeout = undefined; 
    window.addEventListener('scroll', function() { 
     clearTimeout(previousTimeout); 
     previousTimeout = setTimeout(reportScrollPosition, 200); 
    }); 
</script> 

आप इस बिंदु पर अपने अनुप्रयोग चलाते हैं, आपको पहले से ही लॉगकैट में संदेश दिखाना चाहिए जो आपको बता रहा है कि नई स्क्रॉल स्थिति प्राप्त हुई है।

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    webView.saveState(outState); 
    outState.putString("scrollElement", scrollListener.element); 
    outState.putInt("scrollMargin", scrollListener.margin); 
} 

फिर हम onCreate में इसे पढ़ा (गतिविधि के लिए) या onCreateView (टुकड़ा के लिए) विधि:

if (savedInstanceState != null) { 
    webView.restoreState(savedInstanceState); 
    initialScrollElement = savedInstanceState.getString("scrollElement"); 
    initialScrollMargin = savedInstanceState.getInt("scrollMargin"); 
} 

हम भी जोड़ने की जरूरत है

अब हम WebView राज्य को बचाने की जरूरत हमारे WebView को WebViewClient और ओवरराइड onPageFinished विधि:

@Override 
public void onPageFinished(final WebView view, String url) { 
    if (initialScrollElement != null) { 
     // It's very hard to detect when web page actually finished loading; 
     // At the time onPageFinished is called, page might still not be parsed 
     // Any javascript inside <script>...</script> tags might still not be executed; 
     // Dom tree might still be incomplete; 
     // So we are gonna use a combination of delays and checks to ensure 
     // that scroll position is only restored after page has actually finished loading 
     webView.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       String javascript = "(function (selectorToRestore, positionToRestore) {\n" + 
         "  var previousTop = 0;\n" + 
         "  var check = function() {\n" + 
         "   var elem = document.querySelector(selectorToRestore);\n" + 
         "   if (!elem) {\n" + 
         "    setTimeout(check, 100);\n" + 
         "    return;\n" + 
         "   }\n" + 
         "   var currentTop = elem.getBoundingClientRect().top;\n" + 
         "   if (currentTop !== previousTop) {\n" + 
         "    previousTop = currentTop;\n" + 
         "    setTimeout(check, 100);\n" + 
         "   } else {\n" + 
         "    window.scrollBy(0, currentTop - positionToRestore);\n" + 
         "   }\n" + 
         "  };\n" + 
         "  check();\n" + 
         "}('" + initialScrollElement + "', " + initialScrollMargin + "));"; 

       webView.loadUrl("javascript:" + javascript); 
       initialScrollElement = null; 
      } 
     }, 300); 
    } 
} 

यह है। स्क्रीन रोटेशन के बाद, आपकी स्क्रीन के शीर्ष पर मौजूद तत्व अब वहां रहना चाहिए।

संबंधित मुद्दे