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...
Related
I have an activity that holds my tabbed layout fragments. When I have 2 tabs everything works fine, but when I add a new tab I get
Caused by: java.lang.ClassCastException: rauhalamika.rcontrolble.HomeFragment cannot be cast to rauhalamika.rcontrolble.ManualFragment
Here is the SectionsPagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
PresetsFragment presets = new PresetsFragment();
return presets;
case 1:
ManualFragment manual = new ManualFragment();
return manual;
case 2:
HomeFragment home = new HomeFragment();
return home;
default:
return null;
}
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Presets";
case 1:
return "Manual";
case 2:
return "Home";
}
return null;
}
}
And the problem occurs when I'm calling ManualFragment's method from the activity like this:
((ManualFragment)getSupportFragmentManager().findFragmentById(R.id.container)).updatePressure(values);
This method updates a bunch of TextViews in the ManualFragment.
Everything works as it should if I only have PresetsFragment a ManualFragment, but when I add HomeFragment the app crashes.
What am I doing wrong?
When using FragmentPagerAdapter you can not get fragment by id.
getSupportFragmentManager().findFragmentById(R.id.container)
change this to
getSupportFragmentManager().findFragmentByTag("f1")
For tagging fragment Read This thread.
Originally I had a single fragment loaded in my MainActivityclass. I used the following in
onCreate
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(new TimeTableFragment(), TIMETABLEFRAGMENT_TAG)
.commit();
}
and in
onResume
if(*setting has changed*){
TimeTableFragment tf = (TimeTableFragment) getSupportFragmentManager()
.findFragmentByTag(TIMETABLEFRAGMENT_TAG);
if (null != tf) {
tf.onStudentIdChanged();
}
}
The onStudentIdChanged() method would refresh the fragment data as preference settings have changed.
I have now refactored this activity to use FragmentPagerAdapterand load two different fragments in tabs. Both fragments are instances of the same TimetableFragment class, but with different parameters passed when creating.
FragmentPagerAdapter methods
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a Fragment
switch (position) {
case 1:
case 2:
return TimeTableFragment.newInstance(position);
case 0:
//todo add the single day here
return TimeTableFragment.newInstance(position);
}
return null;
}
TimetableFragment Method
public static TimeTableFragment newInstance(int sectionNumber) {
TimeTableFragment fragment = new TimeTableFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
Now that this is working, in onResume of my MainActivityI am no longer able to access the Fragments using findFragmentByTag, as they are not added in this way during onCreateand I can no longer call the onStudentIdChanged method.
How, for all created instances of the Timetable Fragment can I call this method and have the data updated / refreshed?
In your FragmentPagerAdapter imprementation, add private SparseArray<Fragment> mFragments = new SparseArray<Fragment>();
Modify your getItem method as follows:
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a Fragment
Fragment fragment = null;
switch (position) {
case 1:
case 2:
fragment = mFragments.get(position);
if (fragment == null) {
fragment = TimeTableFragment.newInstance(position);
mFragments.append(position, fragment)
}
break;
case 0:
//todo add the single day here
fragment = mFragments.get(position);
if (fragment == null) {
fragment = TimeTableFragment.newInstance(position);
mFragments.append(position, fragment)
}
break;
}
return fragment;
}
Now when you can get instance of fragment by position.
So I have my navigation drawer with 5 different options. They all open a new fragment that I have created. The first one is Home, and I am trying to find a way to bring it back to the first screen that shows up under the navigation drawer. It has the id of "container", in the main_activity.xml. I do not want to use and intent to call the entire class again to load up. Also I do not want to be able to use the back button from another intent. I am confused on how to make this happen.
#Override
public void onNavigationDrawerItemSelected(int position) {
FragmentHowItWorks fragmentHow;
FragmentSettings fragmentSettings;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
switch(position){
case 0:
// should I call the layout?
// this is the "Home" option
break;
case 1:
fragmentHow = new FragmentHowItWorks();
transaction.replace(R.id.container, fragmentHow);
transaction.addToBackStack(null);
transaction.commit();
break;
case 2:
fragmentSettings = new FragmentSettings();
transaction.replace(R.id.container, fragmentSettings);
transaction.addToBackStack(null);
transaction.commit();
break
case 3:
fragment = new FragmentHowItWorks();
transaction.replace(R.id.container, fragment);
transaction.addToBackStack(null);
transaction.commit();
break;
case 4:
fragment = new FragmentHowItWorks();
transaction.replace(R.id.container, fragment);
transaction.addToBackStack(null);
transaction.commit();
break;
}
}
Use methods add,hide and show,
Step1 Prepare all your fragments
Fragment fragment1 = new FragmentOne();
Fragment fragment2 = new FragmentTwo();
Fragment fragment3 = new FragmentThree();
Fragment mCurrentFragment = null;
Step2 Show/hide your fragments
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment;
switch (position) {
case 1:
fragment = fragment1;
break;
case 2:
fragment = fragment2;
break;
case 3:
fragment = fragment3;
break;
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(mCurrentFragment == null) {
ft.add(R.id.container, fragment).commit();
mCurrentFragment = fragment;
} else if(fragment.isAdded()) {
ft.hide(mCurrentFragment).show(fragment).commit();
} else {
ft.hide(mCurrentFragment).add(R.id.container, fragment).commit();
}
mCurrentFragment = fragment;
}
You can do this ,
You can get the name of current fragment which is in the container . This will return name including the package + fragment name
String name = getFragmentManager().findFragmentById(container id ).getClass().getName();
When you click on the home index of drawer, check weather the current name id equal to the home.
If it is home, you don't need to perform any action.
I know that this was asked and answered long time ago but, I would like to show my approach on this problem, maybe it will help anyone else:
I added a Fragment Name over each Fragment that I use like:
public class MainFragment extends BaseFragment {
public static final String FRAGMENT_TAG = "main";
// ... all your fragment
}
And on the Drawer Layout:
public void methodThatSetsTheUi() {
mDrawerView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
getFragmentManager().findFragmentByTag("main");
String tag = getFragmentTag(position);
replaceFragment(getOrCreateFragment(tag), tag);
mDrawerLayout.closeDrawer(mDrawerView);
}
});
}
#NonNull
private String getFragmentTag(int position) {
String tag;
switch (position) {
case MAIN_FRAGMENT_DRAWER_POSITION:
tag = MainFragment.FRAGMENT_TAG;
break;
case FAVORITE_FRAGMENT_DRAWER_POSITION:
tag = FavoriteFragment.FRAGMENT_TAG;
break;
default:
throw new AssertionError("That selection is wrong");
}
return tag;
}
private BaseFragment getOrCreateFragment(String fragmentName) {
BaseFragment fragment = (BaseFragment) getFragmentManager().findFragmentByTag(fragmentName);
if(fragment == null) {
fragment = FragmentFactory.createFragment(fragmentName);
}
return fragment;
}
and well, the FragmentFactory is just a simple Factory:
public final class FragmentFactory {
public static BaseFragment createFragment(String fragmentName) {
switch(fragmentName) {
case MainFragment.FRAGMENT_TAG:
return MainFragment.newInstance();
case FavoriteFragment.FRAGMENT_TAG:
return FavoriteFragment.newInstance();
// ... all fragments here.
default:
return null;
}
}
Hope to help someone.
I have built an Android application that uses tabs to navigate. I am using fragments for the majority of the application, however I need to display one activity. How do i display an activity in case 2. Below is the code i have at the moment, i know it returns a fragment but how do i enable the use of both?
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
System.out.println("current position = " + position);
Fragment fragment = null;
switch(position){
case 0:
return new diaryFragment();
case 1:
return new newEntryFragment();
case 2:
return new Calendarnew();
}
return null;
}
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
fragment = new diaryFragment();
break;
case 1:
fragment = new newEntryFragment();
case 2:
fragment = new Calendarnew();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
Hi I dont actually get what you are up to but try reading this, maybe it could somehow solve your issue. https://stackoverflow.com/a/2958586/4470313
I have a section my class which is for handling what fragment to load for the FragmentPagerAdapter. Right now it loads fragments just fine but now I want to implement a FragmentActivity and I am having a problem figuring how to create it as I cant use "newinstance" since its type of Activity, and on top of it, I dont know how to call that FragmentActivity as its different from loading just a Fragment via "newinstance" method. My second tab is the one that I would like to be extended to FragmentActivity.
public class SectionsPagerAdapter extends FragmentPagerAdapter {
private Context _context;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
_context= getApplicationContext();
}
#Override
public Fragment getItem(int i) {
Fragment f = new Fragment();
switch(i){
case 0:
f=News.newInstance(_context);
break;
case 1:
f=Info.newInstance(_context);
break;
case 2:
f=Files.newInstance(_context);
break;
case 3:
f=Donate.newInstance(_context);
break;
}
/*
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, i + 1);
fragment.setArguments(args);
*/
return f;
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return getString(R.string.title_section1).toUpperCase();
case 1: return getString(R.string.title_section2).toUpperCase();
case 2: return getString(R.string.title_section3).toUpperCase();
case 3: return getString(R.string.title_section4).toUpperCase();
}
return null;
}
}
My guess is that I cant use getitem methods as that for only switching Fragments and that I would have to use something like:
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_content, new BasicFragment());
ft.commit();
Can someone help point me in the right direction?
Can someone help point me in the right direction?
This is not supported. It certainly is not possible with FragmentPagerAdapter, which only works with fragments.