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);
Related
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;
}
}
Thats a big problem for me right now because i need to call a method from an interface
all my fragments in my viewpager are implementing. I need to do something like this:
#Override
public void onPageSelected(int position) {
this.getActivity().getActionBar().setSelectedNavigationItem(position);
FragmentVisible fragment = (FragmentVisible) this.fragmentPager.instantiateItem(this.viewPager, position);
if (fragment != null) {
fragment.fragmentBecameVisible();
}
}
This works for the "normal startup" but when i rotate the screen i get nullpointer exceptions
because onPageSelected gets called before onViewCreated. I need my views to get updated everytime
a fragment gets visible. First i hoped onResume would get called everytime but it doesnt. For that
i implemented the interface:
public interface FragmentVisible {
public void fragmentBecameVisible();
}
Does someone has an idea how to solve this?
Per the FragmentPagerAdapter's setPrimaryItem() method (called when the ViewPager sets the current page), it calls setUserVisibleHint(true) for the current page's fragment. You can override that method in your Fragment and do your fragmentBecameVisible() method in there.
When the action bar has tabs, I've noticed that onTabSelected is called when the activity loads on screen. It also is being called whenever an orientation change occurs. My code queries the database depending on the tab being selected, and displays the query results to the loaded layout.
My problem is when saving tab state, and the current selected tab is 1 or higher, on restore state, since onTabSelected is called by default on the 0 tab, it will be called again when restored state tab is 1 or higher. This makes database query on tab 0 useless.
How to configure android that onTabSelected isn't called on tab creation or at least detect that this call is default and not user triggered?
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
int tabPos = tab.getPosition();
switch(tabPos) {
case 0:
// query database and display result
break;
case 1:
// a different query and display result
break;
case 2: ...
}
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState.containsKey(STATE_SELECTED_TAB)) {
getActionBar().setSelectedNavigationItem(savedInstanceState.getInt(STATE_SELECTED_TAB));
}
super.onRestoreInstanceState(savedInstanceState);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_SELECTED_TAB, getActionBar().getSelectedNavigationIndex());
super.onSaveInstanceState(outState);
}
Added complication:
When the current selected tab is 0, and the orientation changes, onTabSelected is still called twice! Once when the tabs are initially created, and 2nd time when onRestoreState restores the saved tab selected state, even though it is 0.
What I originally supposed was that onTabSelected was called twice, but I was mistaken. It was my fragment onCreateView being called twice, some errors in fragment transaction that added the same fragment twice on orientation change. onTabSelected is called once, and restore state calls onTabReselected is called too when the restored tab is also 0.
After scouring SO and google, I've found this question to have similar cause of problem.
Creating ActionBar tab also calling its selectTab functions
So after reviewing the reference docs on ActionBar from Google's Android site, addTab method is the one responsible for calling onTabSelected by default.
public abstract void addTab (ActionBar.Tab tab)
Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list. If this is the first tab to be added it will become the selected tab.
Parameters
tab Tab to add
Incidentally, other overloaded methods exist that do not call onTabSelected.
public abstract void addTab (ActionBar.Tab tab, boolean setSelected)
So I used these alternative methods instead and it fixed the problem.
However, once the tabs are displayed, the first tab may appear selected even though it's not. Clicking on it will call onTabSelected and not onTabReselected.
I solved it in this way:
Call setupWithViewPager before tabLayout.addOnTabSelectedListener
tabLayout.setupWithViewPager(viewPager)
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {....}
.....
}
because when you call setupWithViewPager, this will internally call
setOnTabSelectedListener(new
ViewPagerOnTabSelectedListener(viewPager));
so you better call it before adding tabSelect Listener to tabLayout
i think you can do this (and ignore any typo please :-)) :
public class MainActivity extends FragmentActivity {
boolean isConfigChanged;
int savedTabIndex;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
if(savedInstanceState.containsKey(STATE_SELECTED_TAB)) {
savedTabIndex = savedInstanceState.getInt(STATE_SELECTED_TAB);
//getActionBar().setSelectedNavigationItem(savedTabIndex); actually you do not need this
}
isConfigChanged = true;
}
// here add actionbar tabs
//...}
and in :
below code checks that if configuration changes and the user selected tab is not zero so this is default call and ignore but if isConfigChanged == true and the user selected tab is 0 you must query DB or if isConfigChanged == false you must query DB because it is first time that app is loading. a quick play may fit it to what you want.
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(isConfigChanged && savedTabIndex != 0){
isConfigChanged = false;
return;
}
isConfigChanged = false;
int tabPos = tab.getPosition();
switch(tabPos) {
case 0:
// query database and display result
break;
case 1:
// a different query and display result
break;
case 2: ...
}
}
remove onRestore
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_SELECTED_TAB, getActionBar().getSelectedNavigationIndex());
super.onSaveInstanceState(outState);
}
I have a ViewPager with 10 pages. When I start the last (10th) page onCreateView() method of my fragment is called. When I swipe to the 9th page onCreateView() is called also. But when I back to the 10th page onCreateView() isn't called. What's wrong?
Try Extending FragmentStatePagerAdapter
That is because a FragmentPagerAdapter keeps in memory every fragment. Hence, when you visit the first time the fragment, onCreate will be invoked but the second time Android will looking for in memory, so it not need invoke onCreate.
If you need run the code in OnCreate every time fragment is displayed, you should move it to getItem(int id)
See offical documentation: http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
Nothing is wrong. The ViewPager already has the page, and so it does not need to create it.
I had the same problem, my solution was to assign again the adapter of the ViewPager instance, just like:
pager.setAdapter(adapter);
This causes a restart of the "mItems" property from the viewPager and removes the cache.
But I don't know if it's a safe solution
You can call the adapter getItem from onPageSelect, which is called also on swipes, and place your code inside the getItem, or even in the onPageSeelect itself.
CommonWare's answer is the best and works like charm:
simple add OnPageChangeListener to your ViewPager item, something like this:
ViewPager viewPager = null;
PagerAdapter pagerAdapter = null;
//Some code come here...
pagerAdapter = new PagerAdapter(); //Or any class derived from it
viewPager = (ViewPager)findViewById(R.id.container);//Connect it to XML
viewPager.setAdapter (mPagerAdapter); //Connect the two
//Next two lines are simply for fun...
//viewager.setPageTransformer(true, new DepthPageTransformer());
//viewPager.setPageTransformer(true, new PaymentZoomOutPageTransformer());
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//This is the right place to connect the pages with a data struct!!!
#Override
public void onPageSelected(int position) {
// Here you can connect the current displayed page with some data..
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
//Here use the inflater to add views/pages
//Don't forget to do:
pagerAdapter.notifyDataSetChanged();
//When you're done...
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