So I've got 5 (6?) fragments in a ViewPager, they all interact with each other using callbacks through the Activity to get information from one another. I was getting some problems because they needed to be started in a particular order, and some seemed to be starting before others were ready regardless of what I did, so I implemented a callback for all the fragments to initialize themselves only once all of them were ready:
#Override
public void notifyReady() {
boolean settingsReady = false;
boolean fileReady = false;
boolean recordTableReady = false;
boolean chartReady = false;
boolean pieChartReady = false;
if (settingsFragment != null)
settingsReady = settingsFragment.isReady();
if (fileFragment != null)
fileReady = fileFragment.isReady();
if (recordTableFragment != null)
recordTableReady = recordTableFragment.isReady();
if (chartFragment != null)
chartReady = chartFragment.isReady();
if (pieChartFragment != null)
pieChartReady = pieChartFragment.isReady();
boolean allReady = settingsReady & fileReady & recordTableReady & chartReady & pieChartReady;
if (allReady) {
settingsFragment.initialize();
fileFragment.initialize();
recordTableFragment.initialize();
chartFragment.initialize();
pieChartFragment.initialize();
}
I put the interface implementation in my FragmentPagerAdapter, since that's where I read I should be initializing fragments anyway:
#Override
public Fragment getItem(int pos) {
switch(pos) {
case 0:
return new SettingsFragment();
case 1:
return new FileFragment();
case 2:
return new OracleFragment(); // No implementation yet
case 3:
return new RecordTableFragment();
case 4:
return new ChartFragment();
case 5:
return new PieChartFragment();
default:
return null; // BAD
}
}
Alongside the initialization of the callback interface to the FragmentPagerAdapter:
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
switch (position) {
case 0:
settingsFragment = (SettingsFragment) createdFragment;
settingsFragment.setNotificationListener(this);
break;
case 1:
fileFragment = (FileFragment) createdFragment;
fileFragment.setNotificationListener(this);
break;
case 2:
oracleFragment = (OracleFragment) createdFragment;
//oracleFragment.setNotificationListener(this); (This fragment has no implementation yet)
break;
case 3:
recordTableFragment = (RecordTableFragment) createdFragment;
recordTableFragment.setNotificationListener(this);
break;
case 4:
chartFragment = (ChartFragment) createdFragment;
chartFragment.setNotificationListener(this);
break;
case 5:
pieChartFragment = (PieChartFragment) createdFragment;
pieChartFragment.setNotificationListener(this);
break;
}
return createdFragment;
}
And I had my fragments call:
public void setNotificationListener(FragmentNotifier fragmentNotifier) {
this.fragmentNotifier = fragmentNotifier;
}
for the FragmentPagerAdapter to set itself as the interface object, and
#Override
public void onStart() {
super.onStart();
ready = true;
fragmentNotifier.notifyReady();
}
For initialization. This works incredibly well at first, but fragmentNotifier becomes null after coming back to the activity after a while, and the fragments' onStart() is called when the activity starts up again, producing a NullPointerException. Why is it becoming null? Other fields like the Fragment's reference to the Activity and the interface provided by it aren't null at this point in the execution, I've checked with a debugger, but fragmentNotifier IS! Why?!
I'm not going to debug your code and find out where is it going wrong, but I'll give you a better solution.
I've been through this in the past and I've realized that the best way to find and store callbacks defined in your Activity is inside your Fragments. So instead of using setters, use this in your Fragments:
#Override
public void onAttach(Activity activity){
if (activity instanceof FragmentNotifier)
mFragmentNotifier = (FragmentNotifier) activity;
else throw new IllegalAccessException("Activity must implement the FragmentNotifier interface");
}
This will ensure that you'll have a reference to your Activity. And add this to avoid leaking the reference:
#Override
public void onDetach(){
mFragmentNotifier = null;
}
Related
I need to replace fragment in view pager in an activity on a nework call success in another fragment. View pager is inside my dashboard activity. Here is get item method
#Override
public Fragment getItem(int pos) {
Fragment fragment = null;
switch (pos) {
case 0:
fragment = new HomeFragment();
break;
case 1:
fragment = new SponsorshipOffersFragment();
break;
case 2:
if (mPreferenceManager.getConnectProfileStatus()) {
fragment = new CollaborationFragment();
} else {
fragment = new ConnectSettingFragment();
}
break;
case 3:
fragment = new ProtfolioFragment();
break;
case 4:
fragment = new SocialSummaryFragment();
break;
}
return fragment;
}
For case 2, first time getConnectProfileStatus will return false and ConnectSettingFragment will get call. After submitting profile from ConnectSettingFragment, on success I need to call CollaborationFragment.
Can someone help me to do ? I am calling below method from fragment
public void replaceFrament() {
mViewPager.setCurrentItem(2);
}
that is inside activity but nothing happening.
I have one activity. In that when I launch a single fragment, i should get different image for each launch of fragment.
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.container, PlaceholderFragment.newInstance(position + 1)).commit();
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
// mTitle = getString(R.string.title_section1);
break;
case 2:
// mTitle = getString(R.string.title_section2);
break;
case 3:
//`enter code here` mTitle = getString(R.string.title_section3);
break;
}
}
I see you use Android Studio but you have to be more specific with how you want to determine the image if you want to determine it based on integer sent to fragment try this
public static class CustomFragment extends Fragment{
.....
#Override
public void onCreateView()
{
position = getArguments().getInt("ARG_SECTION_NUMBER")
View view = getView(getActivity());
return view;
}
public View getView(context)
{
View view = context.getLayoutInflater().inflate(R.id.view_layout,null);
ImageView image= (ImageView)view.findViewById(R.id.image_view);
switch(position)
{
case(1):
image.setImageResource(your_image_res_for_1);
break;
.....
default:
break
}
}
}
or if you want to determine based on something else use an if statement or even switch depending on what it is you are using to determine.....hope i helped
I'm currently working with an application that has "swipey-tabs" and uses the following PagerAdapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new HomeFragment();
case 1:
return new ListFragment();
case 2:
return new ChartFragment();
}
return null;
}
#Override
public int getCount() {
return 3;
}
}
I'm using the following code to communicate with one of my fragments from the main activity:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1)
{
if (resultCode == Activity.RESULT_OK)
{
String className = data.getExtras().getString("class_name");
if (className.trim().length() > 0) {
HomeFragment homeFrag = (HomeFragment) getSupportFragmentManager().findFragmentById(R.id.home_fragment);
if(homeFrag != null) {
Log.d("MainActivity", "homeFrag is not null");
homeFrag.newClass(className);
}else{
Log.d("MainActivity", "homeFrag is null");
HomeFragment newFragment = new HomeFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.home_fragment, newFragment);
transaction.commit();
newFragment.newClass(className);
}
addSubjectToDataBase(className);
}
}
}
}
My problem is that when I try to communicate with the fragment from the main activity, I seem to get multiple fragments existing at the same time. When I change the orientation of the screen or restart the activity, I get the modified version of the fragment, but when I swipe back and forth to view the fragments, I get the old, non-modified version of the fragment. The fragment manager doesn't seem to be registering the fragments created by the adapter because though I know the fragments have been created, the fragment manager always returns a null home fragment.
How should I go about avoiding this problem, and what is the best way to communicate with the fragment from the main activity?
After reading through this post I worked out a solution to my own question. The problem was that since FragmentPagerAdapter manages its own fragments, the fragment that I was trying to access wasn't the same as the one being used by the view pager.
I worked out a way of accessing the tags of the fragments that are used by the view pager by modifying my modifying my PagerAdapter as such:
public class TabsPagerAdapter extends FragmentPagerAdapter {
protected String homeTag;
protected String listTag;
protected String chartTag;
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
Log.d("Adapter", "returning new homeFragment");
return new HomeFragment();
case 1:
return new ListFragment();
case 2:
return new ChartFragment();
}
return null;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
switch (position){
case 0:
homeTag = createdFragment.getTag();
break;
case 1:
listTag = createdFragment.getTag();
break;
case 2:
chartTag = createdFragment.getTag();
break;
}
return createdFragment;
}
public String getHomeTag(){
return homeTag;
}
public String getListTag(){
return listTag;
}
public String getChartTag(){
return chartTag;
}
#Override
public int getCount() {
return 3;
}
}
Then, it was a simple matter to modify my code to access the correct fragments after I had access the proper fragment tags:
HomeFragment homeFrag = (HomeFragment) getSupportFragmentManager().findFragmentByTag(mAdapter.getHomeTag());
if(homeFrag != null) {
Log.d("MainActivity", "homeFrag is not null");
homeFrag.newClass(className);
}else{
Log.d("MainActivity", "homeFrag is null");
HomeFragment newFragment = new HomeFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.pager, newFragment, madapter.get);
transaction.commit();
newFragment.newClass(className);
It's not elegant but I would do something like this (Maybe someone will post a better solution):
In a singleton, I'll put an array of fragment:
public static Fragment[] sShownFragments = new Fragment[3];
Then in the getItem() method:
if(SingletonClass.sShownFragments[index]!=null){
return mFragments[index];
}
switch (index) {
case 0:
SingletonClass.sShownFragments[index] = new HomeFragment();
break;
case 1:
SingletonClass.sShownFragments[index] = new ListFragment();
case 2:
SingletonClass.sShownFragments[index] = new ChartFragment();
default:
SingletonClass.sShownFragments[index] = new Fragment();
}
return SingletonClass.sShownFragments[index];
Then in the getCount():
return SingletonClass.sShownFragments.length();
Now in your onActivityResult():
\\some code ...
((HomeFragment)SingletonClass.sShownFragments[0]).newClass(className);
\\ some other code...
When I open the application for the first time my tab is working fine. When I come back and go to the fragment page some times it returns null. Please help me ! What am I doing wrong? Here is my code.
public class TabsFragmentPagerAdapter extends FragmentPagerAdapter {
private ArrayList<ShareWinLeaderBoardTabModel> mShareWinLeaderBoardTabModelList;
public TabsFragmentPagerAdapter(FragmentManager fragmentManager, ArrayList<ShareWinLeaderBoardTabModel> shareWinLeaderBoardTabModelList) {
super(fragmentManager);
mShareWinLeaderBoardTabModelList = shareWinLeaderBoardTabModelList;
}
#Override
public Fragment getItem(int position) {
/*ShareWinLeaderBoardTabModel model = Once that is donemShareWinLeaderBoardTabModelList.get(position);
return new ShareWinLeaderBoardListFragment(model);*/
switch (position) {
case 0:
ShareWinLeaderBoardTabModel model = mShareWinLeaderBoardTabModelList.get(position);
return new ShareWinLeaderBoardListFragment(model);
case 1:
ShareWinLeaderBoardTabModel model1 = mShareWinLeaderBoardTabModelList.get(position);
return new ShareWinLeaderBoardListFragment(model1);
case 2:
ShareWinLeaderBoardTabModel model2 = mShareWinLeaderBoardTabModelList.get(position);
return new ShareWinLeaderBoardListFragment(model2);
case 3:
ShareWinLeaderBoardTabModel model3 = mShareWinLeaderBoardTabModelList.get(position);
return new ShareWinLeaderBoardListFragment(model3);
default:
return null;
}
}
#Override
public int getCount() {
return mShareWinLeaderBoardTabModelList.size();
}
}
When I press back there is some problem with the tab host. Tabs are coming fine but they are showing blank fragments. Any Help?
Image:
Change FragmentPagerAdapter to FragmentStatePagerAdapter
I'm using the native Android drawer for my menu, here in MainActivity :
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
//Home
fragment = new EntryFragment();
break;
case 1:
//Journal
fragment = new JournalFragment();
break;
case 2:
// Medications
fragment = new RxFragment();
break;
case 3:
// Reminders
fragment = new ReminderFragment();
break;
case 4:
// Counselor Locator
fragment = new LocatorFragment();
break;
case 5:
// Library
fragment = new LibraryFragment();
break;
case 6:
// Settings
break;
case 7:
// About
break;
default:
break;
}
if (fragment != null) {
fm.beginTransaction()
.replace(R.id.container, fragment).addToBackStack(String.valueOf(position)).commit();
String fragmentTitles[] = getResources().getStringArray(R.array.menu_array);
if(mActionBar != null) {
mActionBar.setTitle(fragmentTitles[position]);
}
} else {
Log.e("MainActivity", "Error in creating fragment");
}
But I'm repurposing EntryFragment for another activity (EntryDetailActivity), so in EntryFragment I defined a couple of methods to modify the view based on which activity is hosting it:
// Runs on the home page, shows/hides appropriate views
public void prepareForHomeView() {
mTxtDate.setVisibility(View.GONE);
mBtnDiffDay.setVisibility(View.GONE);
mEdtNotes.setVisibility(View.GONE);
mBtnSave.setVisibility(View.GONE);
mBtnDelete.setVisibility(View.GONE);
}
// Runs on the detail page, shows/hides appropriate views
public void prepareForDetailView() {
mBtnLog.setVisibility(View.GONE);
}
In EntryDetailActivity, I can easily call prepareForDetailView() in onResumeFragments(), but I'm having an issue of where to call prepareForHome() in MainActivity, since I am swapping fragments in and out based on the menu. Any suggestions?
/** Update **/
I assigned the fragments a tag that is the position of the menu through addToBackstack(). I also added the following method to my MainActivity:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
FragmentManager fm = getSupportFragmentManager();
EntryFragment entryFrag = (EntryFragment)fm.findFragmentByTag("0");
if(entryFrag != null) {
Log.v("rx", "Home frag is not null");
} else {
Log.v("rx", "Home frag is null");
}
}
Activity (or FragmentActivity) has onAttachFragment(Fragment fragment) - you could do an instanceof on fragment to determine if it is the correct type of fragment (or use the id, tag, etc) and call the appropriate method.
Alternatively, you could have the Fragment check the getActivity() (in onCreateView / onViewCreated).