समर्थन FragmentPagerAdapter पुराने टुकड़ों के संदर्भ में है


109

नवीनतम जानकारी:

मैंने अपनी समस्या को कम कर दिया है, जो मेरे टुकड़े टुकड़े करने वाले के साथ सिंक के बाहर होने वाले पुराने अंशों और मेरे दृष्टिकोण को बनाए रखने के टुकड़े के साथ एक समस्या है। इस मुद्दे को देखें ... http://code.google.com/p/android/issues/detail?id=19211#makechanges । मुझे अभी भी कोई सुराग नहीं है कि इसे कैसे हल किया जाए। कोई सुझाव...

मैंने लंबे समय तक इस पर बहस करने की कोशिश की है और किसी भी मदद की बहुत सराहना की जाएगी। मैं एक FragmentPagerAdapter का उपयोग कर रहा हूं जो टुकड़े की एक सूची को स्वीकार करता है जैसे:

List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName())); 
...
new PagerAdapter(getSupportFragmentManager(), fragments);

कार्यान्वयन मानक है। मैं Fragments के लिए ActionBarSherlock और v4 कम्प्यूटेशनल लाइब्रेरी का उपयोग कर रहा हूं।

मेरी समस्या यह है कि ऐप को छोड़ने और कई अन्य एप्लिकेशन खोलने और वापस आने के बाद, टुकड़े फ़्रैगमेंटएक्टिविटी (यानी। getActivity() == null) में अपना संदर्भ वापस खो देते हैं । मैं यह पता नहीं लगा सकता कि ऐसा क्यों हो रहा है। मैंने मैन्युअल रूप से सेट करने की कोशिश की setRetainInstance(true);लेकिन यह मदद नहीं करता है। मुझे लगा कि ऐसा तब होता है जब मेरी FragmentActivity नष्ट हो जाती है, हालांकि यह तब भी होता है जब मैं लॉग संदेश प्राप्त करने से पहले ऐप खोल देता हूं। क्या कोई विचार हैं?

@Override
protected void onDestroy(){
    Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
    super.onDestroy();
}

एडॉप्टर:

public class PagerAdapter extends FragmentPagerAdapter {
    private List<Fragment> fragments;

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);

        this.fragments = fragments;

    }

    @Override
    public Fragment getItem(int position) {

        return this.fragments.get(position);

    }

    @Override
    public int getCount() {

        return this.fragments.size();

    }

}

मेरा एक टुकड़ा छीन लिया, लेकिन मैं हर छीन लिया और अभी भी काम नहीं करता है ...

public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    handler = new Handler();    
    setHasOptionsMenu(true);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
    context = activity;
    if(context== null){
        Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
    }else{
        Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
    }

}

@Override
public void onActivityCreated(Bundle savedState) {
    super.onActivityCreated(savedState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.my_fragment,container, false);

    return v;
}


@Override
public void onResume(){
    super.onResume();
}

private void callService(){
    // do not call another service is already running
    if(startLoad || !canSet) return;
    // set flag
    startLoad = true;
    canSet = false;
    // show the bottom spinner
    addFooter();
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
    context.startService(intent);
}

private ResultReceiver resultReceiver = new ResultReceiver(null) {
    @Override
    protected void onReceiveResult(int resultCode, final Bundle resultData) {
        boolean isSet = false;
        if(resultData!=null)
        if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
            if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
                removeFooter();
                startLoad = false;
                isSet = true;
            }
        }

        switch(resultCode){
        case MyService.STATUS_FINISHED: 
            stopSpinning();
            break;
        case SyncService.STATUS_RUNNING:
            break;
        case SyncService.STATUS_ERROR:
            break;
        }
    }
};

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    menu.clear();
    inflater.inflate(R.menu.activity, menu);
}

@Override
public void onPause(){
    super.onPause();
}

public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
    boolean loadMore = /* maybe add a padding */
        firstVisible + visibleCount >= totalCount;

    boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;

    if(away){
        // startLoad can now be set again
        canSet = true;
    }

    if(loadMore) 

}

public void onScrollStateChanged(AbsListView arg0, int state) {
    switch(state){
    case OnScrollListener.SCROLL_STATE_FLING: 
        adapter.setLoad(false); 
        lastState = OnScrollListener.SCROLL_STATE_FLING;
        break;
    case OnScrollListener.SCROLL_STATE_IDLE: 
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_IDLE;
        break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
        break;
    }
}

@Override
public void onDetach(){
    super.onDetach();
    if(this.adapter!=null)
        this.adapter.clearContext();

    Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}

public void update(final int id, String name) {
    if(name!=null){
        getActivity().getSupportActionBar().setTitle(name);
    }

}

}

