viewPager = (ViewPager) findViewById(R.id.tabanim_viewpager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
public void setupViewPager(ViewPager upViewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new ViewTodayFragment(),"Daily");
adapter.addFrag(new ViewWeekFragment(),"Weekly");
adapter.addFrag(new ViewMonthFragment(),"Monthly");
adapter.addFrag(new ViewYearFragment(),"Yearly");
viewPager.setAdapter(adapter);
}
I want to refresh fragment every time when tab changing.
inside fragment this is my code
public class ViewTodayFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
RecyclerView recyclerView;
RecyclerView.LayoutManager mLayoutManager;
RecyclerView.Adapter mAdapter;
// TODO: Rename and change types of parameters
View v;
ListView listview;
private ArrayList<DailyModel> mItems;
public static String datesel="a";
public ViewTodayFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragment_view_today, container, false);
recyclerView =(RecyclerView)v.findViewById(R.id.view_today_lv_today);
recyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
loadData();
return v;
}
#Override
public void onResume() {
super.onResume();
loadData();
}
public void loadData(){
mItems=new ArrayList<DailyModel>();
List<Income> ll = null;
Log.d("ALLL",datesel);
if(datesel.equals("a")){
Calendar cw = Calendar.getInstance();
SimpleDateFormat format1 = new SimpleDateFormat("dd-MM-yyyy");
String currentDay=format1.format(cw.getTime());
ll=new IncomeHandler(getActivity()).getIncomeByThisDay(currentDay);
}else{
ll=new IncomeHandler(getActivity()).getIncomeByThisDay(datesel);
}
for (int i = 0; i < ll.size(); i++) {
DailyModel a=new DailyModel();
a.setCategory(ll.get(i).getWay());
a.setDescription(ll.get(i).getDes());
a.setType(ll.get(i).getType());
a.setBank("Bank");
a.setAmount(ll.get(i).getAmount());
mItems.add(a);
}
mAdapter = new DailyAdapter(mItems);
recyclerView.setAdapter(mAdapter);
}
}
So like this there are 4 tabs so i want to refresh every time it change the tab.I try to do that inside OnResume().But it also not working.
Create your pager adapter like this.
class PagerAdapter extends FragmentPagerAdapter {
private final List<String> fragmentTitleList = new ArrayList<>();
public PagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
public void addFragmentTitle(String title) {
fragmentTitleList.add(title);
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new ViewTodayFragment();
} else if (position == 1) {
return new ViewWeekFragment();
} else if(position == 2){
return new ViewMonthFragment();
} else{
return new ViewYearFragment();
}
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Now you need to add fragment like this
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
adapter .addFragmentTitle("Daily");
//..... add rest of the fragment title.
viewPager.setAdapter(pagerAdapter );
hope this will help you. though i didn't try this. just writing please let me know if you get any error.
I solved this type issue currently.Please reffer my Code line by line.it might be Help you.
Thanks.
ViewPager viewPager;
ViewPagerAdapter adapter;
viewPager = (ViewPager) findViewById(R.id.tabanim_viewpager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabanim_tabs);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
adapter.getItem(position).onResume();
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
public void setupViewPager(ViewPager upViewPager) {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new ViewTodayFragment(),"Daily");
adapter.addFrag(new ViewWeekFragment(),"Weekly");
adapter.addFrag(new ViewMonthFragment(),"Monthly");
adapter.addFrag(new ViewYearFragment(),"Yearly");
viewPager.setAdapter(adapter);
}
Implement ViewPager.OnPageChangeListener http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html
I solved a similar problem. My four tabs (a sports timer with athletes - skiers or cyclsits - starting at intervals) also needed to communicate with each other. For example once a race is started, the set up controls must be disabled.
My project was based on Google's SlidingTabsColors sample.
I created a simple, one method interface called Refreshable and each of my tabs (Fragments) implemented its refresh method.
In my SlidingTabsFragment (extends Fragment) I override instantiateItem so I can capture a reference to each tab's fragment.
// Here we can safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger).
//
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
/**
* Save Fragment references in an array with the MainActivity
* so that other tabs can be refreshed (state updated) depending on
* changes to other tabs.
*/
MainActivity a = (MainActivity)getActivity();
a.addFragment((Refreshable)createdFragment);
return createdFragment;
}
My main is an observer of my data model, but yours may different.
This is the main's method to "register" the fragments.
/**
* Accepts the specified fragment and adds to the list of "Refreshable" Fragments.
* If it is already registered, it is not added a second time.
*
* #param fragment
* the Fragment (Observer) to add.
*/
public void addFragment(Refreshable fragment) {
if (fragment == null) {
throw new NullPointerException("fragment == null");
}
synchronized (this) {
if (!mRefreshables.contains(fragment))
mRefreshables.add(fragment);
}
}
In my case, this code is in the update method of my Observer. My data has changed (race started, interval countdown expired, finisher recorded) and I can refresh all tabs. I cannot call from one tab to another because of race (no pun intended) conditions.
/**
* Call refresh on every Fragment to update state.
*/
for (Refreshable refreshable : array) {
refreshable.refresh();
}
Each tab's refresh method does its own thing.
You may not need an Observer co-ordinating all tabs, but at least you'll have a reference to them and a method other than onResume (which didn't work for me either) to call and have the content refresh itself.
your answer....you must use
viewPager.setAdapter(adapter);
when your selected tab is change.
Related
I have a View pager. The user can choose how many differents pages he can have.
The pages are all the same layout but it will just load different data.
Here is my fragment adapter :
public class FragmentAdapter extends FragmentPagerAdapter
{
private final List<Fragment> lstFragment = new ArrayList<>();
private final List<String> lstTitles = new ArrayList<>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return lstFragment.get(i);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return lstTitles.get(position);
}
#Override
public int getCount() {
return lstTitles.size();
}
public void AddFragment (Fragment fragment , String title)
{
lstFragment.add(fragment);
lstTitles.add(title);
}
}
And here is the code to call the fragment multiple time :
FragAdapter = new FragmentAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.main_tabs_pager);
toolbar = (Toolbar) findViewById(R.id.main_page_toolbar);
tabLayout = (TabLayout) findViewById(R.id.main_tabs);
String[] Fragments = {"Frag1", "Frag2", "Frag3", "Frag4"};
for (int i=0; i<Fragments.length; i++)
{
((FragmentAdapter) FragAdapter).AddFragment(new MenuFragment(),Fragments[i]);
}
viewPager.setAdapter(FragAdapter);
tabLayout = (TabLayout) findViewById(R.id.main_tabs);
tabLayout.setupWithViewPager(viewPager);
So it works fine. But the only problem is that I don't know how to know the difference in code between the differents fragments.
Exemple :
The frag1 must load 5 pictures about the sea
The frag2 must load 8 pictures about the sun
How can I tell the fragment what to do? I tried to pass in the constructeur the arguments by exemple
public MenuFragment(int numberofpictures, String picturesthemes)
{
// Required empty public constructor
}
but the constructors must be empty because it is not called again when fragment is destroyed and recreated...
does anyone has an idea? thanks
UPDATE
I don't know if that is the good way but here is the way I did it :
In main activity I created :
for (int i=0; i<Fragments.length; i++)
{
Bundle parameters = new Bundle();
parameters.putInt("myInt", i);
Fragment menuFragment = new MenuFragment();
menuFragment.setArguments(parameters);
((FragmentAdapter) FragAdapter).AddFragment(menuFragment, Fragments[i]);
}
Which give a everyfragment the the int i which is a reference to the title.
Then I simply wrote this function :
public String getName (int i)
{
return Fragments[i];
}
which return the title based on the int that the fragment got thanks to the bundle
Then, In the MenuFragment() I used this :
private void fillinthelist()
{
myInt = getArguments().getInt("myInt");
String test = ((MainActivity) getActivity()).getName(myInt);
ListOfProgrammes.add(new Modele_carte(test));
}
so it gets the int from the bundle and make a like to it thanks to the function in MainActivity
Is it the good way to do it? It seems to work
You can attach a Bundle containing the parameters with setArguments(Bundle) :
Bundle parameters = new Bundle();
parameters.putInt("myInt", <int_value>);
Fragment menuFragment = new MenuFragment();
menuFragment.setArguments(arguments);
((FragmentAdapter) FragAdapter).AddFragment(menuFragment, Fragments[i]);
A common practice is to build and attach the Bundle in a fragment's class static factory method.
The fragment can use getArguments() to retrieve the parameters.
private int myInt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myInt = getArguments().getInt("myInt");
}
I am having trouble with below code, i have 5 tabs in my application and when i am trying to switch through them i just noticed that onCreateView is called multiple times. Now first i did saw multiple post about similar issue where multiple times onCreateView is called, but mine is lit bit different, in my application onCreateView is called based on number of tabs i am switching. For example if i am DOWNLOAD tab and i switch to FAVORITE, onCreateView will be called 3 times. If i do same action from settings it will be called 4 times. Same thing happens with CANDIDATE and other tab.
Similar Posts -
1 - OnCreateView called multiple times / Working with ActionBar and Fragments
2 - Android fragment OnCreateView called twice
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
private Boolean exit = false;
private static final int REQUEST = 112;
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
mTitle.setText(toolbar.getTitle());
getSupportActionBar().setDisplayShowTitleEnabled(false);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
mContext = MainActivity.this;
setupTabIcons();
}
#Override
protected void onResume()
{
super.onResume();
}
private void setupTabIcons() {
tabLayout.getTabAt(0).setIcon(getResources().getDrawable(R.drawable.settings));
tabLayout.getTabAt(1).setIcon(getResources().getDrawable(R.drawable.download));
tabLayout.getTabAt(2).setIcon(getResources().getDrawable(R.drawable.register));
tabLayout.getTabAt(3).setIcon(getResources().getDrawable(R.drawable.profile));
tabLayout.getTabAt(4).setIcon(getResources().getDrawable(R.drawable.favwhite));
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new SettingsFragment(), getResources().getString(R.string.settings_tab));
adapter.addFragment(new DownloadFragment(), getResources().getString(R.string.download_tab));
adapter.addFragment(new RegisterFragment(), getResources().getString(R.string.register_tab));
adapter.addFragment(new ProfileFragment(), getResources().getString(R.string.profile_tab));
adapter.addFragment(new ProfileFragment(), getResources().getString(R.string.favorites_tab));
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(0);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}
Update -
To add more when it is run 4 times the data on screen is 4 times which is duplicate.
Fragement Code -
public class RegisterFragment extends Fragment{
public RegisterFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_register, container, false);
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
}
}
}
By default view pager load the fragment in the following order.
Currently selected position fragment. Visible
Cache for the (selected position - 1) fragment, if any. Not visible
Cache for the (selected position + 1) fragment, if any. Not visible
Reason for this is, to make smooth animation from one fragment to another without lagging, view pager caches the previous and the next fragment. To confirm this log the position in the getItem() method in the view pager adapter.
As a result of above. When launching activity your viewpager is loading position 0 and 1. ie loading 2 fragment. There is no fragment in -1 position.
I am developing an android weather application that includes a Tablelayout with 3 pages. The adapter is a FragmentStatePagerAdaptater and in each of them is a Fragment that contains a RecyclerView.
The application receives and processes the weather datas in an asynctask and then updates the ViewPager with the received datas.
My question is to know that it is the best practice when initializing the ViewPager, ie, I initialize the ViewPager in my MainActivity OnCreate() with empty datas in Fragment because I do not yet have the weather datas at this moment.
Once the data received I notify the ViewPager that the datas has changed and then in the FragmentStatePagerAdapter in getItemPosition() method I perform the update of the RecyclerView of the fragments. The RecyclerView are updated with a method equivalent of a contrustor that pass the new datas in the RecyclerViewAdaptater and a notifyDataSetChanged() to notify that the data has changed.
But maybe a best practice would be to wait for receiving the weather datas and then initialize the Viewpager adapter with the datas.
After if the user requests a data updates only notify the ViewPager that the data has changed as is already the case in my application.
This is the code of my approach :
MainActivity OnCreate() :
viewPager = (ViewPager)findViewById(R.id.viewpager);
viewPager.setOffscreenPageLimit(2);
SampleFragmentPagerAdapter adapter = new SampleFragmentPagerAdapter(getSupportFragmentManager());
adapter.addFrag(PageFragment.newInstance(0), "today");
adapter.addFrag(PageFragment.newInstance(1), "tomorrow");
adapter.addFrag(PageFragment.newInstance(2), "7 days");
viewPager.setAdapter(adapter);
Asynctask onPostExecute(Climat climat)
mSwipeRefreshLayout.setRefreshing(false);
if (climat != null) {
MainActivity.climat = climat;
viewPager.getAdapter().notifyDataSetChanged();
// Set page 1
viewPager.setCurrentItem(0);
}
FragmentStatePagerAdaptater :
class SampleFragmentPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>(3);
private final List<String> mFragmentTitleList = new ArrayList<>(3);
SampleFragmentPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return mFragmentList.size();
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
Log.i(TAG, "Fragment getItem position " + position);
return mFragmentList.get(position);
}
void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
// Refresh fragment
#Override
public int getItemPosition(Object object) {
PageFragment f = (PageFragment ) object;
if (f != null) {
f.update();
}
return super.getItemPosition(object);
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
return mFragmentTitleList.get(position);
}
}
PageFragment :
public class PageFragment extends Fragment implements Updateable {
private static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
private Climat climat = null;
private RecyclerView rv1;
private RecyclerView rv2;
private RecyclerView rv3;
private ClimatAdaptateurToday climatAdaptateurTodayRv1;
private ClimatAdaptateurToday climatAdaptateurTodayRv2;
private ClimatAdaptateur climatAdaptateur;
// newInstance constructor for creating fragment with arguments
public static PageFragment newInstance(int page) {
PageFragment fragment = new PageFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
fragment.setArguments(args);
return fragment;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments() != null ? getArguments().getInt(ARG_PAGE) : 0;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
switch (mPage) {
case 0:
rv1 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv1.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv1 = new LinearLayoutManager(getActivity());
rv1.setLayoutManager(layoutRv1);
rv1.setItemAnimator(new DefaultItemAnimator());
climatAdaptateurTodayRv1 = new ClimatAdaptateurToday(climat, mPage);
climatAdaptateurTodayRv1.setHasStableIds(true);
rv1.setAdapter(climatAdaptateurTodayRv1);
break;
case 1:
rv2 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv2.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv2 = new LinearLayoutManager(getActivity());
rv2.setLayoutManager(layoutRv2);
rv2.setItemAnimator(new DefaultItemAnimator());
climatAdaptateurTodayRv2 = new ClimatAdaptateurToday(climat, mPage);
climatAdaptateurTodayRv2.setHasStableIds(true);
rv2.setAdapter(climatAdaptateurTodayRv2);
break;
case 2:
rv3 = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
rv3.setHasFixedSize(true);
// use a linear layout manager
RecyclerView.LayoutManager layoutRv3 = new LinearLayoutManager(getActivity());
rv3.setLayoutManager(layoutRv3);
rv3.setItemAnimator(new DefaultItemAnimator());
climatAdaptateur = new ClimatAdaptateur(null,
null, null);
climatAdaptateur.setHasStableIds(true);
rv3.setAdapter(climatAdaptateur);
break;
}
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.content_main, container, false);
}
// To update fragment in ViewPager, we should implement a public method for the fragment,
// and do updating stuff in this method.
#Override
public void update() {
this.climat = MainActivity.climat;
if (climat != null) {
switch (mPage) {
case 0:
climatAdaptateurTodayRv1.updateData(climat, mPage);
climatAdaptateurTodayRv1.notifyDataSetChanged();
rv1.smoothScrollToPosition(0);
break;
case 1:
climatAdaptateurTodayRv2.updateData(climat, mPage);
climatAdaptateurTodayRv2.notifyDataSetChanged();
rv2.smoothScrollToPosition(0);
break;
case 2:
climatAdaptateur.updateData(climat.getClimatInfoDailyArray(),
climat.getTempsArray(), climat.getLieux());
climatAdaptateur.notifyDataSetChanged();
rv3.smoothScrollToPosition(0);
break;
}
}
}
}
interface Updateable {
void update();
}
Thanks for your help !!
I am using Tab layout with view pager and adding fragments dynamically. Now I need to update a TextView which present in ViewPagers fragment, according to swipe of fragment or click of tab. Suppose if I click on first tab or swipe my fragments then TextView in OperatorPlanList fragment is updtate with text like "I just click".
Note - TextView is present in layout of fragment.
When I try to find position by using position in get view method, FragmentStatePageAdapter, then I found out it update position twice when first or last fragment is seen.
So how can I update my fragment dynamically according to current fragment seen on screen in ViewPager???
This is my FragmentStatePagerAdapter adapter.
class ViewPagerFinder extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerFinder(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position){
return mFragmentList.get(position);
}
#Override
public int getCount() {
System.out.println("getcount>>>" + mFragmentList.size());
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
This is how I am setting up ViewPager.
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerFinder(getSupportFragmentManager());
for(int i = 0; i<numberOfPlans;i++){
adapter.addFragment(new OperatorPlanList(),"Plan");
//This is how I am setting up my fragments.
viewPager.setAdapter(adapter);
}
}
This is how I am setting up my Tabs.
private void setupTabIcons() {
System.out.println("number of plans ="+numberOfPlans);
for(int i = 0; i<numberOfPlans;i++){
HashMap<String,String> map = applist.get(i);
String planName = map.get("plan_name");
TextView tabOne = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null);
tabOne.setText(planName);
tabLayout.getTabAt(i).setCustomView(tabOne);
}
}
This is how I am calling my methods.
viewPager = (ViewPager)findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setupTabIcons();
Create an interface that all of the tab fragments will implement
public interface TabFragment {
void onTabSwiped(int position);
}
In the class that hosts the ViewPager have it implement ViewPager.OnPageChangeListener
Inside your ViewPagerFinder, during the instantiateItem() keep a list of TabFragment's (Create the fragment and cast it to the new interface, THEN call super).
Now in your Class that implements OnPageChangeListener, call the method onTabSwiped on all of your TabFragments passing in the position inside the overridden method onPageSelected(int position).
Now you will get a callback to each fragment during swiping, so you can check if its the correct position and perform whatever you need to do if it is the correct page.
There are ways to make this more efficient, but it's just a quick way to get it working. It will all be similar
-- You could also just get the fragment from your list inside your adapter during onPageSelected(int position), then call the interface method.
#Override
public void onPageSelected(int position){
pagerAdapter.mFragmentList.get(position).onTabSwiped();
}
I was just wondering if it is the normal behaviour of viewpager and its adapter to always call the getItem() method for index 0 and 1, even if I immediately set a current position.
Here is my code:
mNewsPagerAdapter = new NewsDetailPagerAdapter(getChildFragmentManager());
mNewsPagerAdapter.updateNewsList(news);
mViewPager = (ViewPager) mView.findViewById(R.id.horizontal_view_pager);
mViewPager.setPageMargin(2);
mViewPager.setPageMarginDrawable(R.color.black);
mViewPager.setAdapter(mNewsPagerAdapter);
mViewPager.setCurrentItem(mCurrentPositionPager, false);
If I switch from my overview activity to my detail activity with this viewpager, the adapter always calls the getItem() method for position 0 and 1 and after that the getItem() method for the position of mOriginalPosition and its neighbors. I was wondering if this is the correct behaviour or if I missed something to implement it in a right way. Thanks for your help :)
Edit: Added my adapter code
public class NewsDetailPagerAdapter extends FragmentStatePagerAdapter {
private SparseArray<Fragment> mPageReferenceMap = new SparseArray<Fragment>();
private ArrayList<News> mNewsList;
public NewsDetailPagerAdapter(FragmentManager fm) {
super(fm);
}
/**
* Setzt die neuen News.
**/
public void updateNewsList(ArrayList<News> list) {
mNewsList = list;
}
#Override
public Fragment getItem(int position) {
Log.d("debug", "getItem position:" + position);
News newsItem = mNewsList.get(position);
NavigationFragment fragment = new NavigationFragment();
mPageReferenceMap.put(position, fragment);
return fragment;
}
#Override
public int getCount() {
return mNewsList.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public Fragment getFragment(int position) {
return mPageReferenceMap.get(position);
}
}
It is normal (and intelligent in my opinion).
ViewPager class has one property named mOffscreenPageLimit with default value of 1. This number determines how many pages on the left and on the right of the current page that the Viewpager will preload. For instance, you have 10 pages, current position is 5 and mOffcreenPageLimit is 1, the page at position 4 and 6 will be loaded.
You could change this property by calling this method
viewpager. setOffscreenPageLimit(int)
If you pass in an integer that is smaller than 1, it has no effect.
Yes, this is the normal behaviour of the ViewPager, because it will always try to stay ahead of the user by rendering tabs that limit with the drawing area. I personally don't recommend creating a custom ViewPager as you are almost sure to break functionality unless you really know what you are doing. Your adapter class should look something like this:
public class YourCustomPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList = new ArrayList<>();
private List<String> titleList = new ArrayList<>();
public WizardPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title) {
fragmentList.add(fragment);
titleList.add(title);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
super.getPageTitle(position);
return titleList.get(position);
}
#Override
public int getCount() {
return fragmentList.size();
}
}
and you should add your fragments as such:
#Override
public void onCreate(Bundle savedInstanceState) {
...
YourCustomPagerAdapter adapter = new YourCustomPagerAdapter (getSupportFragmentManager());
adapter.addFragment(FragmentOne.newInstance(), "Frag 1");
adapter.addFragment(FragmentTwo.newInstance(), "Frag 2");
viewPager.setAdapter(adapter);
...
}
Actually this is the normale behavior. In fact, as soos as you associate the ViewPager with the adapter, the adapter creates the first visibile layout (index 0) end the next one (index 1). This is done by default in the "setAdapter". Then, when you set a different position, the adapter will instantiate the fragment at the selected index, the previous one and the next one.
This is the usual ViewPager setAdapter code:
public void setAdapter(PagerAdapter adapter) {
if (mAdapter != null) {
mAdapter.setViewPagerObserver(null);
mAdapter.startUpdate(this);
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
mAdapter.destroyItem(this, ii.position, ii.object);
}
mAdapter.finishUpdate(this);
mItems.clear();
removeNonDecorViews();
mCurItem = 0;
scrollTo(0, 0);
}
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
mExpectedAdapterCount = 0;
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.setViewPagerObserver(mObserver);
mPopulatePending = false;
final boolean wasFirstLayout = mFirstLayout;
mFirstLayout = true;
mExpectedAdapterCount = mAdapter.getCount();
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else if (!wasFirstLayout) {
populate();
} else {
requestLayout();
}
}
if (mAdapterChangeListener != null && oldAdapter != adapter) {
mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
}
}
In order to change the ViewPager behavior, you could extend the classic ViewPager overriding the setAdapter method and set the mCurrItem to the desired position.
I hope it helped
Edit:
After different tests, we found a solution.
If the ViewPager adapter is set after ViewPager layout become visible, items 0 and 1 are load.
If you want to avoid this behavior but you can't set the adapter before the layout become visible (because you are waiting for data), than you can use this workaround:
1) Set the ViewPager visibility initially to GONE
2) After you receive all the data, you update the adapter and you set the current item value
3) Finally you set the ViewPager visibility to VISIBLE
Here you can find an example:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.detail_overview_fragment, container, false);
final int position = getArguments().getInt("position");
final ViewPager viewPager = (ViewPager) v.findViewById(R.id.viewpager);
viewPager.setVisibility(View.GONE);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
viewPager.setAdapter(new PagerAdapter(getChildFragmentManager()));
viewPager.setCurrentItem(position);
viewPager.setVisibility(View.VISIBLE);
}
},5000);
return v;
}
i think the error is the adapter:
/**
* Setzt die neuen News.
**/
public void updateNewsList(ArrayList<News> list) {
//mNewsList = list;
mNewsList.clear();
mNewsList.addAll(list);
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
this.notifyDataSetChanged();
}
error reason :this list is diffent entity for that adapter.