कैसे निर्धारित करें कि जब Fragment ViewPager में दिखाई देता है


754

समस्या: टुकड़ा onResume()में ViewPagerपहले टुकड़ा वास्तव में दिखाई देने लगता है निकाल दिया जाता है।

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

BUT ViewPagerदूसरा टुकड़ा तब बनाता है जब पहली बार दूसरा टुकड़ा कैश करने के लिए दिखाई देता है और जब उपयोगकर्ता स्वाइप करना शुरू करता है तो यह दिखाई देता है।

इसलिए इस onResume()घटना को दिखाई देने से बहुत पहले दूसरे खंड में निकाल दिया जाता है। इसलिए मैं एक ऐसी घटना खोजने की कोशिश कर रहा हूं, जो उचित समय पर एक संवाद दिखाने के लिए दूसरा टुकड़ा दिखाई देने पर आग लग जाए।

यह कैसे किया जा सकता है?


18
"मेरे पास ViewPager और FragmentPagerAdapter के साथ 2 टुकड़े हैं। दूसरा टुकड़ा केवल अधिकृत उपयोगकर्ताओं के लिए उपलब्ध हो सकता है और जब टुकड़ा दिखाई देता है (अलर्ट डायलॉग) तो मुझे लॉगिन करने के लिए उपयोग करना चाहिए।" - IMHO, जो कि भयानक UX है। उपयोगकर्ता को क्षैतिज रूप से स्वाइप करने के कारण डायलॉग को रोकना मुझे प्ले स्टोर पर आपको एक-स्टार रेटिंग देने के लिए प्रेरित करेगा।
कॉमन्सवेयर

क्या केवल "लॉगिन" बटन के साथ TextView में जानकारी प्रदर्शित करना बेहतर है? उस मामले के लिए आपका समाधान क्या है?
4ntoine

5
"डेटा लोड करने की आवश्यकता नहीं है यदि यह प्रदर्शित नहीं किया जाएगा।" - तब आपको इसे अंदर नहीं डालना चाहिए ViewPager। दो-पृष्ठ पेजर में, दोनों पृष्ठ तुरंत लोड हो जाएंगे, चाहे आप इसे पसंद करें या न करें। उपयोगकर्ता अनुभव ViewPagerयह माना जाता है कि सामग्री स्वाइप करने के तुरंत बाद होती है, कुछ समय बाद नहीं। इसीलिए, उस ViewPagerपृष्ठ के आगे एक पृष्ठ आरंभ करता है, जो उस उपयोगकर्ता अनुभव को सुनिश्चित करने में मदद करता है।
कॉमन्सवेयर

2
ऐसा लगता है कि ViewPager पर्याप्त लचीला नहीं है और यह कैशिंग को बंद करने की अनुमति नहीं देता है क्योंकि न्यूनतम setOffscreenPageLimit 1: stackoverflow.com/questions/10073214/… है । इसके लिए कोई कारण न देखें और अपेक्षित व्यवहार (अनिवार्य कैशिंग के मामले में) टुकड़ा बनाने के लिए है, लेकिन जब टुकड़ा दिखाई देता है, तो खंडित अग्नि के onResume ()।
4ntoine

1
थोड़ा देर से, लेकिन उसी मुद्दे का सामना करने वाले किसी व्यक्ति के लिए, आप FragmentViewPager पुस्तकालय (मैं लेखक हूं) की कोशिश कर सकता हूं, जो इस मुद्दे से निपटता है और कुछ अतिरिक्त सुविधाएं प्रदान करता है। एक नमूने के लिए, प्रोजेक्ट के GitHub पृष्ठ या इस स्टैकओवरफ़्लो उत्तर की जांच करें
एस। ब्रुखंडा

जवाबों:


582

कैसे निर्धारित करें कि जब Fragment ViewPager में दिखाई देता है

आप setUserVisibleHintअपने में ओवरराइड करके निम्नलिखित कर सकते हैं Fragment:

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

7
आज के एंड्रॉइड सपोर्ट लाइब्रेरी अपडेट (रिव्यू 11) के लिए धन्यवाद, उपयोगकर्ता को दिखाई देने वाला संकेत आखिरकार तय हो गया है। अब ViewPager के लिए उपयोगकर्ता दृश्यमान संकेत का उपयोग करना सुरक्षित है।
ओएसिस फेंग