अद्यतन विधि उस समय कहलाती है जब कोई उपयोगकर्ता किसी भिन्न टुकड़े के साथ सहभागिता करता है और getActivity अशक्त लौट रहा होता है। यहाँ विधि अन्य टुकड़ा कह रही है ...

((MyFragment) pagerAdapter.getItem(1)).update(id, name);

मेरा मानना ​​है कि जब ऐप नष्ट हो जाता है तो ऐप को फिर से बनाने के बजाय केवल ऐप को डिफॉल्ट फ्रैगमेंट तक शुरू किया जाता है और फिर व्यूपेजर अंतिम ज्ञात पृष्ठ पर नेविगेट करता है। यह अजीब लगता है, क्या ऐप को केवल डिफ़ॉल्ट टुकड़े में लोड नहीं करना चाहिए?


27
Android के टुकड़े बेकार है!
हमीद्रेजा सादघ

13
भगवान मैं आपके लॉग संदेशों से प्यार करता हूँ: D
oli.G

जवाबों:


120

आप एक समस्या में चल रहे हैं क्योंकि आप तत्काल अपने अंशों के संदर्भों को तत्काल भेज रहे हैं और रख PagerAdapter.getItemरहे हैं, और उन संदर्भों को स्वतंत्र रूप से देखने की कोशिश कर रहे हैं। जैसा कि सेराफ कहते हैं, आपके पास इस बात की गारंटी है कि किसी विशेष समय में एक व्यूपेजर में एक टुकड़े को तत्काल जोड़ा / जोड़ा गया है - इसे कार्यान्वयन विवरण माना जाना चाहिए। एक ViewPager अपने पृष्ठों की लोडिंग को आलसी करता है; डिफ़ॉल्ट रूप से यह केवल वर्तमान पृष्ठ को लोड करता है, और एक को बाईं और दाईं ओर लोड करता है।

यदि आप अपने ऐप को बैकग्राउंड में रखते हैं, तो जो टुकड़े फ़्रेग्मेंट मैनेजर में जोड़े गए हैं, वे अपने आप सहेजे जाते हैं। यहां तक ​​कि अगर आपका ऐप मारा जाता है, तो यह जानकारी तब बहाल होती है जब आप अपने ऐप को फिर से लॉन्च करते हैं।

अब विचार करें कि आपने कुछ पृष्ठ, फ्रेगमेंट ए, बी और सी देखे हैं। आप जानते हैं कि ये खंड प्रबंधक में जोड़ दिए गए हैं। क्योंकि आप उपयोग कर रहे हैं FragmentPagerAdapterऔर नहीं FragmentStatePagerAdapter, ये टुकड़े तब भी जोड़े जाएंगे (लेकिन संभावित रूप से अलग किए गए) जब आप अन्य पृष्ठों पर स्क्रॉल करेंगे।

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

FragmentPagerAdapter getPositionउस विशेष पृष्ठ स्थिति के लिए पहले से ही एक खंड जोड़ा गया है, तो कॉल करने का प्रयास नहीं करेगा । वास्तव में, चूंकि एंड्रॉइड द्वारा बनाए गए टुकड़े को कभी भी हटाया नहीं जाएगा, इसलिए आपको कॉल के साथ इसे बदलने की कोई उम्मीद नहीं है getPosition। इस पर एक हैंडल प्राप्त करना भी इसके लिए एक संदर्भ प्राप्त करना काफी मुश्किल है क्योंकि इसे एक टैग के साथ जोड़ा गया था जो आपके लिए अज्ञात है। यह डिजाइन द्वारा है; आप उन टुकड़ों के साथ खिलवाड़ करने से हतोत्साहित होते हैं, जिन्हें देखने वाला पेजर प्रबंधन कर रहा है। यदि आवश्यक हो, तो आपको एक खंड के भीतर अपने सभी कार्यों का प्रदर्शन करना चाहिए, गतिविधि के साथ संवाद करना चाहिए और एक विशेष पृष्ठ पर स्विच करने का अनुरोध करना चाहिए।

अब, लापता गतिविधि के साथ आपकी समस्या पर वापस। pagerAdapter.getItem(1)).update(id, name)यह सब होने के बाद कॉल करना आपको अपनी सूची में वह टुकड़ा लौटाता है, जिसे अभी तक खंड प्रबंधक में जोड़ा जाना है , और इसलिए इसमें गतिविधि का संदर्भ नहीं होगा। मेरा सुझाव है कि आपकी अपडेट विधि को कुछ साझा डेटा संरचना (संभवतः गतिविधि द्वारा प्रबंधित) को संशोधित करना चाहिए, और फिर जब आप किसी विशेष पृष्ठ पर जाते हैं तो यह इस अपडेट किए गए डेटा के आधार पर खुद को आकर्षित कर सकता है।


