Added Fragment is not removed and causing App crash - android

Adding fragment works ok, but removing it crashes app. I thought removing will be straight forward but Im still a beginner so I must missing something. Can you take a look?
public class MainActivity extends Activity implements OnClickListener {
Button addFragment, closeFragment;
android.app.FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager
.beginTransaction();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initialize();
}
private void initialize() {
addFragment = (Button) findViewById(R.id.bAddF);
closeFragment = (Button) findViewById(R.id.bCloseF);
addFragment
.setOnClickListener((android.view.View.OnClickListener) this);
closeFragment
.setOnClickListener((android.view.View.OnClickListener) this);
}
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
//Instantiate fragment from ExampleFragment class
ExampleFragment fragment = new ExampleFragment();
switch(arg0.getId()){
case R.id.bAddF:
transaction.add(R.id.fragment_holder, fragment).commit();
break;
case R.id.bCloseF:
transaction.remove(fragment).commit();
break;
}
}
}

Your app crashes because you are saving the FragmentTransaction as a member variable. But FragmentTransactions cannot be reused. Furthermore, you are creating a new Fragment everytime, which means that the currently added Fragment will not be removed when clicking the remove Button.
Do it this way:
private ExampleFragment mFragment;
#Override
public void onClick(View v) {
//Instantiate fragment from ExampleFragment class
if(mFragment == null) mFragment = new ExampleFragment();
switch(v.getId()){
case R.id.bAddF:
getFragmentManager().beginTransaction().add(R.id.fragment_holder, mFragment, "tag").commit();
break;
case R.id.bCloseF:
getFragmentManager().beginTransaction().remove(mFragment).commit();
break;
}
}
Create a new FragmentTransaction for each add() or remove() procedure.
As an alternative for keeping your ExampleFragment as a member variable, you can use FragmentManager.findFragmentByTag("tag") to get the currently added fragment.
Alternative way:
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.bAddF:
getFragmentManager().beginTransaction().add(R.id.fragment_holder, new ExampleFragment(), "tag").commit();
break;
case R.id.bCloseF:
Fragment frag = getFragmentManager().findFragmentByTag("tag");
getFragmentManager().beginTransaction().remove(frag).commit();
break;
}
}

Related

How to save state of Fragment in a SpaceNavigationView