58
मैंने पाया है कि setUserVanishHint पद्धति को BEFORE कहा जाता है क्योंकि onCreateView कहा जाता है और इससे किसी भी आरंभीकरण को ट्रैक करना मुश्किल हो जाता है।
AndroidDev

11
@AndroidDev यदि आप संकेत के रूप में सही आने पर कुछ कोड चलाना चाहते हैं, लेकिन जिसे व्यू ट्री पहले से ही प्रारंभ करने की आवश्यकता है, तो isResumed()NPE से बचने के लिए कोड के उस ब्लॉक को लपेटें । मेरे लिए ठीक काम कर रहा है।
रवि थपलियाल

13
यह सिर्फ हास्यास्पद है कि एसडीके को डिफ़ॉल्ट रूप से कुछ प्रदान करने के लिए बहुत सारे अलग-अलग हैक हैं।
माइक ६६

15
setUserVisibleHintअब पदावनत किया गया है
SR

525

अद्यतन : एंड्रॉइड सपोर्ट लाइब्रेरी (रिव्यू 11) ने अंत में उपयोगकर्ता को दिखाई देने वाले संकेत को ठीक कर दिया , अब यदि आप टुकड़ों के लिए समर्थन लाइब्रेरी का उपयोग करते हैं, तो आप गोर के उत्तर द्वारा वर्णित परिवर्तनों को पकड़ने के लिए सुरक्षित रूप से उपयोग getUserVisibleHint()या ओवरराइड कर सकते हैं setUserVisibleHint()

अद्यतन 1 यहाँ एक छोटी सी समस्या है getUserVisibleHint()। यह मान डिफ़ॉल्ट रूप से है true

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

जब आप इसे setUserVisibleHint()लागू करने से पहले उपयोग करने का प्रयास करते हैं तो कोई समस्या हो सकती है । वर्कअराउंड के रूप में आप onCreateइस तरह विधि में मूल्य निर्धारित कर सकते हैं ।

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

पुराना उत्तर:

सबसे उपयोग मामलों में, ViewPagerएक बार में केवल एक पृष्ठ दिखाते हैं, लेकिन पूर्व-संचित टुकड़े भी आप उपयोग कर रहे हैं "दिखाई" राज्य (वास्तव में अदृश्य) के लिए रखा जाता है FragmentStatePagerAdapterमें Android Support Library pre-r11

मैं ओवरराइड करता हूं:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

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


73
GetActivity () का उपयोग करने के लिए केयरफुल रहें, पहली शुरुआत में यह अशक्त होगा।
वोवकाब

10
ध्यान दें कि setUserVanishHint () ने हमेशा FragmentPagerAdapter में ठीक से काम किया है।
लाउलीउली

8
यह समाधान (और भी gorn है) थोड़ा सा पिछड़ रहा है। उन मामलों में जहां दृश्यदर्शी को जल्दी और उत्परिवर्ती समय में स्वाइप किया जाता है, इस पद्धति को विलंब के साथ कहा जाएगा (जब टुकड़ा अब दिखाई नहीं देता / अदृश्य है)।
आसफ

3
मुझे truegetUserVanishHint के लिए मिल रहा है () जब onCreateOptionsMenuकहा जाता है, और जब setUserVisibleHintबुलाया जाता है, तो मेनू अभी तक नहीं बना है ऐसा लगता है। अंततः मुझे दो विकल्प मिलते हैं मेनू का जोड़ा जब मैं केवल उस टुकड़े से मेनू चाहता हूं जो दिखाई दे रहा है। इस संबंध में कोई सुझाव?
cYrixmorten

13
setUserVisibleHintअब पदावनत किया गया
SR

143

यह सामान्य onResume()व्यवहार को बहाल करने के लिए लगता है जो आप उम्मीद करेंगे। यह ऐप छोड़ने के लिए होम की को दबाने के साथ अच्छी तरह से खेलता है और फिर ऐप में फिर से प्रवेश करता है। onResume()एक पंक्ति में दो बार नहीं कहा जाता है।

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}

