Hi guys im working on my MainActivity and I am dealing with the following problem:
In my MainActivity I have 3 buttons(button1, button2, button3). With each I can add a Fragment(Fragment2, Fragment1, ProfileFragment) to my container. Everytime a button is pressed, it checks the Fragments if there is already anotherone visible.
If yes, FragmentManager().beginnTransaction().fragment.hide() should hide it.
Then it checks if the fragment bound to the button already exists.
If no, it adds it.
If yes, it should make the existing hidden fragment visible again with FragmentManager().beginnTransaction().fragment.show()
Now: If I press button2 as the first when I start my App everything works fine and I can infinitely switch between the fragments.
But: If I press button1 as the first, and then switch to button2 or button3, the fragment bound to button1 (m2fragment) can't be shown again. It just shows m1fragment (which should be hidden when I press button1)
The same happens if I press button3 as the first. Everytime I try to switch back to button3(profileFragment) it just shows m1fragment.
May be there a problem with the googleMap which I call from the xml from m2Fragment??
Anyone can see where I made (a) mistake(s) ? I would be really glad since I am dealing with this for several days now.
Thank you all !
Cut from my MainActivity:
public class MainActivity extends FragmentActivity implements ProfileFragment.OnFragmentInteractionListener,
OfferFragment.OnFragmentInteractionListener, MapsFragment.OnFragmentInteractionListener, OpenerFragment.OnFragmentInteractionListener, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
protected int fragment1Open = 0;
protected int fragment2Open = 0;
protected int profileFragmentOpen = 0;
Fragment1 m1Fragment = new Fragment1();
Fragment2 m2Fragment = new Fragment2();
ProfileFragment mProfileFragment = new ProfileFragment();
OpenerFragment mOpenerFragment = new OpenerFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null) {
getFragmentManager().beginTransaction().add(R.id.container,mOpenerFragment).commit();
}
final Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//check fragments
if(mOpenerFragment.isVisible()) {
getFragmentManager().beginTransaction().remove(mOpenerFragment).commit();
Log.d("button1", "remove OpenerFragment");
}
if(m1Fragment.isVisible()) {
getFragmentManager().beginTransaction().hide(m1Fragment).commit();
}
if(mProfileFragment.isVisible()) {
getFragmentManager().beginTransaction().hide(mProfileFragment).commit();
}
if(fragment2Open==0) {
fragment2Open=1;
getFragmentManager().beginTransaction().add(R.id.container, m2Fragment).commit();
} else {
getFragmentManager().beginTransaction().show(m2Fragment).commit();
}
}
});
final Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//hide other fragments
if(mOpenerFragment.isVisible()) {
getFragmentManager().beginTransaction().remove(mOpenerFragment).commit();
}
if(mProfileFragment.isVisible()) {
getFragmentManager().beginTransaction().hide(mProfileFragment).commit();
}
if(m2Fragment.isVisible()) {
getFragmentManager().beginTransaction().hide(m2Fragment).commit();
}
if(fragment1Open==0) {
fragment1Open=1;
getFragmentManager().beginTransaction().add(R.id.container, m1Fragment).commit();
} else {
getFragmentManager().beginTransaction().show(m1Fragment).commit();
}
}
});
final Button button3 = (Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//hide other fragments
if(mOpenerFragment.isVisible()) {
getFragmentManager().beginTransaction().remove(mOpenerFragment).commit();
}
if(m1Fragment.isVisible()) {
getFragmentManager().beginTransaction().hide(m1Fragment).commit();
}
if(m2Fragment.isVisible()) {
getFragmentManager().beginTransaction().hide(m2Fragment).commit();
}
//open fragment
if(profileFragmentOpen==0) {
profileFragmentOpen=1;
getFragmentManager().beginTransaction().add(R.id.container, mProfileFragment).commit();
} else {
getFragmentManager().beginTransaction().show(mProfileFragment).commit();
}
}
});
mProfileFragment and m2Fragment use identically onCreate and onCreateView methods:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview = inflater.inflate(R.layout.fragment_profile, container, false);
return rootview;
}
m1Fragment has the same methods, but gets a googleMaps fragment from its xml file.
m1Fragment:
public class Fragment1 extends Fragment implements OnMapReadyCallback {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MapFragment mapFragment=(MapFragment) getFragmentManager().findFragmentById(mapid);
if (mapFragment==null) {
mapFragment = MapFragment.newInstance();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.container, mapFragment);
fragmentTransaction.commit();
mapFragment.getMapAsync(this);
}
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap map) {
//right upper corner, location layer activated
map.setMyLocationEnabled(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_maps, container, false);
return rootView;
}
fragment_1.xml:
<fragment
android:id="#id/mapid"
class="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
MapFragment mapFragment=(MapFragment) getFragmentManager().findFragmentById(mapid);
in this line your actually tring to get the parent's fragmentManager (in your case the activity),and fail to find the fragment.
and then in this line:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.container, mapFragment);
fragmentTransaction.commit();
you are adding to the parent FragmentManager.
a quick to solution is to replace getFragmentManger() with getChildFragmentManager, which refers to the fragment's FragmentManager.
Maybe you should try to use :
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().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();
With fragment_container as FrameLayout in your activity?
I'm not 100% sure, but i think it'll avoid such issues
I just solved it:
Withing my MapsFragment class I instantiated a googleMaps fragment "twice". Trying to hide the mapFragment I just hid one of them, leaving the other one visible in front.
For people having similar issues here my solution:
I erased the
<fragment
android:id="#id/mapid"
class="com.google.android.gms.maps.MapFragment"/>
from the xml layout file.
And inside the MapsFragment class I changed
MapFragment mapFragment=(MapFragment) getChildFragmentManager().findFragmentById(mapid);
to
MapFragment mapFragment = MapFragment.newInstance();
getChildFragmentManager().beginTransaction().add(R.id.framLay, mapFragment).commit();
Now I put one clean Instance of MapFragment into my FrameLayout and it works properly.
Related
I've been developing an android app which I included the default Navigation-Drawer from Android Studio and so on. In my home fragment, I've implemented CarViews, and then set those cardview(s) OnClickListener to replace the fragment with traditional procedure.
After the fragment replacement and new page comes, I wanted to change the Actionbar title.
So in the onCreateView(...) method, I tried,
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("B");
It worked. But after pressing the hardware back button to go back to the stacked fragment, the title remains changed & it doesn't change to "Home" again. I've tried other ways. Here's my following codes. Thanks in advance.
public class HomeFragment extends Fragment implements View.OnClickListener {
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home, container, false);
Objects.requireNonNull(((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar()).setTitle("Home");
CardView cardView1 = root.findViewById(R.id.doctor_on);
CardView cardView2 = root.findViewById(R.id.ambulance_e);
CardView cardView3 = root.findViewById(R.id.maintainance_s);
cardView1.setOnClickListener(this);
cardView2.setOnClickListener(this);
cardView3.setOnClickListener(this);
return root;
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.doctor_on:
FragmentTransaction fragmentTransaction = Objects.requireNonNull(getActivity()).getSupportFragmentManager().beginTransaction();
Fragment fragment1 = new doctors();
fragmentTransaction.replace(R.id.container1, fragment1).addToBackStack(getString(R.string.menu_home)).commit();
return;
case R.id.ambulance_e:
//Put Actions
FragmentTransaction fragmentTransaction2 = Objects.requireNonNull(getActivity()).getSupportFragmentManager().beginTransaction();
Fragment fragment2 = new ambulance();
fragmentTransaction2.replace(R.id.container1, fragment2).addToBackStack(getString(R.string.menu_home)).commit();
return;
case R.id.maintainance_s:
//Put Actions
FragmentTransaction fragmentTransaction3 = Objects.requireNonNull(getActivity()).getSupportFragmentManager().beginTransaction();
Fragment fragment3 = new maintanance();
fragmentTransaction3.replace(R.id.container1, fragment3).addToBackStack(getString(R.string.menu_home)).commit();
return;
}
}
#Override
public void onResume() {
super.onResume();
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Home");
}
}
To the next fragment(where I change the titlebar and pressed back button):
public class doctors extend Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("B");
View root = inflater.inflate(R.layout.fragment_doctors, container, false);
return root;
}
}
And in the doctors Fragment... Should fix the title error.
#Override
public void onDestroyView() {
super.onDestroyView();
Objects.requireNonNull(((AppCompatActivity) Objects.requireNonNull(getActivity()))
.getSupportActionBar())
.setTitle(getString(R.string.your_title_here));
}
You have to override the method onBackPressed() and write the code:
#Override
public View onBackPressed(){
//Here goes the code that head back to your main fragment
FragmentTransaction fragmentTransaction3 = Objects.requireNonNull(getActivity()).getSupportFragmentManager().beginTransaction();
Fragment fragment3 = new maintanance();
fragmentTransaction3.replace(R.id.container1, fragment3).addToBackStack(getString(R.string.menu_home)).commit();
}
}`
Add below code in Home Fragment...
#Override
public void onHiddenChanged(Boolean hidden) {
super.onHiddenChanged(hidden);
((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Home");
}
On click of Button I am trying to open second fragment(YourResultFragment.java),
I had a lots but don't know why it is not working , here is my code which I am using
public class KnowYourBodyFragment extends Fragment {
public KnowYourBodyFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_know_your_body, container, false);
Button button = (Button)rootView.findViewById(R.id.button2);
button.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Fragment mFragment = new YourResultFragment();
getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.know_your_body_container, mFragment ).commit();
}
});
// Inflate the layout for this fragment
return rootView;
}
Use this Try to replace it with Default Container android.R.id.content.
Button button = (Button)rootView.findViewById(R.id.button2);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Fragment newFragment = new YourResultFragment();
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
// Replace whatever is in the fragment_container view with this
// fragment,
// and add the transaction to the back stack
transaction.replace(android.R.id.content, newFragment);
transaction.addToBackStack("tag");
// Commit the transaction
transaction.commitAllowingStateLoss();
}
});
What is transaction.addToBackStack("tag") ?
Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.
I have button in fragment1. I want to click this buttton and replace fragment1 by fragment2 but my app still close when I try to run it
This is my code in fragment1
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.layout_menu, container, false);
// gallery = (Gallery) rootView.findViewById(android.R.id.list);
//gallery.setAdapter(new ItemAdapter(generateData(), getActivity()));
Button button = (Button) rootView.findViewById(R.id.imageButtonAll);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
HomeFragment homeFragment = new HomeFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, homeFragment);
transaction.commit();
}
});
return rootView;
}
}
You cant open a fragment from inside another fragment.
So you have to move your code from the onClick to your activity and run it there.
In your Activity (lets assume its MainActivity):
public void openMyFragment(){
HomeFragment homeFragment = new HomeFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, homeFragment);
transaction.commit();
}
And then, add this in your fragments onClick:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity) getActivity()).openMyFragement();
}
});
The problem is that you are trying to replace a view that does not exist within the view that you are inflating. You have to switch these fragments from your FragmentActivity, which has the contentview containing the layout you are trying to replace.
MainActivity:
class MainActivity extends FragmentActivity {
public Fragment frag1;
public Fragment frag2;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
frag1 = new Frag1();
frag2 = new Frag2();
//assumption to switch to frag 1 immediately
switchToFragment(R.id.frame_container, homeFragment);
...
}
/** Switch UI to the given fragment (example) */
void switchToFragment(Fragment newFrag) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, newFrag)
.commitAllowingStateLoss();
currentFragment = newFrag;
}
}
Fragment:
....
final Activity activity = getActivity();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
about.setTextColor(Color.WHITE);
if(activity instanceof MainActivity) {
((MainActivity) activity).switchToFragment(((MainActivity) activity).frag1));
}
}
});
...
I have aŃivity with one button and frame:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="new fragment"
android:id="#+id/replaceFragment" android:layout_gravity="center_horizontal"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="fill_parent" android:layout_gravity="center_horizontal" android:id="#+id/contentFrame">
</FrameLayout>
</LinearLayout>
code:
public class MyActivity extends Activity implements View.OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button replaceBtn = (Button) findViewById(R.id.replaceFragment);
replaceBtn.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.replaceFragment:
Fragment fragment1 = new Frag1();
FragmentTransaction fTrans = getFragmentManager().beginTransaction();
fTrans.replace(R.id.contentFrame, fragment1);
fTrans.commit();
break;
}
}
}
button is pressed, etc. I create a fragment:
public class Frag1 extends Fragment implements View.OnClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag1, null);
Button replaceBtn2 = (Button) v.findViewById(R.id.replaceFragment2);
replaceBtn2.setOnClickListener(this);
return v;
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.replaceFragment2:
Frag2 fragment2 = new Frag2();
FragmentTransaction fTrans = getFragmentManager().beginTransaction();
fTrans.replace(R.id.contentFrame,fragment2);
fTrans.commit();
break;
}
}
}
it also has a button which when pressed I create fragment2:
public class Frag2 extends Fragment implements View.OnClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag2, null);
Button replaceBtn3 = (Button) v.findViewById(R.id.replaceFragment3);
replaceBtn3.setOnClickListener(this);
return v;
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.replaceFragment3:
Frag3 fragment3 = new Frag3();
FragmentTransaction fTrans = getFragmentManager().beginTransaction();
fTrans.replace(R.id.contentFrame,fragment3);
fTrans.commit();
break;
}
}
}
fragment2 also creates a new fragment3:
public class Frag3 extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag3, null);
return v;
}
}
if I do when creating each fragment will add a line:
fTrans.addToBackStack(null);
then later when I press the "back" button on the screen will appear the fragments in reverse order. but I want to when I'm in fragmente3 when you press the "back" appears immediately fragment1.
if I do not add the line fTrans.addToBackStack(null); in fragment2 they overlap.
in order to do that, you can keep the fragments backStack references in your activity or for example onBackPressed you could try something like below:
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
//back to fragment 1.
}
Here you are a function which I use for one of my projects. I implemented it in the parent of the Activity that manages the fragments. I don't know if I'm going to clarify anything about the behaviour because all you tell us in you question make sense to me.
I my case, it was a simple app (almost static), but it changes fragments and, as you want to, many times I want to go back two fragment in "history" so I think it could help.
#Override
public void replaceFragment(FRAGMENT_TYPE destinyFragmentType, int fragmentContainerID, boolean addToBackStack) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
Fragment currentFragment = FragmentFactory.getFragment(destinyFragmentType);
fragmentTransaction.replace(fragmentContainerID, currentFragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(null);
}
fragmentTransaction.commit();
}
Notes:
The second parameter is the parameter that indicates if you want to
keep in history or not. For your case: frg1 -> fr2 : param true; fr2
-> fr3 : param false;
I used a Factory in order to get a cleaner code, but you can use
whatever you want, change the header, give the function your fragment
etc.
I hope this helps!!
I'm trying do understand the lifecycle of Fragment.
I have a MainActivity, with a layout including two FrameLayout to load Fragments, one for a top bar with a vertical menu, another one for the content.
When I start the app, an instance of HomeFragment is loaded in the content FrameLayout container. Then when I click on a button in the menu, an instance of SomeFragment replaces it.
When on the SomeFragment content, I click on the device's Home button, and all the visible Fragment (i.e. the TopBarFragment and the SomeFragment) are destroyed and detached from the MainActivity, i.e. onDestroyView, onDestroy and onDetach are called for both.
Now when I restart the app, the TopBarFragment and the SomeFragment are created and destroyed (full lifecycle from onAttach to onDetach) and then the TopBarFragment and the HomeFragment are created, as expected from the code in the MainActivity's onCreate.
Why are the TopBarFragment and the SomeFragment, i.e. the latest visible Fragment before clicking on the device's Home button, recreated and destroyed before executing what's in the MainActivity's onCreate?
Note: in order to test my app I checked the option Don't keep activities in my device's developer options.
MainActivity.java
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment homeFragment = new HomeFragment();
fragmentTransaction.replace(R.id.content_container, homeFragment, "");
Fragment topBarFragment = new TopBarFragment();
fragmentTransaction.replace(R.id.top_bar_container, topBarFragment, "top_bar_fragment");
fragmentTransaction.commit();
}
}
main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="44dp" />
<FrameLayout
android:id="#+id/top_bar_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false" />
</RelativeLayout>
TopBarFragment.java
public class TopBarFragment extends Fragment{
private int mSelectedMenuOption = 0;
private LinearLayout mVerticalMenu;
private Boolean mMenuIsOpen = true;
private ImageButton btn_01, btn_02; // there are more
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.top_bar, container, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
btn_01 = (ImageButton) getView().findViewById(R.id.btn_01);
btn_02 = (ImageButton) getView().findViewById(R.id.btn_02);
btn_01.setOnClickListener(mButtonClickListener);
btn_02.setOnClickListener(mButtonClickListener);
mVerticalMenu = (LinearLayout) getView().findViewById(R.id.vertical_menu);
toggleMenu(0);
Button btn_menu = (Button) getView().findViewById(R.id.btn_menu);
btn_menu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// toggle vertical menu
}
});
}
private OnClickListener mButtonClickListener = new OnClickListener()
{
#Override
public void onClick(View v) {
/* ... */
if(!v.isSelected()){
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
switch(v.getId()){
case R.id.btn_01:
Fragment homeFragment = new HomeFragment();
fragmentTransaction.replace(R.id.content_container,homeFragment, "");
fragmentTransaction.commit();
break;
case R.id.btn_02:
Fragment someFragment = new SomeFragment();
fragmentTransaction.replace(R.id.content_container, someFragment, "");
fragmentTransaction.commit();
break;
}
}
}
};
}
SomeFragment
public class SomeFragment extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
((TopBarFragment)getActivity().getSupportFragmentManager().findFragmentByTag("top_bar_fragment")).setSelectedButton(1);
return inflater.inflate(R.layout.some_fragment, container, false);
}
}