I've already searched about this and found nothing, which helps me to solve my problem. In my Code I have a SpaceNavigationView (like BottomNavigationView) with five Fragments.
So in Fragment A, I've put a Recyclerview. If an item of the Recyclerview gets clicked, it will replace the current fragment with a new child fragment B.
In Fragment B I've set a Chronometer, which should count the time, when it gets pressed.
Now if I switch from Fragment B to Fragment C and go back to Fragment B, the Chronometer starts by zero, because the fragment was replaced.
I've tried to used onSaveInstanceState, so that it can be called when Fragment is recreated, but this doesn't work for me.
Here's a piece of the HomeActivity, which includes all the Fragments.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
init();
setFragment(fragmentHome);
navigationView.initWithSaveInstanceState(savedInstanceState);
navigationView.addSpaceItem(new SpaceItem("", R.drawable.bottom_baby));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.bottom_advise));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.ic_favorite_black_24dp));
navigationView.addSpaceItem(new SpaceItem("", R.drawable.ic_settings));
navigationView.setSpaceOnClickListener(new SpaceOnClickListener() {
#Override
public void onCentreButtonClick() {
setFragment(fragmentPlayground);
navigationView.setCentreButtonSelectable(true);
}
#Override
public void onItemClick(int itemIndex, String itemName) {
switch (itemIndex) {
case 0:
setFragment(fragmentHome);
return;
case 1:
setFragment(fragmentAdvising);
return;
case 2:
setFragment(fragmentMemories);
return;
case 3:
setFragment(fragmentSettings);
return;
default:
setFragment(fragmentHome);
return;
}
}
#Override
public void onItemReselected(int itemIndex, String itemName) {
Toast.makeText(HomeActivity.this, itemIndex + " " + itemName, Toast.LENGTH_SHORT).show();
}
});
}
private void setFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.container, fragment);
fragmentTransaction.commit();
}
So if i navigate now to FragmentHome and use the OnClickListener for Reycleritems, I will switch to Fragment_Chronograph
#Override
public void onItemClick(int position) {
switch (position) {
case 0:
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment_chronograph).commit();
}
So now I'm in Fragment_Chronograph and want to save the base for Chronograph. I will save the variable in onSavedInstanceState, which gets called when Activity is Paused.
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
startTime = SystemClock.elapsedRealtime() - chronometerLeft.getBase();
outState.putLong(CHRONOLEFT_TIME_SAVE_ID,startTime);
super.onSaveInstanceState(outState);
#Override
public void onPause() {
super.onPause();
onSaveInstanceState(new Bundle());
}
At the end i've put this code for restore in the OnCreate Method:
if (savedInstanceState != null) {
startTime = savedInstanceState.getLong(CHRONOLEFT_TIME_SAVE_ID,0);
chronometerLeft.setBase(startTime - SystemClock.elapsedRealtime());
chronometerLeft.start();
The OnSaveInstanceState gets called, but in the OnCreate Method it won't be called. I would be very thankful if someone could help me with this problem. I'm searching for days and didnt get a solution.
in set fragment method , first find fragment with id and then check if it's not null replace that
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
if (fragment == null) {
fragment = new PlaceholderFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, TAG_PLACEHOLDER)
.commit();
Note : you must add tag to your fragments
#Arvin
FragmentHome ist initiated in the init() method;
private void init() {
navigationView = findViewById(R.id.space);
fragmentHome = new FragmentHome();
fragmentAdvising = new FragmentAdvising();
fragmentMemories = new FragmentMemories();
fragmentSettings = new FragmentSettings();
fragmentPlayground = new FragmentPlayground();
============= UPDATE =================
Problem was solved. I didn't have to use OnSavedInstanceState. I used a Helperclass to store the variable of the Chronometerbase. In OnCreate method i check if the variable is not null, then restore the before saved base.

Fragment not launched from another fragment

I have a fragment with two buttons: btnEdit and btnExamen.
Each button has an OnClickListener.
Here is the code:
btnEdit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mPref = getActivity().getSharedPreferences(MISDATOS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = mPref.edit();
editor.putString("id_empleo","No");
editor.putString("id_grado","No");
editor.apply();
HomeFragment firstFragment = new HomeFragment();
((MainActivity)getActivity()).getSupportFragmentManager().beginTransaction()
.replace(R.id.frame, firstFragment).commit();
}
});
btnExamen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "Iniciando examen", Toast.LENGTH_SHORT).show();
ExamenesFragment secondFragment = new ExamenesFragment();
((MainActivity)getActivity()).getSupportFragmentManager().beginTransaction()
.replace(R.id.frame, secondFragment).commit();
}
});
The issue is that the first method works fine, HomeFragment is launched.
But the second method is not launching fragment ExamFragment. The toast is launched.
I am not able to see any difference at the code that could be the problem.
Replace the current Fragment with the new Fragment and push transaction onto the backstack. This preserves back button behaviour...
Creating a new Activity really defeats the whole purpose to use fragments anyway...very counter productive.
btnEdit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
...
// Create new fragment and transaction
HomeFragment firstFragment = new HomeFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the frame view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.frame, firstFragment );
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
});
btnExamen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
...
ExamenesFragment secondFragment= new ExamenesFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frame, secondFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});

Call Fragment from activity