4
ठीक वही जो मेरे द्वारा खोजा जा रहा था। समस्या को setUserVisibleHintपहले बुलाया जा रहा है onCreateViewऔर कहा जाता है कि setUserVisibleHintअगर एप्लिकेशन पृष्ठभूमि और फिर अग्रभूमि जाता है, तो यह नहीं मिलता है। बहुत बढ़िया! धन्यवाद!
एलेक्स

11
मुझे लगता है कि फोन करना अपने आप में एक बहुत बुरा विचार है, लेकिन अन्यथा, इस पोस्ट के प्रश्न का उत्तर देने के लिए आपको जो कुछ भी आवश्यक है वह सेटयूजरविहीनहिंट फ़ंक्शन में है!
क्वेंटिन जी।

4
मैं नहीं बल्कि एक सादे लागू होता onVisibleToUser()विधि और उस से कहा जाता है onResume()और से setUserVisibleHint(boolean)कॉल करने के बजाय onResume()अपने आप को और जीवन चक्र कॉलबैक के साथ दखल दे। अन्यथा मुझे लगता है कि यह दृष्टिकोण अच्छी तरह से काम करता है, धन्यवाद!
Stephan Henningsen

1
हाँ मैं उपरोक्त टिप्पणियों से सहमत हूँ। सामान्य तौर पर, अपनी कक्षा में सार्वजनिक तरीकों से कॉल करने से बचने की कोशिश करें। एक निजी विधि बनाएं और इसे दोनों सार्वजनिक विधियों से कॉल करें।
जोहान फ्रेंजेन

4
setUserVanishHint पदावनत!
हामिद रजा

70

यहाँ एक और तरीका है onPageChangeListener:

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});

1
यह समाधान अच्छी तरह से काम करता है, धन्यवाद। (V4) विखंडन समर्थन पैकेजों का उपयोग करते हुए पुराने प्लेटफार्मों के लिए उन बिल्डिंग के लिए यह अनुशंसित समाधान होना चाहिए
फ्रैंक यिन

7
यह ध्यान देने योग्य है कि यह आपको अपेक्षित परिणाम नहीं दे सकता है यदि आप एक FragmentStatePagerAdapter का उपयोग कर रहे हैं और getItem () पर एक नया फ़्रैग्मेंट इंस्टेंस का उपयोग कर रहे हैं, क्योंकि आप केवल नए टुकड़े पर राज्य सेट कर रहे होंगे, न कि वह जो व्यूअर पेज मिला
बेन पियर्सन

1
यह समाधान अच्छा है यदि आप कुछ दिखाना चाहते हैं जब टुकड़ा दिखाई देता है। यह एक खंडित दृश्यमान स्थिति नहीं रखता है (मतलब, यह नहीं पता है कि टुकड़ा कब अदृश्य है)।
AsafK

मैं एक ही प्रकार की समस्या हूँ। कृपया समस्या को हल करने में मेरी मदद करें। मेरी पोस्ट: stackoverflow.com/questions/23115283/…
जीत परमार

यह अडैप्टर के लिए अशक्त सूचक अपवाद दे रहा है जिसका उपयोग मैं खंड के iamVouble विधि में कर रहा हूं। मैं नेटवर्क से प्राप्त डेटा को केवल तब सेट करने का प्रयास कर रहा हूं जब टुकड़ा दिखाई दे रहा हो।
zaphod100.10

58

setUserVisibleHint()कभी-कभी पहले बुलाया जाता है onCreateView() और कभी-कभी परेशानी का कारण बनता है।

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

