In my application, I have a ViewPager which holds many swipeable Tabs with Fragments inside. I use the setUserVisibleHint method to detect when a Fragment comes to the screen. This works great when the user swipes between tabs but it does not work on the first load. To run the code in the method I have to swipe to left and then back to the first Fragment because the setUserVisibleHint method is called before the onCreateView method.
Do you have any ideas how I can run this code after the first Fragment is visible? Is there a method in the ViewPager or something else?
You can't (and shouldn't) rely on setUserVisibleHint for this. Instead, you should be using a ViewPager.OnPageChangeListener to get callbacks for when a page becomes visible. E.g.
viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// do your work
}
});
Note: You can use ViewPager.SimpleOnPageChangeListener if you don't need to listen for all callbacks.
Update
setOnPageChangeListener method is now deprecated, use addOnPageChangeListener instead
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// do your work
}
});
BELOW WORKED FOR ME
Please create a global view like this
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//inflate view layout
view =inflater.inflate(R.layout.your_fragment, container, false);
// return view
return view;
}
and use this
#Override
public void setUserVisibleHint(boolean isUserVisible)
{
super.setUserVisibleHint(isUserVisible);
// when fragment visible to user and view is not null then enter here.
if (isUserVisible && view != null)
{
onResume();
}
}
and In OnResume method
#Override
public void onResume() {
super.onResume();
if (!getUserVisibleHint()) {
return;
}
//do your stuff here
}
You have to call manually mViewPagerListener.onPageSelected(0); in onCreateView in your root activity/fragment.
I dont understand what you need exactly but you can detect the page of the current fragment by calling ViewPager.getCurrentItem that return integer.
Hope it helps.
Good luck.
Well, its workaround more, and maybe its better to setup OnPageChangeListener, but it works for me. The task is to determine whether the current fragment is visible at creation. You can use the menu to do this. In your fragment:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true); // set current fragment has its own options menu
}
and this
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
...
if(isMenuVisible()) { // menu should be visible if current fragment is visible right now
setUserVisibleHint(true); // manually set value to true
}
...
return view;
Maybe someone will find it usefull. This works for me.
I check your problem,but I find out the onCreateView method is called before the
setUserVisibleHint method
enter code here
because the setUserVisibleHint method is called before the onCreateView method
Related
I have a viewpager attached to a tabbed layout and its on page changed listener looks like this
private void setPageChangeListener(FragmentStatePagerAdapter pagerAdapter) {
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener () {
#Override
public void onPageSelected(int position) {
FragmentLifecycleHelper fragment = (FragmentLifecycleHelper) pagerAdapter.getItem(position);
if (fragment != null) {
fragment.onResumeFragment();
}
}
});
}
Now in my child fragments i create the views like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mFragmentView = inflater.inflate(R.layout.drawer_account, container, false);
Log.d(TAG, "OnCreateView()");
printLog();
return mFragmentView;
}
which saves a reference to the view (i assume getView() does the same).
Whatsoever, now the called interface method is supposed to change the icon and the fab onClick listener of the hosting activity, the problem is whenever onResume is called the view reference is null.
This is my onResumeFragment()
/********************* INTERFACE METHODS ******************************/
#Override
public void onResumeFragment() {
Log.d(TAG,"onResumeFragment()");
printLog();
if(mFragmentView != null){
((MainActivity) getActivity()).setFabIcon(MainActivity.FAB_ICON.PLUS);
((MainActivity) getActivity()).setFabClickListener(v -> {
// TODO
});
}
}
printLog() just prints the current status of the view reference.
Whenever i log this onCreateView of Fragment 1 gets called when i am in fragment 0 or 3 (so neighbour fragments) onResume then gets called when i actually switch to the correct view. But then getView() and my own reference both return null and i get a NPE if i try to access the hosting activity
I am using this method but it is not working for first fragment but on swiping from second to first fragment it working fine. please help me in this.
thanks
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){ //do Something
}
}
This is how it works
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//inflate view layout
view =inflater.inflate(R.layout.your_fragment, container, false);
// return view
return view;
}
and use this
#Override
public void setUserVisibleHint(boolean isUserVisible)
{
super.setUserVisibleHint(isUserVisible);
// when fragment visible to user and view is not null then enter here.
if (isUserVisible && view != null)
{
onResume();
}
}
and inside onResume put this code
#Override
public void onResume() {
super.onResume();
if (!getUserVisibleHint()) {
return;
}
//do your stuff here
}
You really shouldn't rely on the order in which setUserVisibleHint is called when using the support version. From the docs:
Note: This method may be called outside of the fragment lifecycle. and thus has no ordering guarantees with regard to fragment lifecycle method calls.
A similar question has some approaches on this.
I faced this issue when I was loading data on viewPager Fragments as and when they were visible.To load data only when a particular fragment was visible , I relied on
setUserVisibleHint(isVisibleToUser:Boolean) without realising at what point in fragment lifecycle it was being called.
As a result, I was all clueless about a day and two, why the hell where all my variables(present in onCreateView()) were null.Only After going through some stack Answers I realised the mistake I was committing.
setUserVisibleHint() was called even before onCreateView() was called.
So the work around is this . See the highest voted answer here.The guy managed it with Booleans.
Hope it helps all the future visitors and save their time.
I have 3 tabs which I've implemented using fragments. Let's say every time I swipe to Tab2, I want the same code to be executed, that is, I want the Tab2 content to be reloaded each time. The reason behind this is that in Tab1, the user has some options by which he can change what is to be displayed in tab2. Hence each time he goes from Tab2 to Tab1, and back to Tab2...some changes in Tab2 can be expected.
I tried putting this code into onCreateView() ...but it appears in my app that onCreateView() is only being called once at the beginning and never again. I've learnt that another way is to use onPageClickListener . Now heres my doubt. I've implemented a ViewPager class in MainActivity to create my tabs. So my onPageListener is also present in MainActivity.java and not in the Tab2.java class.
So supposing I make a textView in Tab2.xml .... and I write code for it in onPageListener expecting it to control the textView in Tab2 when I swipe to Tab2. But how does the app know that this code in onPageListener is for Tab2? onPageListener is for all tabs, so how I specify which tab this code is for?
One way to do this is to create an Interface which all of the Fragments in your ViewPager implement. Something like:
public interface TabSelectedListener {
void onSelected();
}
Then your have each of the Fragments implement it:
public class TabTwoFragment extends Fragment implements TabSelectedListener {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
// instantiate any member classes
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
// inflate the view and bind any View items (TextViews, etc)
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
// do any setup of your view to get it into it's initial state
}
#Override
void onSelected() {
// any code which you want to run when the Fragment is selected in the ViewPager
// note that this is the method we created in the TabSelectedListener interface
}
}
Then in your pageChangeListener you would need to get the selected Fragment from the Adapter and cast it to your Interface so you can call the method:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
TabSelectedListener listener = (TabSelectedListener) adapter.getItem(position);
listener.onSelected();
}
#Override
public void onPageScrollStateChanged(int state) {}
});
This way your Activity doesn't have to know which Fragment does what. You can also add any code you want to each Fragment which will get run when they are focussed.
We all know that when using ViewPager with Fragment and FragmentPagerAdapter we get 3 Fragment loaded: the visible one, and both on each of its sides.
So, if I have 7 Fragments and I'm iterating through them to see which 3 of them are the ones that are loaded, and by that I mean onCreateView() has already been called, how can I determine this?
EDIT: The Fragment doesn't have to be the one that the ViewPager is showing, just that onCreateView() has already been called.
Well logically, this would be a reasonable test if onCreateView has been called:
myFragment.getView() != null;
Assuming you a have a reference to all of the fragments in the pager iterate, them and check if they have a view.
http://developer.android.com/reference/android/app/Fragment.html#getView()
Update
The above answer assumes that your fragments always create a view, and are not viewless fragments. If they are then I suggest sub classing the fragment like so:
public abstract class SubFragment extends Fragment
{
protected boolean onCreateViewCalled = false;
public boolean hasOnCreateViewBeenCalled()
{
return onCreateViewCalled;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup Container, Bundle state){
onCreateViewCalled = true;
return null;
}
}
Just bear in mind that further sub classes will have to call super or set the flag themselves should they override onCreateView as well.
I added an interface to Fragment. Looks like:
protected OnCreateViewCallback createViewCallback = null;
public void setCreateViewCallback(OnCreateViewCallback createViewCallback) {
this.createViewCallback = createViewCallback;
}
public interface OnCreateViewCallback {
void onCreateView();
}
In my onCreateView():
//initialize your view.
if (createViewCallback != null) {
createViewCallback.onCreateView();
createViewCallback = null;
}
return mainView;
From my activity:
if (ocrFragment.getView() == null) {
ocrFragment.setCreateViewCallback(new MainScreenFragment.OnCreateViewCallback() {
#Override
public void onCreateView() {
ocrFragment.ocrImage(picture, false);
}
});
} else {
ocrFragment.ocrImage(picture, false);
}
If you are trying to perform something after onCreateView is called, use onViewCreated:
Called immediately after onCreateView(LayoutInflater, ViewGroup,
Bundle) has returned, but before any saved state has been restored in
to the view. This gives subclasses a chance to initialize themselves
once they know their view hierarchy has been completely created. The
fragment's view hierarchy is not however attached to its parent at
this point.
public void onViewCreated(View view, Bundle savedInstanceState) {
MyActivity myActivity = (MyActivity) getActivity();
MyActivity.newAsyncTask(mPar);
}
You could also check for Fragment.isVisible() because a Fragment is in visible state when it's in the offscreen page limit of a ViewPager.
Edit: But it just really depends on what you really want to achieve with your question. Perhaps some kind of update to all UIs in your Fragments when their UI is ready?
EDIT:
Just another addition, you could listen to onViewCreated() and set a flag. Or notify your Activity and do further work (getActivity() will return your Activity at this point). But really, better state what you want to accomplish with your question.
I have a ViewPager. My FragmentPageAdapter returns the position of the Viewpager in the getItem() methode. But after rotating the Screen the methode returns no value. Why? If I understood it right, everytime you rotate the screen OnCreateView() is called, but why doesn't it return the value any more? Could someone point out how to solve this? Thank you
Edit: My FragmentPageAdapter:
public Fragment getItem(int position) {
return Fragment_results.newInstance(position);
}
My Fragment:
public static Fragment_results newInstance(int i) {
Fragment_results fragment = new Fragment_results();
fragment.mContent = i +"";
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_result, null);
((TextView) view.findViewById(R.id.text)).setText(mContent);
The position is set to 0 upon creating of the ViewPager instance and whenever you set a new adapter. When onCreateView() is called, you're rebuilding the entire app essentially. In order to revert back to the position it was, you must first use onSavedInstanceState(Bundle savedInstanceState) and store the item position via the Bundle.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("pageItem", myViewPager.getCurrentItem());
}
Then in onCreate, restore the viewPager's state like so:
if (savedInstanceState != null) {
myViewPager.setCurrentItem(savedInstanceState.getInt("pageItem", 0));
}