1
मुझे आपका समाधान पसंद है क्योंकि यह बहुत ही सुरुचिपूर्ण है और संभवतः मेरे कोड को फिर से रिफ्लेक्टर करेगा क्योंकि आपने कहा था कि मैं 100% तर्क और डेटा को खंड के भीतर रखना चाहता था। आपके समाधान के लिए बहुत अधिक आवश्यकता होगी FragmentActivity में सभी डेटा रखने और फिर प्रदर्शन तर्क को संभालने के लिए प्रत्येक Fragment का उपयोग करें। जैसा कि मैंने कहा कि मुझे यह पसंद है लेकिन टुकड़ों के बीच की बातचीत सुपर भारी है और इसे प्रबंधित करने के लिए कष्टप्रद हो सकता है। वैसे भी आपके विस्तृत विवरण के लिए धन्यवाद। आपने मुझे इससे बेहतर समझा।
मॉरिशी

28
शॉर्ट: एडेप्टर के बाहर एक फ्रैगमेंट के लिए कभी भी संदर्भ न रखें
जूल

2
तो एक गतिविधि उदाहरण FragmentPagerAdapter के उदाहरण तक कैसे पहुँचता है अगर उसने FragmentPagerAdapter को त्वरित नहीं किया? क्या भविष्य के उदाहरण केवल FragmentPagerAdapter और उसके सभी टुकड़े उदाहरणों को पुनः स्थापित नहीं करेंगे? FragmentPagerAdapter को टुकड़ों के बीच संचार का प्रबंधन करने के लिए सभी टुकड़ा इंटरफेस को लागू करना चाहिए?
एरिक एच।

बयान "इस पर एक हैंडल प्राप्त करना भी इसके लिए एक संदर्भ प्राप्त करना बहुत मुश्किल है क्योंकि यह एक टैग के साथ जोड़ा गया था जो आपके लिए अज्ञात है। यह डिज़ाइन द्वारा है; आप टुकड़े टुकड़े करने के साथ खिलवाड़ करने से हतोत्साहित होते हैं जो दृश्य पेजर प्रबंधित कर रहा है। । " गलत है। कॉल करके एक संदर्भ प्राप्त करना बहुत आसान है instantiateItemऔर आपको वास्तव में अपनी गतिविधि में ऐसा करना चाहिएonCreate । यहाँ देखें विवरण: stackoverflow.com/questions/14035090/…
morgwai

सामान्य तौर पर यह लोडिंग के बिना अपने आप को टुकड़ों को
तुरंत हटाने के लिए डोडी है

108

मुझे सरल समाधान मिला जिसने मेरे लिए काम किया।

अपने विखंडन एडॉप्टर को FragmentStatePagerAdapter के बजाय FragmentPagerAdapter और ओवरराइड विधि को आगे बढ़ाएं ताकि वह वापस लौट आए

@Override
public Parcelable saveState()
{
    return null;
}

यह एंड्रॉइड को खंडित करने से रोकता है


एक दिन बाद मुझे दूसरा और बेहतर उपाय मिला।

setRetainInstance(true)अपने सभी अंशों के लिए कॉल करें और कहीं न कहीं उनके संदर्भ सहेजें। मैंने अपनी गतिविधि में स्थिर वैरिएबल में किया, क्योंकि यह सिंगलटैस्क के रूप में घोषित किया गया है और टुकड़े हर समय समान रह सकते हैं।

इस तरह से एंड्रॉइड टुकड़ों को फिर से नहीं बनाते हैं लेकिन समान उदाहरणों का उपयोग करते हैं।


4
धन्यवाद, धन्यवाद, बहुत बहुत धन्यवाद, मेरे पास वास्तव में आपके पास धन्यवाद करने के लिए कोई शब्द नहीं है मिक, मैं पिछले 10 दिनों से इस मुद्दे का पीछा कर रहा था और कई तरीकों की कोशिश की थी। लेकिन इस चार जादुई लाइनों ने मेरी जान बचाई :)

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

इसने मेरे लिए काम किया। ऐप क्रैश होने और रिबूट होने के बाद फ्रेगमेंट और उनके संबंधित विचार उनके लिंक रखते हैं। धन्यवाद!
स्वबल

के setRetainInstance(true)साथ उपयोग कर रहा हूं FragmentPagerAdapter। सब अच्छा काम करता है। लेकिन जब मैं डिवाइस को घुमाता हूं, तब भी एडॉप्टर के टुकड़े होते हैं लेकिन टुकड़े नहीं दिखाए जाते हैं। टुकड़ों के जीवनचक्र के तरीकों को भी नहीं कहा जाता है। क्या कोई मदद कर सकता है?
जोनास