इसलिए यदि आप कुछ अद्यतन करना चाहते हैं visible, जब Fragment है , तो अपना अद्यतन कार्य दोनों में डालें ( onCreate()और setUserVisibleHint():

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

अद्यतन: फिर भी मुझे एहसास हुआ कि myUIUpdate()दो बार कभी-कभी कॉल किया जाता है, इसका कारण यह है, यदि आपके पास 3 टैब हैं और यह कोड 2 टैब पर है, जब आप पहली बार 1 टैब खोलते हैं, तो 2 टैब भी बनाया जाता है, यह भी दिखाई नहीं देता है और myUIUpdate()कहा जाता है। तब जब आप 2 टैब पर स्वाइप करते हैं, तब myUIUpdate()से if (visible && isResumed())कॉल किया जाता है और इसके परिणामस्वरूप,myUIUpdate() एक सेकंड में दो बार फोन किया जा सकता है।

अन्य समस्या है !visibleमेंsetUserVisibleHint कहा जाता हो जाता है दोनों 1) जब आप जब तुम टुकड़ा स्क्रीन पहली बार के लिए स्विच टुकड़ा स्क्रीन और 2) इससे पहले कि यह बनाया जाता है, से बाहर जाना।

समाधान:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

स्पष्टीकरण:

fragmentResume, fragmentVisible: सुनिश्चित करें कि बनाता myUIUpdate()में onCreateView()ही जब टुकड़ा बनाई गई है और दिखाई, फिर से शुरू पर नहीं कहा जाता है। जब आप 1 टैब पर होते हैं, तब भी यह समस्या हल करती है, यदि दिखाई न दे तो भी 2 टैब बनाया जाता है। यह हल करता है और जांचता है कि टुकड़ा स्क्रीन कब दिखाई दे रही है onCreate

fragmentOnCreated: यकीन है कि टुकड़ा दिखाई नहीं देता है, और नहीं कहा जाता है जब आप पहली बार टुकड़ा बनाते हैं। तो अब यह तब होता है जब खंड केवल तब कहा जाता है जब आप खंड से बाहर स्वाइप करते हैं।

अद्यतन आप में यह सब कोड डाल सकते हैं BaseFragmentकोड इस तरह और ओवरराइड विधि।


1
आपका समाधान पूरी तरह से एक परिदृश्य को छोड़कर काम करता है, जब टुकड़ा सामान्य रूप से खोला जाता है और फिर आप इसे किसी अन्य टुकड़े के साथ बदलने के लिए कुछ बटन पर क्लिक करते हैं और फिर बैकस्ट से नए टुकड़े को पॉप करने के लिए वापस क्लिक करते हैं, यह उस स्थिति में setUserVisibleHintकॉल नहीं करता है .. ! और अंदर onCreateViewविधि fragmentVisibleथी false? इतना टुकड़ा खाली दिखा ..! कोई विचार।?
अलाआ अबूजरिफा

28

दृश्यमान Fragmentमें पता लगाने के लिए ViewPager, मुझे पूरा यकीन है कि केवल उपयोग setUserVisibleHint करना पर्याप्त नहीं है।
यह जांचने का मेरा उपाय है कि कोई टुकड़ा दिखाई दे रहा है या अदृश्य है। व्यूपेजर को लॉन्च करते समय, पृष्ठ के बीच स्विच करें, दूसरी गतिविधि / खंड / पृष्ठभूमि / अग्रभूमि पर जाएं

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

विस्तार से आप नीचे लॉगकोट को ध्यान से देख सकते हैं, तो मुझे लगता है कि आप जान सकते हैं कि यह समाधान क्यों काम करेगा

पहला लॉन्च

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

पेज 2 पर जाएं

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

पेज 3 पर जाएं

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

पृष्ठभूमि पर जाएं:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

अग्रभूमि पर जाएं

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

DEMO परियोजना यहाँ

आशा है कि यह मदद करेगा


1
यह काम नहीं करता है जब एक टुकड़े में एक और दृश्य पेजर होता है जिसमें टुकड़ा भी होता है।
शिहाब उद्दीन

क्षमा करें, मैं इस समय उत्तर पोस्ट नहीं कर सकता क्योंकि मेरे पास पर्याप्त समय नहीं है, यदि संभव हो तो कृपया अपनी समस्या के बारे में यहाँ gitub.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master को देखेंSubChildContainerFragmentपता लगाने के लिए प्रयोग किया जाता हैa fragment has another view pager which also consists fragment । आप मिश्रण कर सकते हैं SubChildContainerFragmentऔर ChildContainerFragment1 वर्ग के लिए। आशा है कि यह मदद करेगा। मैं बाद में पूरा जवाब पोस्ट करूंगा
फान वन लिन्ह