I have a Navigation drawer activity and it contains couple of Fragments. My problem is I want to call the Navigation drawer activity class in button click which should have fragment B, by default navigation drawer has fragment A.
I am posting my code here:
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(getApplicationContext(),Navigation.class));
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.content_frame, new FragmentB());
tx.commit();
finish();
}
});
Where content_frame is the area were I want to replace fragment B view..
try this approach
in your button click send extra data to check what fragment you need
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), Navigation.class);
intent.putExtra("SELECTEDVALUE", 2);//1 for fragament A use 2 for fragment B
startActivity(intent);
}
});
Now, in your oncreate() method of Navigation Activity
Bundle extras = savedInstanceState != null ? savedInstanceState : getIntent().getExtras();
int selectedValue = extras.getInt("SELECTEDVALUE");
switch (selectedValue) {
case 1:
goToFragment(new A(), false);
break;
case 2:
goToFragment(new B(), false);
break;
finally gotoFragment is...
private void goToFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.replace(R.id.container, fragment).commit();
}
Try below code;
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Fragment fragment = new FragmentB();
FragmentManager fm =getActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
}
});
Just make an instance of FragmentB like this
FragmentB fragment - new FragmentB();
and then pass it like this .
tx.replace(R.id.content_frame, fragment);
tx.commit();
now you can access all the variables and methods of fragment from your activity using instance fragment. like this fragment.methodName();
Try this code:
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getActivity().getSupportFragmentManager().beginTransaction()
.add(R.id.content_frame, new FragmentB(), "fragmentB").addToBackStack(null).commit();
}
});
hope it'll solve your problem.

Android - Show fragment on resume

I currently have a navigation drawer, which has a few fragments (Home, Help, About) in my activity. On startup it opens up Home. The issue i'm having is that when i go to another fragment such as Help and then proceed to put the phone to sleep and subsequently turn on the phone back on it'll always return to Home instead of help.
I'm quite new to lifecycles but was hoping to get some feedback on how to resume from a different fragment.
EDIT: Provided relevant code
Update: Realised that this happens because i reinit the views on resume.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeUI();
}
private void initializeUI() {
fragAbout = new About();
fragHelp = new Help();
fragHome = new MyViewPager();
// Adding fragments to activity
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.main_activity_fraglayout, fragHome);
transaction.commit();
...
}
private void addDrawerItems() {
...
DrawerItemAdapter drawerAdapter = new DrawerItemAdapter(this, R.layout.nav_list_row, drawerItems);
mDrawerList.setAdapter(drawerAdapter);
mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
...
newFragOnClick(fragHome, "Home");
break;
case 1:
...
newFragOnClick(fragSettings, "Help");
break;
case 2:
...
newFragOnClick(fragAbout, "About");
break;
default:
break;
}
}
});
}
private void newFragOnClick(Fragment frag, String actionBarTitle){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_activity_fraglayout, frag);
transaction.commit();
}
Use sharedpreferences to save the current tab position and in onResume() use it to move to the saved position.

Trying to make a flexible UI

I'am trying to make a app with a flexible UI.
I already implemented for handset devices (I have one activity and multiple fragments), and what I done was: The main fragment is a dashboard, and when I click in one button of it, he dashboard is replaced by a new fragment ( the clicked feature). Here is the code:
Dashboard fragment:
public class DashboardFragment extends Fragment {
GridView gridView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.activity_dashboard, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
gridView=(GridView)getView().findViewById(R.id.dashboard_grid);
gridView.setAdapter(new ImageAdapter(getActivity()));
gridView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment fragment = null ;
switch (position) {
case 0:
fragment = new TestFragment();
break;
case 1 :
fragment = new TestFragment();
break;
case 2 :
fragment = new TestFragment();
break;
case 3 :
fragment = new TestFragment();
break;
case 4 :
fragment = new TestFragment();
break;
}
transaction.replace(R.id.container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
}
}
and my Main Activity:
public class MainActivity extends FragmentActivity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.container) != null) {
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
DashboardFragment firstFragment = new DashboardFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.container, firstFragment).commit();
}
}
}
Now, what I want is to adapt this code and use a layout for tablets, with the dashboard on the left and the choosen fragment on the right, like this:
What could I do? I already tried to adapt this example, but I can't because they only update the fragment, they don't replace it.
Check this great article about multi-pane development.
It also includes an example (Sections 10 and 11)
Basically you can check whether there is a fragment element for your "Fragment B" in the current layout. If yes, you just update its content, if no, then start an activity which has it in its layout, or replace one in the current layout.
DetailFragment fragment = (DetailFragment) getFragmentManager().findFragmentById(R.id.detail_frag);
if (fragment==null || ! fragment.isInLayout()) {
// start new Activity or replace
}
else {
fragment.update(...);
}

Categories

Resources