1
आपने मेरा दिन बचाया है !!! अब तक 3 दिन तक इस बग को खोजते रहे। मेरे पास एक सिंगलटेस्क एक्टिविटी के अंदर 2 टुकड़े और "डॉन्ट कीप एक्टिविटीज" फ्लैग इनेबल्ड के साथ व्यूपेजर था। बहुत बहुत धन्यवाद!!!!
मैट्रिक्स

29

मैंने इस तरह से FragmentPagerAdapter के माध्यम से बजाय FragmentManager के माध्यम से सीधे अपने टुकड़े तक पहुंचकर इस मुद्दे को हल किया। सबसे पहले मुझे FragmentPagerAdapter द्वारा उत्पन्न किए गए टुकड़े ऑटो के टैग का पता लगाने की आवश्यकता है ...

private String getFragmentTag(int pos){
    return "android:switcher:"+R.id.viewpager+":"+pos;
}

तब मुझे बस उस टुकड़े का संदर्भ मिलता है और मुझे वैसा ही करने की जरूरत होती है ...

Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1));
((MyFragmentInterface) f).update(id, name);
viewPager.setCurrentItem(1, true);

अपने अंशों के अंदर मैंने सेट किया setRetainInstance(false);ताकि मैं मैन्युअल रूप से सहेजे गए इनस्टांसस्टैट बंडल में मान जोड़ सकूं।

@Override
public void onSaveInstanceState(Bundle outState) {
    if(this.my !=null)
        outState.putInt("myId", this.my.getId());

    super.onSaveInstanceState(outState);
}

और फिर OnCreate में मैं उस कुंजी को पकड़ता हूं और आवश्यक रूप से टुकड़े की स्थिति को पुनर्स्थापित करता हूं। एक आसान समाधान जो कठिन था (मेरे लिए कम से कम) यह पता लगाने के लिए।


मेरे पास आपके समान एक मुद्दा है, लेकिन मैं आपके स्पष्टीकरण को काफी नहीं समझता हूं, क्या आप अधिक विवरण प्रदान कर सकते हैं? मेरे पास और एडॉप्टर हैं जो सूची में टुकड़ों को संग्रहीत करते हैं, लेकिन जब मैं अपने ऐप को हाल के ऐप ऐप्स से फिर से शुरू कर रहा हूं तो बीकॉज क्रैश हो जाता है कुछ अलग-अलग टुकड़े होते हैं। समस्या यहाँ है stackoverflow.com/questions/11631408/…
Georgy Gobozov

3
खंडन संदर्भ में आपका 'मेरा' क्या है?
जोश

हम सभी जानते हैं कि यह समाधान अच्छा दृष्टिकोण नहीं है, लेकिन यह FragmentByTagViewPager में उपयोग करने का सबसे आसान तरीका है ।
यंगजई

यहाँ एक तरीका है कि
असाइनमेंट

26

वैश्विक कामकाजी परीक्षण समाधान।

getSupportFragmentManager()कुछ समय के लिए अशक्त संदर्भ रखता है और देखें पेजर नया नहीं बनाता है क्योंकि यह उसी खंड के संदर्भ को ढूंढता है। तो इस पर आने के लिए यह प्रयोग getChildFragmentManager()सरल तरीके से समस्या हल करता है।

ऐसा न करें:

new PagerAdapter(getSupportFragmentManager(), fragments);

यह करो:

new PagerAdapter(getChildFragmentManager() , fragments);


6
यह तभी संभव होगा, जब आप पेजर एडेप्टर को फ्रैगमेंट के अंदर होस्ट कर रहे हों (और इंस्टेंट कर रहे हों) न कि आपकी गतिविधि के अंदर। इस मामले में आप सही हैं, आपको डिफ़ॉल्ट रूप से चाइल्डफ्रैगमेंट मैनजर का उपयोग करना चाहिए। SupportFragmentManager का उपयोग करना डिफ़ॉल्ट रूप से गलत होगा
Klitos G.

बहुत स्पष्ट। हैक्स का उपयोग किए बिना मेरी समस्या का समाधान किया। धन्यवाद! मेरा कोटलिन संस्करण FragmentStatePagerAdapter(activity!!.supportFragmentManager)एक आसान से देखने के लिए बदल गया FragmentStatePagerAdapter(childFragmentManager):)
एक्ट

7

ViewPager में टुकड़ों के बीच बातचीत करने की कोशिश न करें। आप गारंटी नहीं दे सकते कि अन्य टुकड़ा जुड़ा हुआ है या यहां तक ​​कि मौजूद है। एक्शन के शीर्षक को खंड से बदलने के लिए, आप इसे अपनी गतिविधि से कर सकते हैं। इसके लिए standart इंटरफ़ेस पैटर्न का उपयोग करें:

public interface UpdateCallback
{
    void update(String name);
}