26
package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

2
यह स्वीकृत उत्तर होना चाहिए। Android.app.ragment के साथ भी काम करता है, जो एक बहुत बड़ा बोनस है।
राजवी

setUserVisibleHintपदावनत
1

23

में ViewPager2और ViewPagerसंस्करण से androidx.fragment:fragment:1.1.0तुम सिर्फ उपयोग कर सकते हैं onPauseऔर onResumeकॉलबैक निर्धारित करने के लिए जो टुकड़ा वर्तमान उपयोगकर्ता के लिए दिख रहा है। onResumeकॉलबैक तब कहा जाता है जब टुकड़ा दिखाई देता है और onPauseजब वह दिखाई देना बंद हो जाता है।

ViewPager2 के मामले में यह डिफ़ॉल्ट व्यवहार है लेकिन समान व्यवहार को पुराने अच्छे के लिए ViewPagerआसानी से सक्षम किया जा सकता है ।

पहले ViewPager में इस व्यवहार को सक्षम करने के लिए आपको निर्माता के FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTदूसरे तर्क के रूप में पैरामीटर पास करना होगा FragmentPagerAdapter

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

नोट: setUserVisibleHint()विधि और FragmentPagerAdapterनिर्माता एक पैरामीटर के साथ अब एंड्रॉइड जेटपैक से फ्रैगमेंट के नए संस्करण में पदावनत किए गए हैं।


1
अच्छा समाधान के लिए धन्यवाद। मैं लंबे समय से इसकी तलाश कर रहा था। महान कार्य
अनुराग श्रीवास्तव

1
ViewPager2 के लिए विकल्प BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT डिफ़ॉल्ट व्यवहार नहीं है, आपको इसे मैन्युअल रूप से सेट करने की आवश्यकता है। इस विकल्प को सेट करते समय आपके समाधान ने मेरे लिए काम किया। धन्यवाद।
driAn

1
APR 2020 तक यह अद्यतन समाधान है और एक आकर्षण की तरह काम करता है।
प्रकाश

1
@ 4ntoine कृपया इस उत्तर को सही उत्तर के रूप में स्वीकार करने पर विचार करें, क्योंकि अब तक यह सही है और अधिकांश उत्तर विचलित सेट का उपयोग कर रहे हैं। अदृश्य विधि
Jorn Rigter

16

उपवर्ग setPrimaryItem()में ओवरराइड करें FragmentPagerAdapter। मैं इस विधि का उपयोग करता हूं, और यह अच्छी तरह से काम करता है।

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}

1
यह लगभग काम कर गया लेकिन NPE के साथ कई बार समस्याएँ हुईं। नीचे एक विकल्प पोस्ट किया!
गबर


@ रॉबर्ट सेविंग ऑब्जेक्ट ऑब्जेक्ट पहली बार में, और उसी तरह की ऑब्जेक्ट के साथ फॉलो अप कॉल की जाँच करें और अनदेखा करें, जैसे FragmentStateAdapter setPrimaryItem () विधि में करें
wanglugao

12

उसके लिए ओवरराइड करें Fragment.onHiddenChanged()

public void onHiddenChanged(boolean hidden)

जब छिपी हुई अवस्था (जैसा लौटा हो) कहा जाता है isHidden()खंडित ) को बदल दिया जाता है। टुकड़े बाहर छिपे नहीं शुरू होते हैं; यह तब भी कहा जाएगा जब भी टुकड़ा उस से बदल जाता है।

पैरामीटर
hidden- boolean: सच है अगर टुकड़ा अब छिपा हुआ है, तो झूठ अगर यह दिखाई नहीं दे रहा है।


3
इस विधि को अब तक हटा दिया गया है और अब इसका उपयोग नहीं किया जा सकता है
ब्लूव्हील

4
@bewewhile जहाँ आप देखते हैं कि यह पदावनत है? डेवलपर
.android.com

1
मैंने इसे सफलता के साथ इस्तेमाल किया है, और प्रलेखन यह इंगित नहीं करता है कि यह पदावनत है।
ट्रेवर

