I have a List of fragments which i can open in my navbar with:
getSupportFragmentManager().beginTransaction().replace(android.R.id.tabcontent, l_Fragment).commit();
The fragments are all from the same type -> CDMBasicMovieFragment extends Fragment
Inside this fragment i have a viewpager which i call with getFragmentManager(). And here i have the problem. The first fragment is fine but after replacing the first fragment, the viewpager is empty. Then i changed getFragmentManager() to getChildFragmentManager(). Now all fragments are fine. But only 1 time. After replacing back to the first fragment i have the same problem. What am i doing wrong?
Edit1:
My view hierarchy:
Activity
CDMBasicMovieFragment 1
ViewPager
CDMMovieListFragment
CDMBasicMovieFragment 2
ViewPager
CDMMovieListFragment
CDMBasicMovieFragment 3
ViewPager
CDMMovieListFragment
Edit2:
I tried to create unique ids for the fragments and used them on replace but i already have the same problem:
getSupportFragmentManager().beginTransaction().replace(android.R.id.tabcontent, l_Fragment, uniqueFragmentTag).commit();
Edit3:
I found a possible solution:
OnCreateView called multiple times / Working with ActionBar and Fragments
I tried everything from this link. I already have the same problem... Can anyone help?
UPDATE:
After a intensive debug session ;) i found this:
When loading the first CDMMovieListFragment FragmentPagerAdapter's instantiateItem is called and checks if there's already a fragment. It's not thats why it internally calls
mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
When i replace the CDMBasicMovieFragment which includes the fragmentpageradapter (inside is the added CDMMovieListFragment) and try to load the second CDMBasicMovieFragment then it's still the same fragmentmanager.
FragmentPagerAdapter's instantiateItem is called and found the old CDMMovieListFragment. Now it calls mCurTransaction.attach(fragment); instead. I think here's the problem. But im so confused...
I resolved my problem.
Now i always replace the CDMBasicMovieFragment like this:
getSupportFragmentManager().beginTransaction().replace(android.R.id.tabcontent, CDMBasicMovieFragment.newInstance()).commit();
On calling the FragmentPagerAdapter's constructor i changed getFragmentManager to getChildFragmentManager.
I thought i can create all fragment one time and just replace them if i click on the item of the navigationbar (drawner). But the replace will remove the fragment which was created one time and when i want to replace the removed fragment again, it's not there. That's why i got an empty viewpager.
Try below code snippet, In your custom pager adapter:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Then in your Activity containing the ViewPager:
final MyPagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
... anything you may need to do to handle pager state ...
adapter.notifyDataSetChanged(); //this line will force all pages to be loaded fresh when changing between fragments
}
}
Related
I was coding this program the other day, hoping that the fragment was doing okay, but it isn't. My project is a Relative Layout app where the GPS map will show up on the screen first thing the activity pops up, and then the choices to view some of the Javanese culture of Indonesia (in the form of Grid - Card View combination), will be available for selection.
Here, in this line, I have a class fragment inflation error, according to Logcat :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java_island); // Erroring line
KerisCard = (CardView) findViewById(R.id.KerisCard);
TradClothesCard = (CardView) findViewById(R.id.TradClothesCard);
TradHouseCard = (CardView) findViewById(R.id.TradHouseCard);
TariMerakCard = (CardView) findViewById(R.id.TariMerakCard);
SupportMapFragment javaMapFragment = (SupportMapFragment) getSupportFragmentManager().
findFragmentById(R.id.java_island_map);
javaMapFragment.getMapAsync(this); . . . }
In retrospect, I'm not very familiar to the concept of fragments as the classes I've been attending aren't going too detailed on the usage of fragments. Also, I have to note without the declaring the object references ((CardView) declarations on the onCreate() function), this error would not show up, and the app will be executable as normal.
Instead of making support fragment reference create fragment reference directly and call method from fragment.
SampleFragment fragment=new SampleFragment()
Fragment .someMethod
Here is a confusion
Do you want to open fragment in activity ?
Because you are trying to find fragment from id ,so have you created in xml if not then there is problem.
I have made an activity A which has a fragment X in it. In fragment X, EditText item has on click event which opens fragment Y. This fragment displays a list of names. I press a name in the list, fragment Y closes and sends the selected name of to fragment X EditText. Here's the code I wrote:
YFragment y = new YFragment();
y.setTargetFragment(x.class, code);
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame, y)
.addToBackStack(null)
.commit();
In fragment Y I have the code to send the data but the problem is in this block of code above. If I comment out the setTargetFragment line the code will work but no use as data will not be sent. If I run the app this error occurs:
java.lang.IllegalStateException: Fragment y{46d3d31 #3 id=0x7f090069}
declared target fragment x{e2c16 #0 id=0x7f090104
android:switcher:2131296516:0} that does not belong to this
FragmentManager!
To use setTargetFragment(), both the new Fragment and the target Fragment must be hosted within the same FragmentManager. The most common case where this would not happen is if you are using Activity.getSupportFragmentManager() or Fragment.getFragmentManager() alongside Fragment.getChildFragmentManager().
In my solution, replace
getChildFragmentManager()
with
Activity.getSupportFragmentManager() or Fragment.getFragmentManager()
that worked for me. Thank Mr.Ben P
To new users where the "getFragmentManager" only work for it but don't want use deprecated method, the method "getParentFragmentManager" work too.
I'm building an app with multiple pages to it, each of the main pages is displayed in a fragment in the main activity. Whenever the user wants to change page, the fragment within the activity is changed.
I want to write the code for each fragment within its own java file, as opposed to writing the code for all of the fragments within the main activity.
I've tried putting each bit of code within the fragment's onCreate and onCreateView methods (including trying onStart, onPause and whole other load of ones) but I've noticed that when the fragment is created the code just isn't running.
I've seen other questions on here with similar issues but none of the answers offered me an actual solution.
My question is this, how can I write the code for each fragment within that fragment's java file and actually get it to run? I understand this would be much easier if my fragments were created statically in the activity but these are not, they are created at runtime.
For reference, each fragment is displayed in the activity like so:
fragmentManager.beginTransaction().replace(R.id.container, HomeFragment.newInstance(position)).commit();
Try this,
From main activity you can call a static method defined in your fragment class as:
AuthFragment.NewInstance(LoginActivity.this, constantsObj);
This only line will go in your fragment calling activity.
Now in your fragment, inside the static method, find and create the fragment as,
public static void NewInstance(LoginActivity activityContext, Constants constantsObj) {
clsConstantsObj = constantsObj;
urlToCatch = clsConstantsObj.toString();
urlToCatch = urlToCatch.replace("/params", "");
FragmentManager fragmentManager;
FragmentTransaction fragmentTransaction;
activityContextInFragment = activityContext;
fragmentManager = activityContext.getSupportFragmentManager();
AuthFragment findAuthFragment = (AuthFragment) fragmentManager
.findFragmentByTag("authFragment");
if (findAuthFragment == null) {
AuthFragment authFragment = new AuthFragment();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.mainviewRegionwise, authFragment, "authFragment");
fragmentTransaction.commit();
}
}
Notice the "R.id.mainviewRegionwise", which is the id of your main container which could be a FrameLayout or any other container.
In the main activity of my app there is a container that hosts fragments.
When a user clicks a button in the "default" fragment (the first fragment that is displayed), the fragment changes, and so do the actionbar buttons.
One of the buttons in the actionbar of this new fragment open another activity.
In that activity, when a user clicks the back button, the activity closes, and the fragment that was shown in the MainActivity (the fragment that opened the new activity) is still there (which is fine).
However, if a user clicks the back button again, it does not return to the previous fragment. While it does return when the activity does not open.
It turns out that opening the activity clears the backstack (verified by Logging the count from the FragmentManager class), while I'm not quite sure whether this is supposed to behave like this or not, it kinda makes sense. Unfortunately, it is not the behavior I desire.
MainActivity: Fragment A (default) ---> Fragment B ---> Acivity B
Therefore, my question is how can I keep the backstack after the activity resumes, if at all?
I tried searching for similar questions, but all questions I found actually asked how to clear the backstack.
Try that:
#Override
public void onBackPressed() {
Intent intent = new Intent(A_Acticity.this, B_Activity.class);
startActivity(intent);
}
Hope it helped! :)
Reading the documentation, there is a way to pop the back stack based on either the transaction name or the id provided by commit. Using the name may be easier since it shouldn't require keeping track of a number that may change and reinforces the "unique back stack entry" logic.
Since you want only one back stack entry per Fragment, make the back state name the Fragment's class name (via getClass().getName()). Then when replacing a Fragment, use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack. If not, actually execute the Fragment replacement logic.
private void replaceFragment (Fragment fragment){
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
}
I've decided to add more tablet-friendly UI to my app by creating a dual-pane layout using the new fragments API. But the problem is that lots of screens in my app are Activity subclasses. Manually converting them all to fragments is not an option because:
- There are nearly 50 activities.
- I'd like my app to be compatible with all versions of Android starting at 1.6
- And I'd like it to be as small as possible so using a compatibility library is not an option as it is too huge
Although I've found some questions whose answers are saying that it is impossible, I've done it almost successfully. Here is code of my custom Fragment:
public static class ActivityFragment extends Fragment{
Intent intent;
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(intent==null){
Bundle a=getArguments();
intent=a.getParcelable("intent");
}
LocalActivityManager am=((ActivityGroup)getActivity()).getLocalActivityManager();
Window wnd=am.startActivity("intent"+intent.hashCode(), intent);
if(view==null){
view=wnd.getDecorView();
view.setLayoutParams(new FrameLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
}
return view;
}
public void onDestroy(){
super.onDestroy();
if(!((TabletMainActivity)getActivity()).tabs.containsValue(this)){
((ActivityGroup)getActivity()).getLocalActivityManager().destroyActivity("intent"+intent.hashCode(), true);
}
}
}
In order to work it must be used only in ActivityGroup.
Only problem is that in some activities with a ListView method onItemClick() does not get called after the activity is resumed, i.e. I click an item, another activity starts on top of current, but when I go back, items are no longer clickable.
I've finally found a solution by comparing all ListView's fields' values before and after onResume. And solution to this problem is to call the notifyDataSetInvalidated() method on the list adapter.