public class MyActivity extends FragmentActivity implements UpdateCallback
{
    @Override
    public void update(String name)
    {
        getSupportActionBar().setTitle(name);
    }

}

public class MyFragment extends Fragment
{
    private UpdateCallback callback;

    @Override
    public void onAttach(SupportActivity activity)
    {
        super.onAttach(activity);
        callback = (UpdateCallback) activity;
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        callback = null;
    }

    public void updateActionbar(String name)
    {
        if(callback != null)
            callback.update(name);
    }
}

ज़रूर अब कोड शामिल होगा ... मैं पहले से ही उन तरीकों को लॉग इन कर रहा हूं। OnDetach को तब कहा जाता है जब OnStop को खंडितता में कहा जाता है। onAttach को FragmentActivity के onCreate से ठीक पहले कहा जाता है।
मॉरिशी

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

ठीक है, इसलिए मैंने अभी इसे पूरी तरह से जांचा और मेरे सभी कोड के बारे में टिप्पणी की ... शीर्षक बदलने के लिए कॉलबैक को छोड़कर। जब ऐप पहली बार लॉन्च किया जाता है और मैं उस स्क्रीन पर जाता हूं, जिसका नाम np बदल गया है। कई एप्लिकेशन लोड करने के बाद फिर शीर्षक वापस आने से कोई बदलाव नहीं होता है। ऐसा लगता है कि कॉलबैक किसी पुरानी गतिविधि के उदाहरण पर प्राप्त हो रहा है। मैं एक और स्पष्टीकरण के बारे में नहीं सोच सकता
मॉरिशी

मैंने इसे नीचे दिए गए टुकड़े के साथ एक समस्या के रूप में बताया और इस मुद्दे को ... code.google.com/p/android/issues/detail?id=19211 । मुझे अभी भी कोई सुराग नहीं है कि इसे कैसे हल किया जाए
मारीसी

5

जब आप व्यूअर को नष्ट करते हैं तो आप टुकड़े हटा सकते हैं, मेरे मामले में, मैंने उन्हें onDestroyView()अपने टुकड़े पर निकाल दिया :

