android - व्यूपेजर में फ्रैगमेंट दिखाई देने पर कैसे निर्धारित किया जाए




android-fragments android-viewpager (15)

व्यूपेजर में फ्रैगमेंट दिखाई देने पर कैसे निर्धारित किया जाए

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

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

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

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

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

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

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


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

public void onHiddenChanged(boolean hidden)

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

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


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

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

     }
}

टुकड़े में GestureDetector.OnGestureListenerand उपयोग करें। GestureDetector.OnGestureListenerand और जब वह आग लगती है तो आपको पता चलेगा कि "यह" टुकड़ा दिखाई दे रहा है


ध्यान दें कि 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
    }
}

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

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

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

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

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


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


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


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

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

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

FragmentStatePagerAdapters और 3 टैब के साथ काम करते समय मुझे एक ही समस्या का सामना करना पड़ा। जब भी पहला टैब क्लिक किया गया था और उसे अन्य टैब पर क्लिक करने के लिए छुपाया गया था तो मुझे डिलाग दिखाना पड़ा।

setUserVisibleHint() अकेले setUserVisibleHint() मौजूदा दृश्य खंड को खोजने में मदद नहीं करता है।

तीसरे टैब से क्लिक करते समय -----> पहला टैब। यह दूसरे खंड के लिए और 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.
    }

}

ViewPager दृश्यमान में Fragment का पता लगाने के लिए, मुझे पूरा यकीन है कि केवल setUserVisibleHint का उपयोग 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 call when viewpager create fragment and when we go to this fragment from
     * background or another activity, fragment
     * NOT call 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 call 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

डेमो परियोजना यहाँ

उम्मीद है कि यह मदद करता है


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

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

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

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

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

दूसरी समस्या यह है कि setUserVisibleHint में !visible दोनों को 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 myUIUpdate() , fragmentVisible myUIUpdate() : सुनिश्चित myUIUpdate() कि myUIUpdate() को केवल तभी बुलाया जाता है जब खंड बनाया जाता है और दिखाई देता है, फिर से शुरू नहीं होता है। जब आप पहली टैब पर होते हैं तो यह समस्या हल करता है, दूसरा टैब बनाया जाता है भले ही यह दिखाई न दे। यह हल करता है और जांच करता है कि क्रिएटिव स्क्रीन कब दिखाई दे रही onCreate

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

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


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

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




android-viewpager