In Android, when we replace a container view with a new fragment, we can use replace() and addToBackStack( ), so on pressing back button we goto previous fragment.
But what if the following occurs :
Activity1 (fragment1 -> fragment2 [calls startActivity for Activity2]) -> Activity2
Within Activity1, I can press back button to go from fragment2 to fragment1. But when fragment2 starts another activity, on pressing back button from Activity2, it takes me to fragment1 in Activity1. How can I make back button press from Activity2 come back to fragment2 in Activity1 ?
Code :
// In Activity1 - starts with a ListFragment
#Override
protected void onResume() {
super.onResume();
getFragmentManager().beginTransaction()
.replace(R.id.container, ListFragment.newInstance(0))
.commit();
}
// In Activity1 , each item in list replaces the container view
// with new fragment
#Override
public void onItemSelected(int position) {
if(position == 0) {
getFragmentManager().beginTransaction()
.replace(R.id.container, Example1_Fragment.newInstance(0))
.addToBackStack(null)
.commit();
}
....
}
// In Example1_Fragment
public class Example1_Fragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.example1_fragment, container, false);
Button btnIntent = (Button) view.findViewById(R.id.btnIntent);
btnIntent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction("com.android.test2.app.example1_action");
startActivityForResult(intent,1);
}
});
return view;
}
// this is why I want to come back to Example1_Fragment -
// the activity I start has to send a result back to
// the Example1_Fragment, but on back button, it takes me
// back to the ListFragment, and I cannot take any UI
// action (change to UI) in Example1_Fragment, as the
// ListFragment gets displayed - the onActivityResult of
// Example1_Fragment does get called
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1 && resultCode == Activity.RESULT_OK){
// do something
}
}
Do not replace Fragment1 in onResume() callback, this method will always be called when you come back to activity1, please do it in onCreate() callback.
You can manually set the back pressed calls inside of this method in your Second Activity. So that when you press back, you can find fragment 2 by its tag and add it back.
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
Intent i = new Intent(this, firstActivity.class);
startActivity(i);
FragmentTwo fragmentTwo = manager.findFragmentByTag("fragmentTwoTag");
manager.beginTransaction.replace(fragmentOne, fragmentTwo).addToBackStack(null).commit();
}
Related
I have a navigation drawer activity with fragments and I will send each fragment to an activity. I have a problem if I select option 3 of my menu that is a fragment that will send me to an activity, but when I return with the Back button, it sends me to option 1, what I want is that I return to option 3.
How can I change this?
I tried to do it by parentActivity but it did not work
Thank you.
My navigation drawer activitywithfragments
When I click on the button, it sends me to an activity that is this
And in the activity I have a toolbar to return and what I want is to return to option 3 and not to 1.
What do I need to do in my code or what can I place?
My fragment code option 3
public class Mis_Aliados extends Fragment {
Button boton;
public Mis_Aliados() { }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.persona_mi_perfil, container, false);
boton=view.findViewById(R.id.buton);
boton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ActividadEx.class);
startActivity(intent);
}
});
return view;
}
}
My activity code
public class ActividadEx extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pruebaactividad);
Toolbar toolbarback=findViewById(R.id.include);
setSupportActionBar(toolbarback);
getSupportActionBar().setTitle("Activity");
ActionBar actionBar=getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
In your ActividadEx add this method to handle toolbar back button press
I'm putting 1 intent extra to make sure you want to open Option 3 when you are back to Navigation drawer Activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NavgationDrawerActivity.class);
intent.putExtra("openOption3", true);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}
On NavigationDrawerActivty inside onCreate()
you can check if Bundle has data or not. If its empty you can open Option1 fragment.
If it has data check it. and open Option3 Fragment.
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if (bundle.getBoolean("openOption3", false)) {
//Use Fragment Transaction to Open Option 3 Fragment
} else {
//Open Option 1 Fragment Or any other Fragment
}
} else {
//Open Option 1 Fragment Or any other Fragment
}
Feel free to comment if you've any queries.
Parent Activity have 3 fragment A,B and C. When i select fragment B that showing one fab button that call new Activity (Contact Activity) for result then i called finish() method in Contact Activity it did not back to fragment B instead it showing Parent Activity with A fragment.
onCreate() method set first fragment A.
if(messaging_fragment == null) {
messaging_fragment = new all_messaging_fragment();
}
fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frameLayout, messaging_fragment).commit();
Parent Activity replace fragment using setFragment() method.
private void setFragment(android.support.v4.app.Fragment fragment, String tag)
{
fragmentManager.beginTransaction().replace(R.id.frameLayout,fragment).addToBackStack(tag).commit();
}
Fragment B called new Activity for result.
FloatingActionButton createMessageFloatButton = (FloatingActionButton) rootview.findViewById(R.id.createNewGroupFloatbutton);
createMessageFloatButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getContext(),add_contact_list_activity.class);
startActivityForResult(intent,2);
}
});
Contact Activity call the finish() method.
ImageView back_button = (ImageView) findViewById(R.id.back_button_conatc_List_id);
back_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setResult(2);
finish();
}
});
Maybe you should 'add' messaging_fragment in the onCreate() to set first fragment A. Let us know if it is working.
I created an interface so I can set text on a FragmentB when I press a TextView on FragmentA. Something is not working and I can't figure this out.
I've created an interface called Communicator:
public interface Communicator {
void respond(String data);
}
On FragmentA I've set a reference on the interface called Communcator and an OnClickListener on the TextView:
Communicator comm;
homeTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
comm.respond("Trying to set text on FragmentB from here");
}
});
FragmentB, set my method to change text:
public void setText(final String data) {
startTripTxt.setText(data);
}
Finally in MainActivity I've implemented the interface .. I think here is where I'm doing something wrong:
#Override
public void respond(String data) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container_main, new FragmentB(), "fragment2").addToBackStack(null).commit();
FragmentB fragmentB= (FragmentB) getSupportFragmentManager().findFragmentByTag("fragment2");
if (fragmentB != null) {
fragmentB.setText(data);
}
}
Fragment 2 loads, but the text is empty.
Fragment 2 loads, but the text is empty.
You implement Communicator is ok but the way you call FragmentB and passing data is not ok. That 's is the reason why you cannot get text from FragmentB. the right way to send data to FragmentB should be like this:
public static FragmentB createInstance(String data) {
FragmentB fragment = new FragmentB();
Bundle bundle = new Bundle();
bundle.putString("data", data);
fragment.setArguments(bundle);
return fragment;
}
And you can get data from FragmentB by:
Bundle bundle = getArguments();
if (bundle != null) {
String data = bundle.getString("data");
}
It looks like after you declare fragmentB, you're meaning to set the text on that fragment. You are Instead calling trainFinderFragment.setText(). Is that your issue?
FragmentB fragmentB= (FragmentB) getSupportFragmentManager().findFragmentByTag("fragment2");
if (fragmentB != null) {
fragmentB.setText(data);
}
I have a fragment with a view and an options menu:
public class OfferingsFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
From the options menu, the user opens this preference fragment, which is hosted by the SettingsActivity:
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
}
The view of the OfferingsFragment depends on one of the preferences. That is, after this preference has changed, the OfferingsFragment must be refreshed by calling onCreateView again. What I do is this:
Open preference screen from OfferingsFragment's option menu
Change preference
Return to OfferingsFragment
If I return to the OfferingsFragment via the Home Button (left arrow in ActionBar), then the OfferingsFragment gets refreshed by calling its onCreateView (which is the desired effect). However, if I return to the OfferingsFragment via the Back Button (on the device), onCreateView is NOT CALLED and thus the view is NOT re-created. What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
What Happens
When you press Up button parent activity is called via startActivity which means a new instance is created by default.
When you press Back button current activity is finished and you're back in the previous activity and its already existing instance (it was in stopped state).
How To Deal With It
What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
Start the settings activity via startActivityForResult:
public static final int RC_SETTINGS = 1;
private void startSettingsActivity() {
Intent i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, RC_SETTINGS);
}
When the result comes back reattach the fragment. This will recreate its view hierarchy.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Call to super if you value your life. And want proper lifecycle handling.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS) {
// If we just came back from SettingsActivity...
// ...reattach fragment and trigger view recreation.
final FragmentManager fm = getFragmentManager();
final f = fm.findFragmentById(android.R.id.content);
fm.beginTransaction().detach(f).attach(f).commit();
}
}
Replace the fragment ID with whatever you used.
Pro tip
If your fragment is not misconfigured you should be able to call
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
} else {
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);
}
}
}
This is both resourceful and practical as your original code would lose state (for example scroll position) on configuration change.
I got a situation here .
I have FragmentActivty which holds a Fragment . when i click on a button in Fragment i am going to a Activty . when i reach the activity i do something which will affect the data displayed in Fragment where i came from. So in order to bring that changes in fragment i would like to give a callback from the Activity to Fragment. First i thought of implementing onActivityResult. But i realized it's not what i needed.
Is my approach is wrong?? Please guide me
MyActivity extends FragmentActivity
MyActivity holds
MyFragment extends Fragment
From here i'm going to
SecondActivity extends Activity
from SecondActivity i need to get a callback to MyFragment . Is there anything wrong with my approach ??
EDIT:
public class MainActivity extends FragmentActivity {
private FrameLayout frameLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout = (FrameLayout) findViewById(R.id.framelayout);
loadFragment();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void loadFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
MyFragment myFragment = new MyFragment();
fragmentTransaction.add(R.id.framelayout, myFragment);
fragmentTransaction.commit();
}
}
MyFragment.java
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_view, container, false);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
callActivity();
}
});
return view;
}
private void callActivity() {
Intent intent = new Intent(getActivity(), SecondActivity.class);
startActivityForResult(intent, 10);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// super.onActivityResult(requestCode, resultCode, data);
Log.e("MyFragment Inside", "Onresultttt");
if (requestCode == 10) {
if (resultCode == Activity.RESULT_OK) {
Log.e("Result code", Activity.RESULT_OK + " okkk");
}
if (resultCode == Activity.RESULT_CANCELED) {
Log.e("Result code", Activity.RESULT_CANCELED + "cancelll inside fragment");
}
}
}
}
SecondActivity.java
public class SecondActivity extends Activity {
private static final String TAG = "second activity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secondactivity_view);
}
#Override
public void onBackPressed() {
// super.onBackPressed();
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
Log.e(TAG, "result setted");
finish();
}
}
Refer this question it will help you understand what is happening:
onActivityResult is not being called in Fragment
If I understand your situation correctly, then the correct way of handling the communication is to have SecondActivity pass back information to MyActivity, which will in turn configure it's instance of MyFragment directly. You don't want to be accessing Fragments from Activities unless the Fragment is attached to the Activity.
As for how to do the communication, as you suggested, one way of doing it would be through the use of startActivityForResult(). See this answer for more details: How to manage `startActivityForResult` on Android?
Just a note about startActivityForResult(). If you are calling it from the Fragment then your Fragment will receive the result, not your Activity. There are also some other issues with calling startActivityForResult() from a Fragment, so I would generally recommend that you instead call it from the Activity and therefore handle the result from the Activity.
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, INTENT_CODE);
The best practice way of communicating from a Fragment to an Activity is by defining an Interface in the Fragment, which the Activity implements.
public class MyFragment extends Fragment {
public interface MyFragmentListener() {
public void onMyFragmentEvent();
}
public void startTheActivityForResult() {
((MyFragmentListener)getActivity()).onMyFragmentEvent();
}
}
public class MainActivity extends FragmentActivity implements MyFragmentListener{
#Override public void onMyFragmentEvent() {
}
}
The Fragment then simply casts the reference to the Activity it is attached to, knowing that the Activity must implement the Listener, thus allowing you to reuse the Fragment in other Activities.