@Override
public void onDestroyView() {

    if (getChildFragmentManager().getFragments() != null) {
        for (Fragment fragment : getChildFragmentManager().getFragments()) {
            getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    super.onDestroyView();
}

धन्यवाद, आपका समाधान काम करता है। यह आवश्यक है कि एडेप्टर (नहीं ) ViewPagerपर भी आधारित हो । इसके अलावा एक और संस्करण काम करता है: उपयोग नहीं , लेकिन एडेप्टर निर्माण से पहले बच्चे के टुकड़े हटा दें । childFragmentManagerfragmentManageronDestroyViewViewPager
CoolMind

4

इसी तरह के मुद्दे की तलाश में कुछ घंटों के बाद मुझे लगता है कि एक और समाधान है। इसने कम से कम मेरे लिए काम किया और मुझे केवल कुछ पंक्तियों को बदलना है।

यह वह समस्या है जो मेरे पास थी, मेरे पास एक दृश्य पेजर के साथ एक गतिविधि है जो दो फ्रेगमेंट के साथ एक FragmentStatePagerAdapter का उपयोग करती है। जब तक मैं गतिविधि को नष्ट करने के लिए मजबूर नहीं करता (डेवलपर विकल्प) या स्क्रीन को घुमाने तक सब कुछ ठीक रहता है। वे विधि getItem के अंदर बनने के बाद मैं दो टुकड़ों का संदर्भ रखते हैं।

उस बिंदु पर गतिविधि फिर से बनाई जाएगी और इस बिंदु पर सब कुछ ठीक काम करता है, लेकिन मैंने अपने सुगंधकों का संदर्भ खो दिया है क्योंकि getItem फिर से कॉल नहीं करता है।

इस तरह मैंने उस समस्या को ठीक किया, FragmentStatePagerAdapter के अंदर:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object aux = super.instantiateItem(container, position);

        //Update the references to the Fragments we have on the view pager
        if(position==0){
            fragTabOne = (FragOffersList)aux;
        }
        else{
            fragTabTwo = (FragOffersList) aux;
        }

        return aux;
    }

यदि एडेप्टर में पहले से ही आंतरिक रूप से इसका संदर्भ है, तो आपको फिर से getItem पर कॉल नहीं मिलेगा और आपको इसे बदलना नहीं चाहिए। इसके बजाय आप इस दूसरी विधि इंस्टेंटइइटम () को देखकर उपयोग किए जा सकने वाले टुकड़े को प्राप्त कर सकते हैं, जिसे आपके प्रत्येक टुकड़े के लिए कहा जाएगा।

आशा है कि किसी की मदद करता है।


यदि आपके पास बहुत सारे टुकड़े हैं तो क्या होगा? क्या आप वास्तव में उनमें से प्रत्येक के लिए एक संदर्भ सहेजना पसंद करेंगे? क्या यह स्मृति के लिए बुरा नहीं है?
Android डेवलपर

यह सब इस बात पर निर्भर करता है कि आप वास्तव में क्या हासिल करना चाहते हैं। आम तौर पर इन दृश्य पेजर के साथ आप एक समय में 3 से अधिक टुकड़े नहीं रखते हैं। बीच में एक और हर तरफ एक। जो मैं चाहता हूं, उसके लिए मुझे वास्तव में अंशों का संदर्भ होना चाहिए, लेकिन मुझे पता है कि मुझे केवल दो से निपटने की आवश्यकता है।
लैंसलेट

0

चूंकि FragmentManager आपके लिए अपने Fragments को पुनर्स्थापित करने का ध्यान रखेगा, जैसे ही onResume () विधि कहा जाता है, मेरे पास गतिविधि के लिए खंड कॉल है और सूची में खुद को जोड़ें। मेरे उदाहरण में मैं अपने पेजर एडेप्टर कार्यान्वयन में यह सब संग्रहीत कर रहा हूं। प्रत्येक टुकड़ा जानता है कि यह स्थिति है क्योंकि यह सृजन पर खंड तर्क में जोड़ा जाता है। अब जब भी मुझे किसी विशेष इंडेक्स पर एक खंड में हेरफेर करने की आवश्यकता होती है, तो मुझे अपने एडॉप्टर से सूची का उपयोग करना होगा।

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

अनुकूलक

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener {

public final String TAG = this.getClass().getSimpleName();

private final int COUNT = 4;

public static final float BASE_SIZE = 0.8f;
public static final float BASE_ALPHA = 0.8f;

private int mCurrentPage = 0;
private boolean mScrollingLeft;

private List<SummaryTabletFragment> mFragments;

public int getCurrentPage() {
    return mCurrentPage;
}

public void addFragment(SummaryTabletFragment fragment) {
    mFragments.add(fragment.getPosition(), fragment);
}

public GrowPagerAdapter(FragmentManager fm) {
    super(fm);

    mFragments = new ArrayList<SummaryTabletFragment>();
}

@Override
public int getCount() {
    return COUNT;
}

@Override
public Fragment getItem(int position) {
    return SummaryTabletFragment.newInstance(position);
}

@Override
public void onPageScrollStateChanged(int state) {}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    adjustSize(position, positionOffset);
}

@Override
public void onPageSelected(int position) {
    mCurrentPage = position;
}

/**
 * Used to adjust the size of each view in the viewpager as the user
 * scrolls.  This provides the effect of children scaling down as they
 * are moved out and back to full size as they come into focus.
 * 
 * @param position
 * @param percent
 */
private void adjustSize(int position, float percent) {

    position += (mScrollingLeft ? 1 : 0);
    int secondary = position + (mScrollingLeft ? -1 : 1);
    int tertiary = position + (mScrollingLeft ? 1 : -1);

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent;
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent;

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp;
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown;

    if (scaleUp < BASE_SIZE)
        scaleUp = BASE_SIZE;

    if (scaleDown < BASE_SIZE)
        scaleDown = BASE_SIZE;

    // Adjust the fragments that are, or will be, on screen
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null;
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null;
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null;

    if (current != null && next != null) {

        // Apply the adjustments to each fragment
        current.transitionFragment(percentIn, scaleUp);
        next.transitionFragment(percentOut, scaleDown);

        if (afterNext != null) {
            afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE);
        }
    }
}

@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {

    // Keep track of which direction we are scrolling
    mScrollingLeft = (oldl - l) < 0;
}
}

टुकड़ा

public class SummaryTabletFragment extends BaseTabletFragment {

public final String TAG = this.getClass().getSimpleName();

private final float SCALE_SIZE = 0.8f;

private RelativeLayout mBackground, mCover;
private TextView mTitle;
private VerticalTextView mLeft, mRight;

private String mTitleText;
private Integer mColor;

private boolean mInit = false;
private Float mScale, mPercent;

private GrowPagerAdapter mAdapter;
private int mCurrentPosition = 0;

public String getTitleText() {
    return mTitleText;
}

public void setTitleText(String titleText) {
    this.mTitleText = titleText;
}

public static SummaryTabletFragment newInstance(int position) {

    SummaryTabletFragment fragment = new SummaryTabletFragment();
    fragment.setRetainInstance(true);

    Bundle args = new Bundle();
    args.putInt("position", position);
    fragment.setArguments(args);

    return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null);

    setupViews();
    configureView();

    return mRoot;
}

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

    if (savedInstanceState != null) {
        mColor = savedInstanceState.getInt("color", Color.BLACK);
    }

    configureView();
}