1
@bluewhile, 4.1 (एपी 16) में इसे अभी तक नहीं हटाया गया है।
dan

8
मैं एपीआई स्तर 19 का उपयोग कर रहा हूं और पुष्टि कर सकता हूं कि जब यह पदावनत नहीं होता है, तो यह विज्ञापित के रूप में काम नहीं करता है। onHiddenChanged को नहीं कहा जाता है जब टुकड़ा दूसरे खंड द्वारा छिपाया जाता है, उदाहरण के लिए।

4

मुझे यह पता चला कि onCreateOptionsMenuऔर onPrepareOptionsMenuकेवल खंड के मामले में वर्णित तरीके वास्तव में दिखाई देते हैं। मुझे ऐसा कोई तरीका नहीं मिला, जो इस तरह का व्यवहार करता हो, मैंने भी कोशिश की OnPageChangeListenerलेकिन यह स्थितियों के लिए काम नहीं किया, उदाहरण के लिए, मुझे एक वैरिएबल आरम्भिक onCreateविधि की आवश्यकता है।

तो इन दो तरीकों का उपयोग इस समस्या के समाधान के लिए किया जा सकता है, विशेष रूप से छोटी और छोटी नौकरियों के लिए।

मुझे लगता है, यह बेहतर समाधान है लेकिन सबसे अच्छा नहीं है। मैं इसका उपयोग करूंगा लेकिन एक ही समय में बेहतर समाधान की प्रतीक्षा करूंगा।

सादर।


2

एक और समाधान यहां पोस्ट किया गया है। कृप्या लार्सन द्वारा पेजरैडिटरी में सेटप्रेमरीइटम को ओवरराइड किया गया। लेकिन इस विधि को प्रत्येक सेटअप के लिए कई बार कहा जाता है। इसके अलावा, मैंने खंडों में विचारों आदि से एनपीई प्राप्त किया, क्योंकि यह इस पद्धति को कहा जाता है पहले तैयार नहीं होता है। निम्नलिखित परिवर्तनों के साथ यह मेरे लिए काम कर रहा है:

private int mCurrentPosition = -1;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);

    if (position == mCurrentPosition) {
        return;
    }

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;

        if (fragment.isResumed()) {
            mCurrentPosition = position;
            fragment.doTheThingYouNeedToDoOnBecomingVisible();
        }
    }
}

2

टुकड़े के अंदर निम्नलिखित कोड जोड़ें

@Override
public void setMenuVisibility(final boolean visible) 
 {
    super.setMenuVisibility(visible);
    if (visible && isResumed()) 
     {

     }
}

यह केवल ViewPagerFragments के लिए काम करता है। सामान्य गतिविधि में अंशों के लिए नहीं।
AndroidGuy

2

मुझे FragmentStatePagerAdapters3 टैब के साथ काम करते समय एक ही समस्या का सामना करना पड़ा । जब भी 1 टैब क्लिक किया गया था और इसे अन्य टैब पर क्लिक करने पर मुझे एक डायलॉग दिखाना था।

अधिभावी setUserVisibleHint()अकेले वर्तमान दिखाई टुकड़ा खोजने के लिए मदद नहीं की।

3rd टैब से क्लिक करने पर -----> 1 टैब। यह 2 टुकड़े के लिए दो बार और 1 टुकड़े के लिए ट्रिगर हुआ। मैंने इसे isResumed () विधि के साथ जोड़ दिया।

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    isVisible = isVisibleToUser;

    // Make sure that fragment is currently visible
    if (!isVisible && isResumed()) {
        // Call code when Fragment not visible
    } else if (isVisible && isResumed()) {
       // Call code when Fragment becomes visible.
    }

}

2

हमारे पास एमवीपी के साथ एक विशेष मामला है जहां टुकड़े को प्रस्तुतकर्ता को सूचित करने की आवश्यकता होती है कि दृश्य दिखाई दे रहा है, और प्रस्तुतकर्ता को डैगर द्वारा इंजेक्ट किया जाता है fragment.onAttach()

