I'm on the verge of losing it. Anyone that could help me solve this issue will be showered with coding props for all of eternity.
I have a Tablayout containing 3 Tabs within my Main Activity. Each tab contains their own Fragment(Tasks,Calendar,Contacts). There is a FAB button in the Main Activity that lives on top of all the Fragments and performs a certain action base on what Tab I'm currently in.
My main issue is that, when I'm in Tab1, and the Fab is clicked, I need to replace the Tasks Fragment with a different Fragment called AddContact.
I've been looking up how to solve this for hours and finally settled on an earlier answer from this stack post
The issue is, I'm not quite grasping how they were able to do it.
My understanding is that the swapping of Fragments should be managed by the getItem method within the View Pager Adapter. And that there should be a listener that exists between the ViewPagerAdapter and the buttonClicked action in my Main Activity that tells it to execute.
The problem is that I can't make this link. I've tried a number of things but with no luck. Below I included my MainActivity Class and my ViewPagerAdapter Class. If someone could please explain to me how I could swap out my Tasks Fragment with my AddContacts Fragment in the ViewPager, I would greatly appreciate it
Main Activity:
public class MainActivity extends AppCompatActivity {
private CollapsingToolbarLayout collapsingToolbarLayout;
private ViewPager viewPager;
private ArrayList<Fragment> fragments = new ArrayList<>();
private ArrayList<String> titles = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.collapsing_toolbar);
setSupportActionBar(toolbar);
fragments.addAll(Arrays.asList(new Tasks(),new Calendar(),new Contacts()));
titles.addAll(Arrays.asList(getResources().getString(R.string.fragment_task_title),
getResources().getString(R.string.fragment_cal_title),getResources().getString(R.string.fragment_contacts_title)));
TabLayout tabLayout = (TabLayout)findViewById(R.id.tabs);
final FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
viewPager = (ViewPager)findViewById(R.id.container);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),fragments,titles);
viewPager.setAdapter(viewPagerAdapter);
viewPager.setCurrentItem(1);
tabLayout.setupWithViewPager(viewPager);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fabAction();
}
});
}
public void fabAction(){
int current = viewPager.getCurrentItem();
switch (current){
case 0:{
//Need to indicate Fragment Swap here somehow. Through Listener?
break;
}
}
}}
ViewPagerAdapter Class:
public class ViewPagerAdapter extends FragmentPagerAdapter {
ArrayList<Fragment> fragments;
ArrayList<String> titles;
private Fragment mFragmentAtPos0;
public ViewPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments, ArrayList<String>titles) {
super(fm);
this.fragments = fragments;
this.titles = titles;
}
#Override
public Fragment getItem(int position) {
if (position == 0)
{
if (mFragmentAtPos0 == null)
{
//HANDLES THE CHANGING OF FRAGMENTS IN HERE USING THE LISTENER BELOW? BUT I'M NOT GRASPING THE LOGIC
//ON HOW TO IMPLEMENT THIS.
}
}
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
//GOT FROM LESSON - ALLOWS VIEWPAGER TO CHANGE IT'S VIEW AND ATTACH NEW FRAGMENT WHEN LAUNCHED
#Override
public int getItemPosition(Object object)
{
if (object instanceof Tasks && mFragmentAtPos0 instanceof AddContact)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
//GOT FROM LESSON. A LISTENER BUT I'M NOT SURE WHERE AND HOW TO IMPLEMENT IT. WHERE IN THE MAIN ACTIVITY SHOULD THIS
//GO? HOW DOES IT COMMUNICATE BACK TO THE ADAPTER?
public interface FirstPageFragmentListener
{
void onSwitchToNextFragment();
}
The post or lesson that Im referring is also from 5 years ago so I don't know if there's a simpler way to implement this, in which case I would love to hear it. Thanks so much.
Related
I have a tab layout with 2 fragment and I have a toolbar with different options that belong to the activity which hosts the view pager.
When I click on a toolbar option, it clean the data from DB that are used in the fragment and then it should refresh it to apply changes at screen.
But I can't find a way to refresh the fragment from the activity.
Here is the 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);
}
}
Here is the way I call it in the fragments in main :
FragAdapter = new FragmentAdapter(getSupportFragmentManager());
((FragmentAdapter) FragAdapter).AddFragment(new FragmentProgramme(),"Programmes");
((FragmentAdapter) FragAdapter).AddFragment(new FragmentPlanning(),"Agenda"); //FRAGMENT I WANT TO REFRESH
mviewpager.setAdapter(FragAdapter);
mtablayout = (TabLayout) findViewById(R.id.main_tabs);
mtablayout.setupWithViewPager(mviewpager);
In the main, I have the option on the toolbar and I would like to do something like this :
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int pos = item.getItemId();
switch (pos)
{
DatabaseAgenda.getInstance(this).dropDB(); //CLEAN DB
// I would like to find a function like this : ((FragmentAdapter) FragAdapter).refresh(FragmentPlanning, "Agenda");
break;
}
return super.onOptionsItemSelected(item);
}
The point is to simply destroy the fragment because all the data must be cleaned from screen and then recreate it so the users can add new data to it
I hope this is clear, thank you for your hlelp
I have found a lot answers but never what I was really looking for
You can override the method setUserVisibleHint inside fragments to detect when it is visible. Data can be refreshed inside this method. Selecting the items of pageradapter programatically triggers setUserVisibleHint of present fragment. You would not need destroying fragments each time.
in my TabLayout i create my login TabItem dynamically at the onCreate() method of my MainActivity , but every time i exit from my application with back button and i re-enter my app , it creates that login TabItem again and so it doubles it every time , i tried to check and use :
if(savedInstanceState == null) {
// create the first tab
}
but it didn't work , can you please help me ?
here is my code for the MainActivity :
public class MainActivity extends AppCompatActivity {
TabLayout tabLayout;
ViewPager viewPager;
public static List<Fragment> fragments = new ArrayList<>();
public static List<String> fragmentsTitle = new ArrayList<>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabLayout = findViewById(R.id.myTabLayout);
viewPager = findViewById(R.id.myViewPager);
MyViewPagerAdapter Adapter = new MyViewPagerAdapter(getSupportFragmentManager());
Adapter.createTab(new LoginFragment(), "Login", getSupportFragmentManager(), viewPager);
viewPager.setAdapter(Adapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
public void onTabSelected(TabLayout.Tab tab) {
}
public void onTabUnselected(TabLayout.Tab tab) {
}
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
}
and this is the code for FragmentPagerAdapter
public class MyViewPagerAdapter extends FragmentPagerAdapter {
public MyViewPagerAdapter(FragmentManager manager) { super(manager);}
public void createTab(Fragment fragment, String title, FragmentManager manager, ViewPager viewPager) {
MyViewPagerAdapter Adapter = new MyViewPagerAdapter(manager);
MainActivity.fragments.add(fragment);
MainActivity.fragmentsTitle.add(title);
Adapter.notifyDataSetChanged();
viewPager.setAdapter(Adapter);
}
public Fragment getItem(int position) {
return MainActivity.fragments.get(position);
}
public CharSequence getPageTitle(int position) {
return MainActivity.fragmentsTitle.get(position);
}
public int getCount() {
return MainActivity.fragments.size();
}
}
Try removing static keyword from your list in MainActivity. Instead use final to prevent reinitialisation of list.
It's creating that tab everytime because static things stay alive as long as they get killed. And the professional way on earth to create tab and store them inside list is to keep that list inside viewpager adaptor.
I am happy it was helpful.
whenever i swipe to move to the next fragment the code inside another fragment gets executed tho the layout is loaded properly and the code also but it also executes the code of another fragment
ex: 3 fragments A,b,c
when i swipe from fragment A to fragment b : fragment b layout and code are executed but also fragment c code
when i swipe from b to c , only the code and layout of fragment c ,so its executed properly
so the problem is if it isnt the last fragment it calls the code of next one
here is my main_activty code
public class Main2Activity extends AppCompatActivity {
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
// 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);
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
home h1 = new home();
return h1;
case 1:
status st = new status();
return st;
case 2:
info info = new info();
return info;
case 3:
setting set = new setting();
return set;
}
return null;
}
#Override
public int getCount() {
return 4;
}
}
}
Your SectionsPagerAdapter is extending FragmentStatePagerAdapter, and its default behaviour is to preload at least one page for optimisation reasons. You can set the number of fragments it preloads using setOffscreenPageLimit but it has to be at least one.
You can use this method in your fragment and put in there something that you want executed only once the fragment becomes visible:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
}
}
Hi, to all!
I have a problem with the TabMenu, below i've posted a GIF, which shows how it works, it doesn't work correctly.
How it works in code:
MainActivity(ViewPager, TabsLayout)->FragmnetAdapter(For tabs layout)
How its must be: "Home"(HomeFragment), "History"(HistoryFragment), "Places"(PlacesFragment)
Now when its start at "Home" start "Home" but and call "History" on "Hstory" call "Places"
But in code i didnt see some problems can you help me, please:)
Here is the code:
MainActivity:
public class MainActivity extends AppCompatActivity {
#BindView(R.id.appToolbarMainTabs)
Toolbar appToolbar;
#BindView(R.id.tabLayoutTabs)
TabLayout tabs;
#BindView(R.id.viewPager)
ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(appToolbar);
setupViewPager(viewPager);
tabs.setupWithViewPager(viewPager);
}
// Add Fragments to Tabs
private void setupViewPager(ViewPager viewPager) {
FragmentTabsAdapter adapter = new FragmentTabsAdapter(getSupportFragmentManager());
adapter.addFragment(new HomeFragment(), getString(R.string.home_tab));
adapter.addFragment(new HistoryFragment(), getString(R.string.history_tab));
adapter.addFragment(new PlacesFragment(), getString(R.string.places_tab));
viewPager.setAdapter(adapter);
}}
FragmentAdapter:
public class FragmentTabsAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public FragmentTabsAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}}
Home&History&PlaceFragment don't needed i think so:)
Thanks a lot!:)
The tabs and the ViewPager are ok, it always preloads at least the previous and the next fragments.
You could only show the dialog when the fragment is shown and it is still loading the info.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (fragmentIsLoadingInfo()) { // implement this
showLoading(); // implement this
}
}
}
Also check the setOffscreenPageLimit of a ViewPager to understand what is happening and to update this value if you, for example, want all the three fragments to load at once. Its a shame that if you set it to 0 it still works as if it was 1.
I have a screen that uses viewpager which contains two fragments A and B and data in fragments are loaded using async task. My MainActivity class contains the following code in onCreate method:
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
pager = (ViewPager) findViewById(R.id.pager);
adapter = new MyPagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
tabLayout.setupWithViewPager(pager);
//Async task to get data from server and update the fragments
runGetDataTask(true);
/**
* Adapter class for View pager
*/
public class MyPagerAdapter extends FragmentPagerAdapter {
private final String[] TITLES = {"A",
"B"};
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return TITLES[position];
}
#Override
public int getCount() {
return TITLES.length;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
iFragment = A.getInstance();
if (Utility.IS_DEBUG)
Log.i(LOG_TAG,"A fragment created");
return iFragment;
case 1:
dFragment = B.getInstance();
if (Utility.IS_DEBUG)
Log.i(LOG_TAG,"B fragment created");
return dFragment;
}
return null;
}
}
The problem is that sometimes Async task completes its execution before fragments are initialized and when it try to access iFragment and dFragment to update data its has got from server I got nullpointerexception because fragments have not been initialized.
But this happens sometimes, I don't know what am I doing wrong here. Could someone please help me fix this issue?
Thanks
I think a possibile workaround is to make sure that the ViewPager is initialised after the AsyncTask completes the its job. This can be achieved by creating a listener like:
public interface DataListener{
public void onDataReceived();
}
and provide it to the AsyncTask (your activity must implement this interface). When the AsyncTask completes its work, then the listener is called and the control will be transferred back to the Activity that can initialise the UI.