@Override
public void onSaveInstanceState(Bundle outState)  {

    outState.putInt("color", mColor);

    super.onSaveInstanceState(outState);
}

@Override
public int getPosition() {
    return getArguments().getInt("position", -1);
}

@Override
public void setPosition(int position) {
    getArguments().putInt("position", position);
}

public void onResume() {
    super.onResume();

    mAdapter = mActivity.getPagerAdapter();
    mAdapter.addFragment(this);
    mCurrentPosition = mAdapter.getCurrentPage();

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) {
        mInit = true;
        transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE);
        return;
    }

    if (getPosition() == mCurrentPosition && !mInit) {
        mInit = true;
        transitionFragment(0.00f, 1.0f);
    }
}

private void setupViews() {

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover);
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left);
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right);
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root);
    mTitle = (TextView) mRoot.findViewById(R.id.title);
}

private void configureView() {

    Fonts.applyPrimaryBoldFont(mLeft, 15);
    Fonts.applyPrimaryBoldFont(mRight, 15);

    float[] size = UiUtils.getScreenMeasurements(mActivity);
    int width = (int) (size[0] * SCALE_SIZE);
    int height = (int) (size[1] * SCALE_SIZE);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
    mBackground.setLayoutParams(params);

    if (mScale != null)
        transitionFragment(mPercent, mScale);

    setRandomBackground();

    setTitleText("Fragment " + getPosition());

    mTitle.setText(getTitleText().toUpperCase());
    mLeft.setText(getTitleText().toUpperCase());
    mRight.setText(getTitleText().toUpperCase());

    mLeft.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showNextPage();
        }
    });

    mRight.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showPrevPage();
        }
    });
}

private void setRandomBackground() {

    if (mColor == null) {
        Random r = new Random();
        mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
    }

    mBackground.setBackgroundColor(mColor);
}

public void transitionFragment(float percent, float scale) {

    this.mScale = scale;
    this.mPercent = percent;

    if (getView() != null && mCover != null) {

        getView().setScaleX(scale);
        getView().setScaleY(scale);

        mCover.setAlpha(percent);
        mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE);
    }
}

@Override
public String getFragmentTitle() {
    return null;
}
}

0

मेरा समाधान: मैं लगभग हर दृश्य को सेट करता हूं static। अब मेरा ऐप परफेक्ट इंटर करता है। हर जगह से स्थिर तरीकों को कॉल करने में सक्षम होना शायद एक अच्छी शैली नहीं है, लेकिन कोड के साथ खेलने के लिए क्यों काम नहीं करता है? मैंने SO पर बहुत सारे प्रश्न और उनके उत्तर यहां पढ़े और कोई समाधान सफलता नहीं लाया (मेरे लिए)।

मुझे पता है कि यह मेमोरी, और बेकार ढेर को लीक कर सकता है, और मेरा कोड अन्य परियोजनाओं पर फिट नहीं होगा, लेकिन मुझे इस बारे में डर नहीं लगता है - मैंने विभिन्न उपकरणों और स्थितियों पर ऐप का परीक्षण किया, कोई समस्या नहीं है, एंड्रॉइड प्लेटफ़ॉर्म इसे संभालने में सक्षम लगता है। UI हर सेकंड ताज़ा हो जाता है और यहां तक ​​कि S2 ICS (4.0.3) डिवाइस पर ऐप हजारों जियो-मार्कर को संभालने में सक्षम है।


0

मैं एक ही मुद्दे का सामना करना पड़ा, लेकिन मेरा ViewPager एक TopFragment के अंदर था जिसने एक एडेप्टर का उपयोग करके बनाया और सेट किया था setAdapter(new FragmentPagerAdapter(getChildFragmentManager()))

मैंने इस मुद्दे को onAttachFragment(Fragment childFragment)इस तरह TopFragment में ओवरराइड करके तय किया है :

@Override
public void onAttachFragment(Fragment childFragment) {
    if (childFragment instanceof OnboardingDiamondsFragment) {
        mChildFragment = (ChildFragment) childFragment;
    }

    super.onAttachFragment(childFragment);
}

जैसा कि पहले से ही ज्ञात है (ऊपर दिए गए उत्तर देखें), जब चाइल्डफ्रैग्मेंटरमैन खुद को फिर से बनाता है, तो यह उन टुकड़ों को भी बनाता है जो व्यूपेजर के अंदर थे।
महत्वपूर्ण बात यह है कि उसके बाद, वह onAttachFragment को कॉल करता है और अब हमारे पास नए रीक्रिएट किए गए टुकड़े का संदर्भ है!

आशा है कि यह मेरी तरह इस पुराने क्यू पाने में किसी की मदद करेगा :)


0

