I have 10 tabs in my activity. As soon as I open the activity the first fragment gets displayed but the method (AsyncTask) present in the next fragment gets called. And if I go to the next tab say 3rd tab then the method present in the 4th fragment gets called and so on.
I don't understand this behavior. Please help!
You must know how the viewPager works populating the fragment in the different positions
When you start on the position 0, then the fragment on the position 0 and the one of the position 1 are created.
Then when you swipe to the position 1 the fragment on the 2 position is created, so you have now the three fragments created on the different positions (0,1,2..assuming you have only 3 pages on the viewPager).
We swipe to the position 2, the last one, and the fragment on the first position (0) get destroy, so we have now the fragments on the positions 2 and 3.
This is how Fragment LifeCycle Works
you can set mViewPager.setOffscreenPageLimit(2); // 2 is just an example to limit it
If you want some code to execute when Fragment become Visible to User add part of code in setUserVisibleHint method
By default it is viewpager.setOffscreenPageLimit(1) , meaning View pager will by default load atleast 1 on the right and one on the left tab of current tab.
It is done so, mostly because there is a point when you slide viewpager, when certain area of both tabs is visible. For those smooth transitions preloading is required.
You cannot set it viewpager.setOffscreenPageLimit(0).
The only way out is to use this method setUserVisibleHint
add this to your fragment
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
// fragment is no longer visible
}
}
This will be called only when that particular tab is visible to user, so only then you can call all loading function.
check sample example
Put your AsyncTask method inside this.
You can override setMenuVisibility like this:
#Override
public void setMenuVisibility(final boolean visible) {
if (visible) {
//execute AsyncTask method
}
super.setMenuVisibility(visible);
}
Happy coding!!
Override below method and move your code for Aync task into this instead of onStart() or onCreateView.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Load your data here or do network operations here
isFragmentLoaded = true;
}
}
Related
Explaining my problem :
I spend much time but I can not get this to work.I have view pager in main activty that contains three fragments using (Tabhost).My ViewPagerAdapter class extend FragmentStatePagerAdapter.
The problem I'm facing that my OnResume() Method is not called when I swipe the View .And I want to update the view of viewpager's fragment on swipe.
My OnResume() method is only called when i click on the ListView item and back again . but when I press OnLongClick on the ListView other fragments are not refreshed .
Note : I know that this question was asking before but none of those solutions helped me .
Note 2: When my phone goes to sleep then after unlocking phone 2nd fragment calling onResume()
My OnResume() Method in the first tab :
#Override
public void onResume() {
super.onResume();
adapterLogin.notifyDataSetChanged();
}
My OnResume() Method in the second Tab:
#Override
public void onResume() {
super.onResume();
adapterLogin.UpdateView(databaseHelper.getAllVoitureFavourite(1,username));
adapterLogin.notifyDataSetInvalidated();
adapterLogin.notifyDataSetChanged();
}
My UpdateView() Method in the BaseAdapter :
public void UpdateView(List<Voiture> items) {
this.voitureList = items;
notifyDataSetInvalidated();
notifyDataSetChanged();
}
Screen shot of my App for more understanding mu issue :
Any help will be appreciated.
Use setUserVisibleHint(boolean isVisibleToUser).
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Do your stuff here
}
}
The behavior you describe is how ViewPager works, there is nothing wrong with it.
Also, if you check the source code of the ViewPager class you will notice that the minimum offscreenPageLimit is 1. Setting it to 0 simply does nothing as it falls back to the default value 1.
What you can do is to add a TabHost.OnTabChangeListener so that on each swipe have the appropriate method called.
mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
#Override
public void onTabChanged(String tabId) {
switch (mTabHost.getCurrentTab()) {
case 0:
//fragment 1 update()
break;
case 1:
//fragment 2 update()
break;
case 2:
//fragment 3 update()
break;
}
}
});
if you set offscreen page limit = 0 then this on swipe onviewcreate or onviewcreated method also call again and again this may disturb the flow of your app best approach which I use is using interface method override in your fragment and on page change invoke this override method that will work fine for you
If your ViewPager only has 2 pages, then neither of the fragments will pause during a swipe, and onResume() will never be called.
I believe that it always retains the neighbor pages by default, meaning it has a page limit of 1.
You could try setting the number of pages that it retains to 0.
mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(0);
I'm using viewPager to create a layout with tabs and in each tab I use a Fragment. One of them is to get all de user contacts and put it on a ListView. But I want to call de AssyncTask only if the Fragment is displayed on screen.
is there a method to do it?
TY
Start your AsyncTask in onStart which is the method called once the fragment becomes visible to the user. onCreateView will be called even if the fragment doesn't become visible.
See also: http://developer.android.com/reference/android/app/Fragment.html#onStart()
I would however recommend to use a loader instead of AsyncTask (e.g. an AsyncTaskLoader http://developer.android.com/reference/android/content/AsyncTaskLoader.html).
you can use
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser)
{
//things to do when fragment is visible
}
}
Even it is called before onCreateView.
You can check to see if it's visible with .isVisible() something like this
MyFragmentClass fragment = (MyFragmentClass) getSupportFragmentManager().findFragmentByTag("fragmentTAG");
if (fragment != null && fragment.isVisible()) {
// call AsyncTask
}
In my application, I have a ViewPager which holds many swipeable Tabs with Fragments inside. Is there a method like onResume which is called every time the fragment comes to the screen? onResume, onCreateView and so on are called after the Fragment is created and not if it comes to the screen, so they do not work for me. Which method can I use for my problem?
You can use setUserVisibleHint method in your fragment:
#Override
public void setUserVisibleHint(boolean isVisibleToUser){
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
// Your fragment is visible
}
}
I'm using a ViewPager to display 2 Fragments as tabs. Once the according activity is loaded, both fragments are loaded immediatly, while only the first one is visible to the user.
Therefore view.isShown() is not sufficent for testing, as this method returns true for the second fragment which is not visible to the user.
ViewAsserts.assertOnScreen(decorView, view) seems to behave the same way and is therefore useless for solving this problem.
I'm aware that some similar questions have been asked, but none of their answers is satisfying my needs. So how to test this behavior (using Robotium)?
Solution:
I solved it according to Leon's suggestion by using a flag within the fragment like this:
private static boolean isVisibleToUser = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
MyFragment.isVisibleToUser = isVisibleToUser;
}
public static boolean isVisibleToUser() {
return isVisibleToUser;
}
implementing it as a static method I can use it in my test this way:
assertTrue(MyFragment.isVisibleToUser());
the only drawback to this solution is that I have to implement these 2 methods in every single Fragment I want to test this way... any improvements?
You could override setUserVisibleHint inside your fragment like this:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
//Fragment is selected in ViewPager
//Put your "on appear" validation/loading here
}
}
This method will fire every time you show or hide the fragment in the ViewPager.
As opposed to view.isShown() this method does take a "loaded but not visible" state into account.
use OnPageChangedListener to detect changes and maintain a reference to the currently visible fragment/page.
http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html
Alternatively GetCurrentItem() may work for you as detailed here: How do you get the current page number of a ViewPager for Android?
I have a ViewPager, each page is a Fragment view. I want to test if a fragment is in a visible region. the Fragment.isVisible only test
the fragment is attached to a activity
the fragment is set to visible
the fragment has been added to a view
The ViewPager will create 3 (by default) fragment and all three of them meet the above criteria, but only one is actually visible to the user (the human eyes)
This is what I use to determine the visibility of a fragment.
private static boolean m_iAmVisible;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
m_iAmVisible = isVisibleToUser;
if (m_iAmVisible) {
Log.d(localTAG, "this fragment is now visible");
} else {
Log.d(localTAG, "this fragment is now invisible");
}
}
You're right there is a better way to do this!
Have a look at the FragmentPagerAdapter javadoc online and you'll see there is a method setPrimaryItem(ViewGroup container, int position, Object object):void doing exactly what you need.
From the javadoc
public void setPrimaryItem (ViewGroup container, int position, Object object)
Called to inform the adapter of which item is currently considered to
be the "primary", that is the one show to the user as the current
page.
Parameters container The containing View from which the page will be
removed. position The page position that is now the primary.
object The same object that was returned by instantiateItem(View,
int).
Note on scroll state
Now if you implement this and start debugging to get a feel of when exactly this is called you'll quickly notice this is triggered several times on preparing the fragment and while the user is swiping along.
So it might be a good idea to also attach a ViewPager.OnPageChangeListener and only do what has to be done once the viewpagers scroll state becomes SCOLL_STATE_IDLE again.
For my purposes, it worked to use ViewPager.OnPageChangeListener.onPageSelected() in conjunction with Fragment.onActivityCreated() to perform an action when the Fragment is visible. Fragment.getUserVisibleHint() helps too.
I'm using "setMenuVisibility"-Method for resolving this Problem. As every Fragment can have actionbar-items this is the part where you can determine which Fragment is currently visible to the user.
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (!visible) {
//not visible anymore
}else{
yay visible to the user
}
}
What is wrong with using getView().isShown() to find out if a Fragment is actually visible?
isVisible()
Can still return true even if the fragment is behind an activity.
I'm using the following:
if (getView() != null && getView().isShown()) {
//your code here
}
If you know what "page" each fragment is attached to you could use ViewPager.getCurrentItem() to determine which fragment is "visible".
In my case i a have to do some work on the first fragment when the fragment is visible to the user
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(viewPager.getAdapter() instanceof YourPager)
{
Fragment fragemnt=((YourPager)viewPager.getAdapter()).getFragment(0); //getFragment(int index) custom method
if( fragemnt instanceof YourFragment)
{
((YourFragment)fragemnt).methodWhochShouldBeCalledAfterUIVisible();
}
}
}
setUserVisibleHint probably may not be called, onHiddenChanged may be called not every time when another fragment is being closed. So, you may rely on onResume (and onPause), but it is usually called too often (for example, when you turn on a device screen). Also in some situations it is not called, you should manage current fragment in host activity and write:
if (currentFragment != null) {
currentFragment.onResume();
}
Kotlin:
if (userVisibleHint) {
// the fragment is visible
} else {
// the fragment is not visible
}
Java
if (getUserVisibleHint()) {
// the fragment is visible
} else {
// the fragment is not visible
}
https://developer.android.com/reference/android/app/Fragment.html#getUserVisibleHint()
https://stackoverflow.com/a/12523627/2069407