setUserVisibleHint()पर्याप्त नहीं है, हमने 3 अलग-अलग मामलों का पता लगाया है जिन्हें संबोधित करने की आवश्यकता है ( onAttach()यह उल्लेख किया गया है ताकि आप जान सकें कि प्रस्तुतकर्ता कब उपलब्ध है):

  1. विखंडन अभी-अभी बना है। सिस्टम निम्नलिखित कॉल करता है:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. पहले से बनाया हुआ और होम बटन दबाया हुआ है। एप्लिकेशन को अग्रभूमि में पुनर्स्थापित करते समय, इसे कहा जाता है:

    onResume()
  3. अभिविन्यास परिवर्तन:

    onAttach() // presenter available
    onResume()
    setUserVisibleHint()

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

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_list, container, false);
    setHasOptionsMenu(true);

    if (savedInstanceState != null) {
        lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION,
              getResources().getConfiguration().orientation);
    } else {
        lastOrientation = getResources().getConfiguration().orientation;
    }

    return root;
}

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

    int orientation = getResources().getConfiguration().orientation;
    if (orientation == lastOrientation) {
        if (getUserVisibleHint()) {
            presenter.onViewBecomesVisible();
        }
    }
    lastOrientation = orientation;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (presenter != null && isResumed() && isVisibleToUser) {
        presenter.onViewBecomesVisible();
    }
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
}

2

द्वारा पता लगा रहे हैं focused view!

यह मेरे लिए काम करता है

public static boolean isFragmentVisible(Fragment fragment) {
    Activity activity = fragment.getActivity();
    View focusedView = fragment.getView().findFocus();
    return activity != null
            && focusedView != null
            && focusedView == activity.getWindow().getDecorView().findFocus();
}

1

मुझे इस समस्या का सामना तब करना पड़ा जब मैं उस घड़ी में आग लगाने की कोशिश कर रहा था, जब उपयोगकर्ता के देखने के लिए व्यूपर में टुकड़ा ऑन-स्क्रीन था।

उपयोगकर्ता द्वारा टुकड़े को देखे जाने से ठीक पहले टाइमर हमेशा शुरू होता था। ऐसा इसलिए है क्योंकि onResume()खंड में विधि को हम खंड को देखने से पहले कहते हैं।

मेरा समाधान onResume()विधि में एक जांच करने के लिए था । मैं एक निश्चित विधि को 'फू' () कहना चाहता था जब टुकड़ा 8 था दृश्य पेजर्स वर्तमान टुकड़ा।

@Override
public void onResume() {
    super.onResume();
    if(viewPager.getCurrentItem() == 8){
        foo();
        //Your code here. Executed when fragment is seen by user.
    }
}

उम्मीद है की यह मदद करेगा। मैंने देखा है कि यह समस्या बहुत अधिक है। यह सबसे आसान समाधान है जो मैंने देखा है। बहुत से अन्य कम एपीआई आदि के साथ संगत नहीं हैं।


1

मेरी भी यही समस्या थी। ViewPagerअन्य खंडित जीवन चक्र की घटनाओं को अंजाम देता है और मैं उस व्यवहार को बदल नहीं सका। मैंने टुकड़ों और उपलब्ध एनिमेशन का उपयोग करके एक साधारण पेजर लिखा। SimplePager


1

मैंने इसका इस्तेमाल किया और यह काम कर गया!

mContext.getWindow().getDecorView().isShown() //boolean

1

मैं बाल खंडों के साथ SectionsPagerAdapter का समर्थन करता हूं ताकि बहुत सारे सिरदर्द के बाद मुझे अंत में इस विषय के समाधान के आधार पर काम करने का संस्करण मिला:

public abstract class BaseFragment extends Fragment {

    private boolean visible;
    private boolean visibilityHintChanged;

    /**
     * Called when the visibility of the fragment changed
     */
    protected void onVisibilityChanged(View view, boolean visible) {

    }

    private void triggerVisibilityChangedIfNeeded(boolean visible) {
        if (this.visible == visible || getActivity() == null || getView() == null) {
            return;
        }
        this.visible = visible;
        onVisibilityChanged(getView(), visible);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!visibilityHintChanged) {
            setUserVisibleHint(false);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        triggerVisibilityChangedIfNeeded(!hidden);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        visibilityHintChanged = true;
        if (isVisibleToUser && isResumed() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        } else if (!isVisibleToUser) {
            triggerVisibilityChangedIfNeeded(false);
        }
    }

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

