I find myself switching between a lot of Fragments in my card game app.
When my user creates a deck he goes through the following fragments:
Deck List Fragment (Click on 'New Deck') -> Class Selection Fragment (Mage, Warrior etc.) -> Name Selection Fragment -> Back to Deck List Fragment with our new deck listed.
I do this in order to have a smooth deck creation process but here are my two questions:
1) Is it recommended to use more Fragments than required if it makes the UX better and smoother?
2) Note that I do the following in order to switch Fragment:
Fragment fragment = new MyFragment();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Set argument(s) - Not all fragments set arguments
Bundle args = new Bundle();
args.putString("deckName", deckName);
fragment.setArguments(args);
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Instead of having similar methods from one Fragment to the other, I wish to create a generic method that takes a variable amount of parameters and the type of Fragment to create and switches to said Fragment.
But I can't seem to figure out how to do it.
Please excuse this lengthy post, and thank you.
Check out my code :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
replaceFragment(new HomeFragment());
}
public void replaceFragment(final Fragment fragment) {
getFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, fragment.getClass().getSimpleName())
.commit();
}
public void addFragment(final Fragment fragment) {
final Fragment hideFragment = getFragmentManager().findFragmentById(R.id.container);
getFragmentManager()
.beginTransaction()
.add(R.id.container, fragment, fragment.getClass().getSimpleName())
.hide(hideFragment)
.addToBackStack(hideFragment.getClass().getSimpleName())
.commit();
}
}
Call above methods in Fragment :
final HomeFragment homeFragment = new HomeFragment();
final Bundle bundle = new Bundle();
bundle.putString("KEY","VALUE");
homeFragment.setArguments(bundle);
((MainActivity) getActivity()).addFragment(homeFragment);
Related
I'm struggling with the changing of a fragment from another fragment. In my main_activity i have a layout-file with a bottomnavigationview and a framelayout for the content on the page. The changing from fragment to fragment on the navigationview workes fine, but if i access a new "subfragment" from a fragment, the new fragment is transparent.
Any suggestions? code below from my fragment and my mainactivity.
MainActivity parentActivity = (MainActivity) getActivity();
parentActivity.switchContent(new VisArrangementInfo(), data, true);
switchContent inside MainActivity:
public void switchContent(final Fragment fragment, Bundle data, final Boolean addToBackStackOption){
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.addToBackStack(null)
.commit();
}
The id content_frame is the id for the layoutfile for my MainActivity. the bottomnavigationview and the framelayout is in this file. the ID for the framelayout is "main_frame".
After my understanding i would want to change the "main_frame" and not the "content_fram" since it's here the main-content is supposed to be. When i do that i can't find the layout for the fragment i try to open at all. Is it because i'm trying to replace the framelayout instead of the whole layout?
In your Fragment A define an interface.
public interface FragmentEventListener {
void onFragmentChangeEvent(Fragment newFragment);
}
private FragmentEventListener listener;
public void setEventListener(FragmentEventListener listener) {
this.listener = listener;
}
In your fragment, when you want to change the fragment, call the method.
if (listener != null) {
listener.onFragmentChangeEvent(FragmentB);
}
In your main activity, implement FragmentEventListener, so you can switch to Fragment B from MainActivity.
public class MainActivity implements FragmentA.FragmentEventListener {
...
...
fragmentB.setEventListener(this); // here identify the listener.
....
#Override
public void onFragmentChangeEvent(Fragment newFragment) {
switchContent // Switch to Fragment B here
}
In the first time use add instead of replace:
public void switchContent(final Fragment fragment, Bundle data, final Boolean addToBackStackOption){
getSupportFragmentManager()
.beginTransaction()
.add(R.id.content_frame, fragment)
.addToBackStack(null)
.commit();
}
Solve by using getActivity()
I have this MainActivity.java and RepeatEntry.java
inside my MainActivity i have this code to have RepeatEntry ui
//i did hide two linear layout here with buttons and edittext inside it ,using the following method
hideTwoLinearLayout();
showCategoryContainerLayout();
Fragment fragment = new RepeatEntry();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//category_cont is a linear layout container for my fragment
ft.replace(R.id.category_cont, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
inside my RepeatEntry.java sample code
Button k = (Button) v.findViewById(R.id.button);
k.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Intent intent = new Intent(getActivity(),MainActivity.class);
// startActivity(intent);
// if i use popBackStack and also remove the code for intent , i cannot show what i hide
//note i have a method inside mainactivity to showTwoLinearLayout()
getFragmentManager().popBackStack();
}
});
Now my question is, do i have other option other than using intent to go back to MainActivity view
Note:Edited
You can add the transaction to backstack and then reverse with poping the backstack the code is here
Fragment fragment = new RepeatEntry();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//category_cont is a linear layout container for my fragment
ft.replace(R.id.category_cont, fragment).addToBackStack("tag");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
And for going back to the activity view call this to pop backstack
FragmentManager fm = getFragmentManager();
fm.popBackStack();
Also you can use the tag to poping back the specific transaction with
fm.popBackStack("tag");
There is a method called onAttach in your fragment. You can write an interface and assign your fragment's activity like:
private MyFragmentListener mListener;
#Override public void onAttach(Activity activity) {
super.onAttach(activity);
mListener = ((MainActivity) activity);
}
public interface MyFragmentListener{
void onClicked(int value);
}
//Call your listener when button clicked or other events
... mListener.onClicked(position);
Or another solution is to use Otto or Eventbus to get rid unnecessary code
You can also go back in Fragment like
getActivity().getSupportFragmentManager().popBackStack();
any way i solve my problem just in case if anyone might see this.I came up with two solution which i think the first one is best for me.Thanks to Adnan Basar who give me idea.
First solution is so simple by just adding this code inside my onClick event
((MainActivity) getActivity()).showTwoLinearLayout();
((MainActivity) getActivity()).hideCategoryContainerLayout();
Second Solution I created Activity after extends Fragment{
Activity mainActivity;
And use override methods
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mainActivity= activity;
}
#Override
public void onDestroy() {
super.onDestroy();
// this is where i call the method for showing the twolinearlayout again
((MainActivity) mainActivity).showTwoLinearLayout();
((MainActivity) mainActivity).hideCategoryContainerLayout();
}
I have a FragmentActivity in which I am implementing navigation drawer, like if I select any item from drawer list then its fragment is opened in activity. Now my XML layout code to display fragments looks like this
<!-- Framelayout to display Fragments -->
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
This means I have not any fragment tags in xml file but I open fragments dynamically from activity as below
private void displayView(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
if(session.isLoggedIn())
{
fragment = new SlidingFragment();
}
else
{
fragment = new LoginFragment();
}
break;
case 1:
fragment = new HomeFragment();
break;
case 2:
fragment = new LoginFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, fragment).commit();}
Now I want to use putFragment to keep fragment state alive for one fragment when orientation changed.so, I coded below on my FragmentActivity's onSaveInstanceState method
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Log.v(TAG, "In frag's on save instance state ");
FragmentManager manager = getSupportFragmentManager();
fr = new RandomFragment();
manager.putFragment(outState, "randomFragment", fr);
}
here fr is an instance of fragment declared globally Fragment fr and RandomFragment is the fragment that I call in onPostExecute method of homeFragment AsyncTask class. I don't know initiation of RandomFragment is right or not that is fr = new RandomFragment();
Because I don't know how to find fragment by id or tag because there is no fragment tag in my activity layout file. I have just a fragment classes that extends Fragment and i call them like above. I am very confused here. I get the error
02-28 16:28:37.809: E/AndroidRuntime(4336): java.lang.IllegalStateException: Fragment RandomFragment{4247d248} is not currently in the FragmentManager
When I try to change the orientation to landscape. I got this error in putFragment Line. And in onRestoreInstanceState method I write code for getFragment
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction t = manager.beginTransaction();
if(savedInstanceState != null)
{
fr = (FragmentRandom)manager.getFragment(savedInstanceState, "randomFragment");
}
else
{
fr = new FragmentRandom();
}
t.add(fr, "randomFragment");
t.commit();
}
But when putFragment on onSaveInstanceState is execute that time I get error and my application stops forcefully so onRestoreInstanceState is not executing.
My actual problem is I don't know I initiate fragment in right way or not. And if it is right then why I get the error? Should I have to do anything with onSaveInstanceState method of fragment also.
You seem to be trying to put a completely new instance of your fragment into the fragment manager. Not only will this cause the issue that you are seeing, if it had worked you wouldn't have been restoring the instance state of that fragment. Try finding the current fragment using the fragment manager.
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
FragmentManager manager = getSupportFragmentManager();
Fragment fr = manager.findFragmentById(R.id.frame_container);
manager.putFragment(outState, "randomFragment", fr);
}
I am creating tabs in my app using info from
epidemian answer
I created main class which handles tabs, this class extends TabActivity and creates tabs:
Resources res = getResources();
TabHost tabHost = getTabHost();
TabHost.TabSpec spec;
Intent intent;
intent = new Intent().setClass(this, stackA.class);
spec = tabHost.newTabSpec("tab1").setIndicator("Tab 1",
res.getDrawable(android.R.drawable.ic_menu_search))
.setContent(intent);
tabHost.addTab(spec);
intent = new Intent().setClass(this, stackB.class);
spec = tabHost.newTabSpec("tab2").setIndicator("Tab 2",
res.getDrawable(android.R.drawable.ic_menu_search))
.setContent(intent);
tabHost.addTab(spec);
tabHost.setCurrentTab(0);
Then for every tab is created FragmentActivity and this gives possibility to create stack in every tab. My fragments is created:
protected void navigateTo(Fragment newFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, newFragment);
ft.addToBackStack(null);
ft.commit();
}
public class fragmentA extends Fragment{
private LinearLayout ll;
private FragmentActivity fa;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fa = super.getActivity();
ll = (LinearLayout) inflater.inflate(R.layout.fragmenta, container, false);
Button next = (Button) ll.findViewById(R.id.button1);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
((ActivityInTab) getActivity()).navigateTo(new fragmentB());
}
});
return ll;
}
}
This is working. What is wrong that when i am at tab 1 and fragment B (first fragment in stack is A), rotate my device and then it go back at fragment A.
epidemian said something about orientations and using setArguments/getArguments, but i am new to android programming so i don't really know how to do that:
Finally, if you need to survive orientation changes, it's important
that your fragments are created using setArguments/getArguments. If
you set instance variables in your fragments' constructors you'll be
screwed. But fortunately that's really easy to fix: just save
everything in setArguments in the constructor and then retrieve those
things with getArguments in onCreate to use them.
int x;
Bundle args = new Bundle();
args.putString("variable", x);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment fragment = Fragment.instantiate(getActivity(), MyFragment.class.getName(), args);
ft.replace(R.id.layout1, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
than in the fragment:
getArguments().getInt("variable");
You can use Fragment.setRetainInstance(true) for this. See this related question.
You can prevent the Activity from beeing destroyed due to orientation change. Add the following line to your manifest of your TabActivity (orientation change + keyboard visibility changes)
android:configChanges="keyboardHidden|orientation"
See here for description.
setArguments/getArguments does not help you here. As he stated you may use it to save instance variables and retrieving them after orientation change. But with the given line above you do not need that because your Activity/Fragments won't get destroyed
I have a fragment inside a group activity and I want to replace it with another fragment:
FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
SectionDescriptionFragment bdf = new SectionDescriptionFragment();
ft.replace(R.id.book_description_fragment, bdf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
It works fine when it is done as a seperate project without using activity group, every thing works fine in log cat as control goes inside getview(), but no view is visible, not even any exception arises, I want the book detail fragment to be replaced by section detail fragment.
Xml of book detail fragment has id book_description_fragment and xml for section description fragment has id section_description_fragment.
The above code is in onClick method of an item, I want that when user taps on an item in horizontal scroll view, then the fragment changes.
Fragments that are hard coded in XML, cannot be replaced. If you need to replace a fragment with another, you should have added them dynamically, first of all.
Note: R.id.fragment_container is a layout or container of your choice in the activity you are bringing the fragment to.
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Please see this Question
You can only replace a "dynamically added fragment".
So, if you want to add a dynamic fragment, see this example.
I've made a gist with THE perfect method to manage fragment replacement and lifecycle.
It only replace the current fragment by a new one, if it's not the same and if it's not in backstack (in this case it will pop it).
It contain several option as if you want the fragment to be saved in backstack.
=> See Gist here
Using this and a single Activity, you may want to add this to your activity:
#Override
public void onBackPressed() {
int fragments = getSupportFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
return;
}
super.onBackPressed();
}
Use the below code in android.support.v4
FragmentTransaction ft1 = getFragmentManager().beginTransaction();
WebViewFragment w1 = new WebViewFragment();
w1.init(linkData.getLink());
ft1.addToBackStack(linkData.getName());
ft1.replace(R.id.listFragment, w1);
ft1.commit();
Use ViewPager. It's work for me.
final ViewPager viewPager = (ViewPager) getActivity().findViewById(R.id.vp_pager);
button = (Button)result.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewPager.setCurrentItem(1);
}
});
hope you are doing well.when I started work with Android Fragments then I was also having the same problem then I read about
1- How to switch fragment with other.
2- How to add fragment if Fragment container does not have any fragment.
then after some R&D, I created a function which helps me in many Projects till now and I am still using this simple function.
public void switchFragment(BaseFragment baseFragment) {
try {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
if (getSupportFragmentManager().findFragmentById(R.id.home_frame) == null) {
ft.add(R.id.home_frame, baseFragment);
} else {
ft.replace(R.id.home_frame, baseFragment);
}
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
enjoy your code time :)
you can use simple code its work for transaction
Fragment newFragment = new MainCategoryFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame_NavButtom, newFragment);
ft.commit();
You Can Use This code
((AppCompatActivity) getActivity()).getSupportFragmentManager().beginTransaction().replace(R.id.YourFrameLayout, new YourFragment()).commit();
or You Can This Use Code
YourFragment fragments=(YourFragment) getSupportFragmentManager().findFragmentById(R.id.FrameLayout);
if (fragments==null) {
getSupportFragmentManager().beginTransaction().replace(R.id.FrameLayout, new Fragment_News()).commit();
}
I change fragment dynamically in single line code
It is work in any SDK version and androidx
I use navigation as BottomNavigationView
BottomNavigationView btn_nav;
FragmentFirst fragmentFirst;
FragmentSecond fragmentSecond;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
fragmentFirst = new FragmentFirst();
fragmentSecond = new FragmentSecond ();
changeFragment(fragmentFirst); // at first time load the fragmentFirst
btn_nav = findViewById(R.id.bottomNav);
btn_nav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch(menuItem.getItemId()){
case R.id.menu_first_frag:
changeFragment(fragmentFirst); // change fragmentFirst
break;
case R.id.menu_second_frag:
changeFragment(fragmentSecond); // change fragmentSecond
break;
default:
Toast.makeText(SearchActivity.this, "Click on wrong bottom SORRY!", Toast.LENGTH_SHORT).show();
}
return true;
}
});
}
public void changeFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout_changer, fragment).commit();
}
In kotlin you can do:
// instantiate the new fragment
val fragment: Fragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.book_description_fragment, fragment)
transaction.addToBackStack("transaction_name")
// Commit the transaction
transaction.commit()
This will work if you're trying to change the fragment from another fragment.
Objects.requireNonNull(getActivity()).getSupportFragmentManager()
.beginTransaction()
.replace(R.id.home_fragment_container,new NewFragment())
NOTE As stated in the above answers, You need to have dynamic fragments.
You can use fragment-ktx
// If you are in fragmet
childFragmentManager.beginTransaction()
// or if you are in activiry
supportFragmentManager.beginTransaction()
// Create and commit a new transaction
supportFragmentManager.commit {
setReorderingAllowed(true)
// Replace whatever is in the fragment_container view with this fragment
replace<ExampleFragment>(R.id.fragment_container)
}
To replace a fragment with another one do this, Note that R.id.fragment comes from the id that you give to the first tag of the fragment in the XML.
barAudioPlaying.setOnClickListener(view -> {
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment,new HomeFragment())
.commit();