In bottom navigation with three tabs (Home, Dashboard, Notifications). Each bottom navigation tab is a fragment. The first tab ie. Home fragment contains another top navigation tabs having four tabs (Tab 1, Tab 2, Tab 3, Tab 4).
The problem
When navigate from Home tab to Notifications tab directly and come back to Home tab, Tab1/which ever tab previously selected tab (top navigation tabs) the content of the tab is not loaded.
When swipe the tabs from Tab 1 (Home fragment tab) all the way to Notification tab and swipe back, on reaching the Tab 4 the content of the tab is not loaded and on first swipe from Tab 4 to Tab 3 the swipe does not take to Tab 3. The tab indicator just move a little and on second swipe it goes to Tab 3 as expected.
The app contains a lot of code so I'll just link the full code to Github.
For quick reference here are my codes
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
NavigationView navigationView;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
viewPager.setCurrentItem(0);
return true;
case R.id.navigation_dashboard:
viewPager.setCurrentItem(1);
return true;
case R.id.navigation_notifications:
viewPager.setCurrentItem(2);
return true;
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Hide the activity toolbar
getSupportActionBar().hide();
//Initializing viewPager
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
final BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
switch (position){
case 0:
navigation.setSelectedItemId(R.id.navigation_home);
break;
case 1:
navigation.setSelectedItemId(R.id.navigation_dashboard);
break;
case 2:
navigation.setSelectedItemId(R.id.navigation_notifications);
break;
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void setupViewPager(ViewPager viewPager) {
BottomNavPagerAdapter adapter = new BottomNavPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FirstFragment());
adapter.addFragment(new SecondFragment());
adapter.addFragment(new ThirdFragment());
viewPager.setAdapter(adapter);
}
}
FirstFragment.java (Home)
public class FirstFragment extends Fragment {
private TabLayout tabLayout;
private ViewPager firstViewPager;
public FirstFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_first, container, false);
firstViewPager = (ViewPager) rootView.findViewById(R.id.viewpager_content);
tabLayout = (TabLayout) rootView.findViewById(R.id.tabs);
tabLayout.setupWithViewPager(firstViewPager);
setupViewPager(firstViewPager);
return rootView;
}
private void setupViewPager(ViewPager viewPager) {
TabViewPagerAdapter adapter = new TabViewPagerAdapter(getActivity().getSupportFragmentManager());
adapter.addFragment(new Tab1Fragment(), "Tab 1");
adapter.addFragment(new Tab1Fragment(), "Tab 2");
adapter.addFragment(new Tab1Fragment(), "Tab 3");
adapter.addFragment(new Tab1Fragment(), "Tab 4");
viewPager.setAdapter(adapter);
}
}
Questions
What's wrong in code ?
How to solve this issues? (The layout is a requirement) or is there any better approach to come up with the layout from the screenshot?
Actually, it is a common mistake - you're using FragmentManager of your Activity inside the fragment, but since this fragment contains another child fragments you have to use FragmentManager of the fragment itself. So the fix is simple - you just have to change getActivity().getSupportFragmentManager() to getChildFragmentManager() inside your fragments, so the code will be:
private void setupViewPager(ViewPager viewPager) {
TabViewPagerAdapter adapter = new TabViewPagerAdapter(getChildFragmentManager());
...
...
viewPager.setAdapter(adapter);
}
And this should work as expected.
Try use getChildFragmentManager() instance of getSupportFragmentManager().
Related
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 have implemented tab layout in android.I have 3 tabs-settings, clock, lost devices. While scrolling, the contents of tab get updated, but not the headings.But if I chose any tab heading, the content get displayed.
When I start the app, it shows 3 tabs. Suppose current highlighted tab is clock. When I scroll right, the contents of settings tab is displayed, but the current highlighted tab is still clock. It should be settings as per my need.
my code is
This is the main activity->
public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSelectedListener{
//This is our tablayout
private TabLayout tabLayout;
//This is our viewPager
private ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Adding toolbar to the activity
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Initializing the tablayout
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//Adding the tabs using addTab() method
tabLayout.addTab(tabLayout.newTab().setText("clock"));
tabLayout.addTab(tabLayout.newTab().setText("settings"));
tabLayout.addTab(tabLayout.newTab().setText("lost devices"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
//Initializing viewPager
viewPager = (ViewPager) findViewById(R.id.pager);
//Creating our pager adapter
Pager adapter = new Pager(getSupportFragmentManager(), tabLayout.getTabCount());
//Adding adapter to pager
viewPager.setAdapter(adapter);
//Adding onTabSelectedListener to swipe views
tabLayout.setOnTabSelectedListener(this);
}
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
}
Pager->
public class Pager extends FragmentStatePagerAdapter {
//integer to count number of tabs
int tabCount;
//Constructor to the class
public Pager(FragmentManager fm, int tabCount) {
super(fm);
//Initializing tab count
this.tabCount= tabCount;
}
//Overriding method getItem
#Override
public Fragment getItem(int position) {
//Returning the current tabs
switch (position) {
case 0:
clock tab1 = new clock();
return tab1;
case 1:
settings tab2 = new settings();
return tab2;
case 2:
map tab3 = new map();
return tab3;
default:
return null;
}
}
//Overriden method getCount to get the number of tabs
#Override
public int getCount() {
return tabCount;
}
}
It is due to TabLayout and ViewPager is separate each other. You need to update their status in case of one of them make change. You have done with update ViewPager status when tab is selected. But vice versa you need to update Tablayout when ViewPager scroll.
Add listener for viewPager: viewPager.addOnPageChangeListener(this);. It will require you implement some methods.
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// don't use in that's case
}
#Override
public void onPageSelected(int position) {
// update Tablayout here
TabLayout.Tab tab = tabLayout.getTabAt(position);
if (tab != null) {
tab.select();
}
}
#Override
public void onPageScrollStateChanged(int state) {
// don't use in that's case
}
Don't forget to remove the listener if no longer use it
viewPager.removeOnPageChangeListener(this);
Hope it help !
you have setup your viewpager with tab layout. add this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
.....
......
....
//Adding adapter to pager
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
//Adding onTabSelectedListener to swipe views
tabLayout.setOnTabSelectedListener(this);
}
I am working on the application in which I added a Navigation Drawer with Tabs. So, for the Navigation Drawer menu option I create a fragment for each option and it works well...
In my main Activity I inflate my TabFragment (contains fragment_1, or fragment_2) as UI, and on the Fragment_1 there is one Button and when I click the button I want to open the Navigation Drawer fragment....
My Navigation Drawer fragment opens fine, but the problem is when my Navigation drawer fragment is open my titleBar doesn't change as per the fragment title...
Note: I know when we open a fragment we have to replace the fragment View to the new fragment View but I want to open a Fragment as an Activity
Picture1: When I click on Button the Second Picture appears, but I want to change the TitleView and remove the Navigation drawer icon.
Edit
In my MainActivity, I inflate Tabfragment as
mFragmentTransaction=mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.containerView, new TabFragment()).commit();
MyTabFragment :
public class TabFragment extends Fragment {
public TabFragment() {
// Required empty public constructor
}
public static TabLayout tabLayout;
public static ViewPager viewPager;
public static int int_items = 2 ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View x=inflater.inflate(R.layout.content_main_menu,null);
tabLayout=(TabLayout) x.findViewById(R.id.tabs);
viewPager =(ViewPager) x.findViewById(R.id.viewpager);
viewPager.setAdapter(new MyAdapter(getChildFragmentManager()));
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
return x;
}
class MyAdapter extends FragmentPagerAdapter{
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new HomeFragment();
case 1:
return new Offers();
}
return null;
}
#Override
public int getCount() {
return int_items;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0 :
return "Home";
case 1:
return "Offers";
}
return null;
}
}
}
On Button click in HomeFragment(or fragment_1)
Fragment fragment = new NavigationDrawerFragment();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.containerView, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
I just find my Question's Answer..
Before i tried to call fragment to fragment class that's why my ContainerView Remains same in the NavigationDrawer Fragment. So Now this time, I am Creating a new Activity and in this Activity I inflate the navigation Drawer fragment, due to this my previous ContainerView is Replaced by the New Activity layout
I'm sorry I am not fluent in English.
I wanna change the starting fragment in tab layout.
There is a value from another activity that gave with intent.
so I just want to start tab layout's fragment with my selection.
This is main activity's onCreate()
public class MainActivity extends AppCompatActivity {
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
And this is the code of changing in fragment
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position)
{
case 0:
return new TotalFragment();
case 1:
return new StatsFragment();
case 2:
return new FeedbackFragment();
case 3:
return new ManageFragment();
}
return null;
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "TOTAL";
case 1:
return "STATS";
case 2:
return "FEEDBACK";
case 3:
return "MANAGE";
}
return null;
}
}
}
Jisu Lee, we can set the starting tab in tab layout with the help of setCurrentItem() to viewpager.
We can set it by two types :
Example :
1.Simple setting tab:
Explation : Set the currently selected page. If the ViewPager has already been through its first layout with its current adapter there will be a smooth animated transition between the current item and the specified item.
viewPager.setCurrentItem(1);
2.For enableing smoothScroll :
Secand parameter "True" to smoothly scroll to the new item, "false" to transition immediately
viewPager.setCurrentItem(1, true);
Or for getting the CurrentItem we have : viewPager.getCurrentItem();
It will return the starting tab index (int).
mViewPager.setCurrentItem({index you want}, true);
My application has 3 tabs, each represented by Fragments. There is a custom FragmentPagerAdapter which also implements ActionBar.TabListener. Initially my FragmentPagerAdapter was returning new instances of fragments in public Fragment getItem(int index) method. Also, my TabListener implementation was instantiating Fragments and showing/hiding them in the onTabSelected/onTabUnselected. Unfortunately this approach doesn't fit me well, because in this case each Fragment is actually created twice. What I want is to create it once and then just hide/show, when necessary.
For that I have instantiated my fragment earlier in the activity and passed them to the PagerAdapter:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tabs_layout);
List<Fragment> fragments = initTabFragments();
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final ActionBar actionBar = getSupportActionBar();
TabsPagerAdapter pagerAdapter = new TabsPagerAdapter(this, getSupportFragmentManager(), fragments, viewPager);
viewPager.setAdapter(pagerAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int tab_name : tabs) {
Tab tab = actionBar.newTab();
actionBar.addTab(tab.setText(tab_name).setTabListener(pagerAdapter));
}
...
}
private List<Fragment> initTabFragments() {
List<Fragment> fragments = new ArrayList<Fragment>(3);
fragments.add(SherlockFragment.instantiate(this, TabFragment1.class.getName()));
fragments.add(SherlockFragment.instantiate(this, TabFragment2.class.getName()));
fragments.add(SherlockFragment.instantiate(this, TabFragment3.class.getName()));
return fragments;
}
Then in the PagerAdapter I just return fragment:
#Override
public Fragment getItem(int index) {
return fragments.get(index);
}
To handle Tab selection there is the following code:
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
int position = tab.getPosition();
String tag = "android:switcher:" + viewPagerId + ":" + position;
Fragment fragment = context.getSupportFragmentManager().findFragmentByTag(tag);
switch (position) {
case 0:
if (fragment == null) {
ft.add(viewPagerId, fragments.get(0), tag);
} else {
// If it exists, simply attach it in order to show it
ft.show(fragments.get(0));
}
break;
}
case 1:
if (fragment == null) {
ft.add(viewPagerId, fragments.get(1), tag);
} else {
// If it exists, simply attach it in order to show it
ft.show(fragments.get(1));
}
break;
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
switch (tab.getPosition()) {
case 0:
ft.hide(fragments.get(0));
break;
case 1:
ft.hide(fragments.get(1));
break;
}
}
In this case each fragment is instantiated once. And the first tab is shown correctly, but when I switch between tabs second and third tabs are empty (just white background). Why?
I was trying to add fragment to a container android.R.id.content, but exception was thrown: Can't change container ID of fragment.... I found out that it is because when Fragment is returned in getItem method it is added to a viewPager container. That why I use viewPagerId when add fragment to a transaction.
Any help would be greatly appreciated.
Have a ExampleFragment class defined that extends Fragment. Something like shown below
public class ExampleFragment extends Fragment {
private View categoryView;
private ViewPager mViewPager;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
categoryView = inflater.inflate(R.layout.viewpager, container, false);
mViewPager = (ViewPager) categoryView.findViewById(R.id.pager);
return categoryView;
}
public ViewPager returnViewPager() {
return mViewPager;
}
}
main_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:context=".ExampleFragmentActivity" >
<fragment
android:id="#+id/main_fragment"
android:name="somepackage.ExampleFragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
And then add this in the onCreate() of ExampleFragmentActivity
ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.main_fragment);
mViewPager = fragment.returnViewPager();
fragmentManager = getSupportFragmentManager();