    @Override
    public void onStop() {
        super.onStop();
        triggerVisibilityChangedIfNeeded(false);
    }

    protected boolean isReallyVisible() {
        return visible;
    }
}

उल्लेख करने के लिए भूल जाते हैं, एक बार जब आप बच्चे के टुकड़े को माता-पिता के टुकड़े से जोड़ते हैं, तो सेट करें ।setUserVanishHint (सच);
एंडोक्टोरी

और बच्चे के टुकड़े जोड़ने के लिए getFragmentManager () के बजाय getChildFragmentManager () का उपयोग करना न भूलें।
एंडोक्टोरी

0

ध्यान दें कि setUserVisibleHint(false)गतिविधि / विखंडन रोक पर नहीं कहा जाता है। आपको अभी भी register/unregisterकिसी भी श्रोता / आदि को ठीक से शुरू करने / रोकने की जाँच करने की आवश्यकता होगी ।

इसके अलावा, setUserVisibleHint(false)यदि आपका टुकड़ा किसी गैर-दिखाई देने वाली स्थिति में शुरू होता है , तो आपको मिलेगा ; आप unregisterउस मामले में पहले कभी पंजीकृत नहीं होने के बाद से वहां नहीं जाना चाहते ।

@Override
public void onStart() {
    super.onStart();

    if (getUserVisibleHint()) {
        // register
    }
}

@Override
public void onStop() {
    if (getUserVisibleHint()) {
        // unregister
    }

    super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser && isResumed()) {
        // register

        if (!mHasBeenVisible) {
            mHasBeenVisible = true;
        }
    } else if (mHasBeenVisible){
        // unregister
    }
}

0

इसे लागू करने का एक सरल तरीका यह जांचना है कि टुकड़ा जाने से पहले उपयोगकर्ता लॉग इन है या नहीं ।

अपने MainActivity में आप onNavigationItemSelected मेथड के अंदर कुछ ऐसा कर सकते हैं ।

 case R.id.nav_profile_side:


                if (User_is_logged_in) {

                    fragmentManager.beginTransaction()
                            .replace(R.id.content_frame
                                    , new FragmentProfile())
                            .commit();
                }else {

                    ShowLoginOrRegisterDialog(fragmentManager);

                }

                break;

हालाँकि, यदि आप नेविगेशन ड्रावर का उपयोग कर रहे हैं, तो दराज में चयन प्रोफ़ाइल में बदल जाएगा, हालांकि हम प्रोफाइलफ्रैगमेंट में नहीं गए हैं।

चयन को वर्तमान चयन में रीसेट करने के लिए नीचे दिए गए कोड को चलाएं

        navigationView.getMenu().getItem(0).setChecked(true);

-4

मैं संबंधित FragmentStatePagerAdapter की गणना पद्धति को ओवररोड करता हूं और इसे छिपाने के लिए पृष्ठों की संख्या की कुल संख्या घटा देता है:

 public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
 {   
     private List<Fragment> _fragments;

     public int TrimmedPages { get; set; }

     public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }

     public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
     {
         _fragments = fragments;

         TrimmedPages = 0;
     }

     public override int Count
     {
         //get { return _fragments.Count; }
         get { return _fragments.Count - TrimmedPages; }
     }
 }

इसलिए, अगर ViewPager में शुरू में 3 टुकड़े होते हैं, और केवल पहले 2 को तब तक दिखाया जाना चाहिए जब तक कि कुछ शर्त पूरी नहीं हो जाती है, ट्रिममेडपेजेस को 1 पर सेट करके पृष्ठ की संख्या को ओवरराइड करें और इसे केवल पहले दो पृष्ठ दिखाना चाहिए।

यह अंत में पृष्ठों के लिए अच्छा काम करता है, लेकिन अभ्यस्त शुरुआत या मध्य में लोगों के लिए वास्तव में मदद करता है (हालांकि ऐसा करने के बहुत सारे तरीके हैं)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.