मैंने SparceArray में टुकड़ों को बचाकर समस्या का हल किया:

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter {

    SparseArray<Fragment> fragments = new SparseArray<>();

    public SaveFragmentsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.append(position, fragment);
        return fragment;
    }

    @Nullable
    public Fragment getFragmentByPosition(int position){
        return fragments.get(position);
    }

}

0

बस तुम इतना जानते हो...

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

मैं आइटम के एक पेड़ को नेविगेट करने के लिए एक ViewPager का उपयोग कर रहा हूं (एक आइटम का चयन करें और व्यू पेजर स्क्रॉल को दाईं ओर स्क्रॉल करता है, और अगली शाखा प्रकट होती है, वापस नेविगेट करें और पिछले नोड पर लौटने के लिए ViewPager विपरीत दिशा में स्क्रॉल करता है) ।

समस्या तब होती है जब मैं FragmentStatePagerAdapter के अंत से धक्का और पॉप टुकड़े करता हूं। यह देखने के लिए पर्याप्त स्मार्ट है कि आइटम बदल गए हैं, और जब आइटम बदल गया है, तो एक टुकड़ा बनाने और बदलने के लिए पर्याप्त स्मार्ट है। लेकिन जब अडॉप्टर स्टेट को छोड़ने के लिए पर्याप्त स्मार्ट नहीं है, या एडेप्टर आकार में परिवर्तन होने पर आंतरिक रूप से सहेजे गए टुकड़े को ट्रिम करने के लिए पर्याप्त स्मार्ट नहीं है। इसलिए जब आप किसी आइटम को पॉप करते हैं, और अंत में एक नया धक्का देते हैं, तो नए आइटम के टुकड़े को पुराने आइटम के टुकड़े के सहेजे गए राज्य को प्राप्त होता है, जिससे मेरे कोड में पूर्ण तबाही हुई। मेरे टुकड़े ऐसे डेटा को ले जाते हैं, जिन्हें इंटरनेट से रीफैच करने के लिए बहुत सारे काम करने पड़ सकते हैं, इसलिए राज्य को बचाना वास्तव में कोई विकल्प नहीं था।

मेरे पास एक स्वच्छ समाधान नहीं है। मैंने कुछ इस तरह इस्तेमाल किया:

  public void onSaveInstanceState(Bundle outState) {
    IFragmentListener listener = (IFragmentListener)getActivity();
    if (listener!= null)
    {
        if (!listener.isStillInTheAdapter(this.getAdapterItem()))
        {
            return; // return empty state.
        }

    }
    super.onSaveInstanceState(outState);

    // normal saving of state for flips and 
    // paging out of the activity follows
    ....
  }

एक अपूर्ण समाधान क्योंकि नए टुकड़े का उदाहरण अभी भी एक saveState बंडल मिलता है, लेकिन कम से कम यह बासी डेटा नहीं ले जाता है।


0

चूंकि लोग टिप्पणियों को पढ़ने के लिए नहीं आते हैं, यहां एक जवाब है कि ज्यादातर जो मैंने यहां लिखा है उसकी नकल करता है :

समस्या का मूल कारण तथ्य यह है कि एंड्रॉइड सिस्टम getItemउन टुकड़ों को प्राप्त करने के लिए कॉल नहीं करता है जो वास्तव में प्रदर्शित होते हैं, लेकिन instantiateItem। यह विधि पहले एक दिए गए टैब के लिए एक खंड को देखने और पुन: उपयोग करने का प्रयास करती है FragmentManager। केवल अगर यह लुकअप विफल हो जाता है (जो FragmentManagerकि नव निर्मित होने पर केवल पहली बार होता है) तब getItemकहा जाता है। यह स्पष्ट कारणों के लिए है कि प्रत्येक बार जब कोई उपयोगकर्ता अपने डिवाइस को घुमाता है, तो खंडों (जो कि भारी हो सकता है) को दोबारा न बनाया जाए।
इसे हल करने के लिए, Fragment.instantiateअपनी गतिविधि के साथ टुकड़े बनाने के बजाय , आपको इसे करना चाहिए pagerAdapter.instantiateItemऔर इन सभी कॉलों को startUpdate/finishUpdateक्रमशः विधि कॉलों से घिरा होना चाहिए जो कि खंड लेनदेन शुरू / शुरू करते हैं।getItem वे स्थान होने चाहिए जहां वास्तव में अपने संबंधित निर्माणकर्ताओं का उपयोग करके टुकड़े बनाए जाते हैं।

List<Fragment> fragments = new Vector<Fragment>();

@Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        fragments.add(adapter.instantiateItem(viewPager, 0));
        fragments.add(adapter.instantiateItem(viewPager, 1));
        // and so on if you have more tabs...
        adapter.finishUpdate(viewPager);
}

class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.