Fragment: which callback invoked when press back button & customize it - android

I have a fragment:
public class MyFragment extends Fragment{
...
#Override
public View onCreateView(...){...}
...
}
I instantiate it:
MyFragment myFragment = new MyFragment();
I use the above fragment to replace the current fragment:
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// replace fragment
fragmentTransaction.replace(R.id.fragment_placeholder, myFragment, "myTag");
// NOTE: I did not add to back stack
Now, myFragment is showing on the screen. NOTE: I did not add myFragment to back stack.
My two questions:
1. If now, I press mobile phone back button, which fragment's life cycle callback will be invoked??
2. How can I customize the back button click listener in MyFragment class? (please do not suggest me to do myFragment.getView().setOnclickListener, but do it in MyFragment class)

Question 1: See http://developer.android.com/reference/android/app/Fragment.html#Lifecycle:
"As a fragment is no longer being used, it goes through a reverse series of callbacks:
onPause() - fragment is no longer interacting with the user either because its activity is being paused or a fragment operation is
modifying it in the activity.
onStop() - fragment is no longer visible to the user either because its activity is being stopped or a fragment operation is modifying it
in the activity.
onDestroyView() - allows the fragment to clean up resources associated with its View.
onDestroy() - called to do final cleanup of the fragment's state.
onDetach() - called immediately prior to the fragment no longer being associated with its activity."
Question 2: If you must know that it was the back button specifically that is triggering the callbacks, You can capture the back button press in your Fragment's Activity and use your own method to handle it:
public class MyActivity extends Activity
{
//...
//Defined in Activity class, so override
#Override
public void onBackPressed()
{
super.onBackPressed();
myFragment.onBackPressed();
}
}
public class MyFragment extends Fragment
{
//Your created method
public void onBackPressed()
{
//Handle any cleanup you don't always want done in the normal lifecycle
}
}

androidx.activity 1.0.0-alpha01 is released and introduces ComponentActivity, a new base class of the existing FragmentActivity and AppCompatActivity.
You can now register an OnBackPressedCallback via addOnBackPressedCallback to receive onBackPressed() callbacks without needing to override the method in your activity.

Related

Call a function of Activity from a fragment

I want to call the function Ask() of MainActivity from fragment2.
How can I call a function of MainActivity from fragment2?
I want to import results into a page called from fragment2.
Edit:
I already see that discussion, but don't have the solution of my problem.
Make that function static, after that you can access that function in Fragment e.g. MainActivity.Ask();
From fragment to activty:
((YourActivityClassName)getActivity()).yourPublicMethod();
From activity to fragment:
FragmentManager fm = getSupportFragmentManager();
//if you added fragment via layout xml
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentById(R.id.your_fragment_id);
fragment.yourPublicMethod();
If you added fragment via code and used a tag string when you added your fragment, use findFragmentByTag instead:
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentByTag("yourTag");
Cheers!
I would recommend you read this documentation.
call to function Ask() of MainActivity from fragment2.
For this you need a create a interface in your fragment2. The below code is an example from the document. You shouldn't ignore the onAttach method in your fragment as well.
public class Fragment2 extends ListFragment {
OnCallActivityListener mCallback;
// Container Activity must implement this interface
public interface OnCallActivityListener {
public void callAskMethod();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnCallActivityListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the callAskMethod() method (or other methods in the interface) using the mCallback instance of the OnCallActivityListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.callAskMethod();
}
After that you should implement the interface in your host activity.
public static class MainActivity extends Activity
implements Fragment2.OnCallActivityListener{
...
public void callAskMethod() {
// Do something here
ask();
}
}
So that is it. You have called ask() method from fragment2 fragment.
I want to import results into a page called from fragment2.
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.
In your `MainActivity you can call send the result to the fragment.
Fragment2 fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.article_fragment);
So you have a instance value of the Fragment2 in MainActivity. So you can any public method of the fragment2 from there.
for example
fragment2.doSomething();
That's it.

Android Fragments - Should we really save state?

I have implemented a simple activity with this code:
public class MainActivity extends AppCompatActivity implements Fragment_1.Operations{
Fragment_1 fragment_1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmentactivity);
fragment_1=(Fragment_1)getSupportFragmentManager().findFragmentByTag("fragment_1");
}
//called on buttonclick, fired from a button existing in R.layout.fragmentactivity
public void createFragment(View view){
if (getSupportFragmentManager().findFragmentByTag("fragment_1")==null){
fragment_1=new Fragment_1();
FragmentTransaction transaction=getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragmentactt,fragment_1,"fragment_1");
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
}
else{
fragment_1=(Fragment_1)getSupportFragmentManager().findFragmentByTag("fragment_1");
}
//Simply adding item to the listview contained in fragment_1.
fragment_1.add("Project #1");
fragment_1.add("Project #2");
fragment_1.add("Project #3");
}
//callback of interface "Operations"
#Override
public void buttonClicked() {
FragmentTransaction transaction=getSupportFragmentManager().beginTransaction();
if (fragment_1.isAdded()){
transaction.remove(fragment_1);
//transaction.addToBackStack(null);
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
}
}
}
Well, the doubt came from the fact that no "onSaveInstanceState" needed to be implemented, everything got "saved" without any problems.
So, why should i use putfragment and getFragment? Do these methods need to be called in order to avoid that, when OS kills app process, they would be lost? This is the only reason i can imagine to force onSaveInstanceState and onRestoreInstanceState methods.
Any help is appreciated.
Activity and fragment lifecycles are linked so when any callback method such as onResume is called for the activity, it is called for the fragment too.
putFragment and getFragment help the activity to manage its fragment child's lifecycle. The activity also has to save instance state.
In order to be activity independant, a fragment can manage his own instance state.

Unable to close FragmentActivity correctly

I'm facing a strange problem inside my app. From a fragment, if i push a button, i start new FragmentActivity that contains a fragment and some other elements, but if i would go back to previous Activity (that contain fragment that start the current activity), i need to push back button twice.
First time fragmentActivity seem close itself, but it reopens again. I close FragmentActivity as always:
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
so what's wrong?
i start new FragmentActivity that contains a fragment and some other elements
You're most likely adding that FragmentTransaction to the backstack, which is why it's consuming the back button press. You shouldn't need to override onBackPressed() at all. Instead, look at the code where you add the Fragment to the new FragmentActivity and ensure you're not calling .addToBackStack() on the initial transaction. For example, typically in onCreate() you would do something like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.your_container_id, new YourInitialFragment())
.commit();
}
}

Similar onBackPressed() for Fragments?

Hello my Android application is using fragments. I am wondering if there is a way I can use the idea of the onBackPressed() method in my app with fragments. I have previous and next buttons, but as of now I am just creating new fragments and replacing, and none of the data gets saved. Is there a way to save my data/go back once I have gone forward?
The concept of Fragment is different of Activity.
One Activity could have a many Fragments, read that:
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities. You
can think of a fragment as a modular section of an activity, which has
its own lifecycle, receives its own input events, and which you can
add or remove while the activity is running (sort of like a "sub
activity" that you can reuse in different activities).
See more here: http://developer.android.com/guide/components/fragments.html
SOLUCTION
So if you wanna handle the onBackPressed behavior in you Fragment you could do that:
package com.example.stackoverflowsandbox;
import android.app.Activity;
import android.app.Fragment;
public class MainActivity extends Activity {
public class MyFragment extends Fragment {
public void onBackPressed() {
// your code here...
}
}
private MyFragment myFragment;
#Override
public void onBackPressed() {
// super.onBackPressed(); // comment to not back
this.myFragment.onBackPressed(); // the onBackPressed method of Fragment is a custom method
}
}
Sorry, do not know if I understand your question, but if the idea and have control of direct backbutton in its fragment, and from it to perform some task of data persistence, you can add your fragment to control stack FragmentManager, the as follows.
FragmentManager fm = getSupportFragmentManager();
MyFragment mMyFragment = new MyFragment();
fm.beginTransaction()
.add(mMyFragment, "mMyFragment")
.addToBackStack( null )
.commit();
In the fragment you need to implement the interface OnBackStackChangedListener
In Fragment:
public class MyFragment extends Fragment implements OnBackStackChangedListener {
#Override
public void onBackStackChanged() {
//your code here
}
}
If you just keep the values
public class MyFragment extends Fragment {
String valeu;
#Override
public void onCreate( final Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
if ( savedInstanceState != null ) {
this.valeu = savedInstanceState.getString( "key" );
}
}
#Override
public void onSaveInstanceState( final Bundle outState ) {
super.onSaveInstanceState( outState );
outState.putString( "key", "Your content" );
}
}
while moving to front fragment, save the state of previous fragment using onSaveInstanceState()
while moving back restore the state in onCreate() or onCreateView() in the previous fragment

Android managing fragments from activity elegantly

Description of what I'm trying to accomplish:
I have an app that uses a FragmentActivity with a LinearLayout as a container for the fragments. I click different buttons on the FragmentActivity UI and add and remove Fragments to the container in the FragmentActivity. In addition to clicking buttons on the FragmentActivity UI, each Fragment also has buttons that can be clicked which will remove the current fragment and add a different fragment in its place.
The Android way of doing things as I understand it:
I have been reading up on how to do this and as I understand it, the 'proper' way of doing things is to use the FragmentActivity as sort of a relay station and have each fragment do callbacks to the FragmentActivity to communicate events and deal with them.
Scenario:
So let's say that the FragmentActivity is displaying Fragment A and when the user clicks a button in FragmentA I want to stop showing FragmentA and start showing FragmentB. To do this I have created an interface in FragmentA called AListener. In the onAttach() method of FragmentA I use the suggested method of checking that the FragmentActivity implements AListener. When the button in FragmentA is clicked I use one of the callback methods from AListener to communicate the click event to the FragmentActivity. In the FragmentActivity I create an instance of FragmentB and add it to the container in FragmentActivity. Then if some event happens in FragmentB I use the same scheme to communicate the event to the FragmentActivity and do something interesting.
So what's the problem?
For my application I have found this scheme of having Fragments call back to the FragmentActivity and then having the FragmentActivity create a new fragment or call forward to and existing fragment very cumbersome. I have many fragments that need to be displayed by the FragmentActivity and therefore I am implementing an interface for every type of fragment that needs to be displayed (Each fragment is different so they each have their own interface). This causes clashes when I have two interfaces that have the same method signatures and I'm forced to rename one of the methods.
For instance, if I want to attach a listener to a fragment using the onAttach() method of the fragment, then my FragmentActivity must implement the interface. I have found several instances where I have callback methods that have the same name (or I'm forced to name them something similar but different because of a namespace collision). One solution to this would be to use an anonymous classes as callbacks instead of having the FragmentActivity implement the interface. This seems to work well enough, but goes against what the Android documentation says about using the onAttach() method to set the listener.
Are there any elegant ways to approach this problem? It seems to me the tradeoff is that you either force the FragmentActivity to implement an interface for each Fragment that you want to display in it and have the fun problem of watching out for method signature collisions, or you go against the Android documentation and use Anonymous classes to handle the callbacks (not sure of the implications of this).
I am fairly new to Java and feel like I could be missing a concept here that would solve my problem. Can anyone set me straight on how to solve this problem elegantly?
I completely understand your problem since i was dealing it for a long time. Here is the solution i came up right now! It may need some modification based on your need but i it works well.
first of all to to make communicating of event easier in your app use an EventBus! here is the most famous one https://goo.gl/nAEW6
event bus allows you to send event from anywhere to anywhere without need to worry about implementing interfaces, broadcast receivers, threading, etc.
Then add FragmentOrganizer to your app. It's a base class for all of your Fragment Organizers. basically you need one for each activity. Here is the code
public abstract class FragmentOrganizer {
protected FragmentManager fragmentManager;
public FragmentOrganizer(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
openFragment(getInitialFragment());
EventBus.getDefault().register(this);
}
protected abstract Fragment getInitialFragment();
protected abstract void onEvent(Object event);
public abstract boolean handleBackNavigation();
public void freeUpResources(){
EventBus.getDefault().unregister(this);
}
protected Fragment getOpenFragment(){
String tag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1).getName();
return fragmentManager.findFragmentByTag(tag);
}
protected boolean isFragmentOpen(Fragment fragment){
return isFragmentOpen(fragment, true);
}
protected boolean isFragmentOpen(Fragment fragment, boolean useArgs){
String fragmentTag = createFragmentTag(fragment, useArgs);
if (fragmentManager.getBackStackEntryCount() != 0) {
String name = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
if(!useArgs)
name = name.substring(0, name.indexOf("-"));
return name.equals(fragmentTag);
}
return false;
}
private String createFragmentTag(Fragment fragment, boolean addArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fragment.getClass().getSimpleName());
if(addArgs) {
stringBuilder.append("-");
if (fragment.getArguments() != null)
stringBuilder.append(fragment.getArguments().toString());
}
return stringBuilder.toString();
}
public void openFragment(Fragment fragment) {
if(isFragmentOpen(fragment))
return;
String fragmentTag = createFragmentTag(fragment, true);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.activity_main_fragment_container, fragment, fragmentTag);
transaction.addToBackStack(fragmentTag).commit();
}
}
Now you need to create your fragment organizer that inherit from FragmentOrganizer and implements 3 required methods. here the sample
public class MainFragmentOrganizer extends FragmentOrganizer {
public MainFragmentOrganizer(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
protected Fragment getInitialFragment() {
return HomeFragment.newInstance();
}
#Override
public void onEvent(Object event){
if(event instanceof ClickedOnPhotoEvent){
String photoCode = ((ClickedOnPhotoEvent) event).photoCode;
openFragment(PhotoFragment.newInstance(photoCode));
}
}
#Override
public boolean handleBackNavigation(){
Fragment fragment = getOpenFragment();
if (fragment instanceof HomeFragment){
return false;
} else {
fragmentManager.popBackStack();
return true;
}
}
}
And in your activity you just need to insatiate your FragmentManager and let it do the magic!
fragmentManager = getSupportFragmentManager();
fragmentOrganizer = new MainFragmentOrganizer(getSupportFragmentManager());
#Override
public void onBackPressed() {
//first let fragment organizer handle back. If it does not activity takes cares of it!
if(!fragmentOrganizer.handleBackNavigation()){
finish();
}
}
#Override
protected void onDestroy() {
fragmentOrganizer.freeUpResources();
super.onDestroy();
}
It may seem a lot of code but as you see most of the code encapsulated in FragmentOrganizer base class and it does all the general works so you just have to copy this file from one project to another.
As i said in the beginning i just came up with this solution right now, so it may not be perfect. I Plan to use this in my next project i hope you do to. And if you do i really appritiate if you share your though. have a good time
A co-worker of mine came up with what I consider an elegant solution to this problem.
Remember, what we're trying to achieve is a way for fragments to callback to the parent activity without having the activity implement the interface. Also, we need to be able to automatically set the listener again if the activity is destroyed and then recreated.
Activities have a lifecycle callback called onAttachFragment(Fragment fragment) which is called whenever a fragment is being attached to the activity. So, for instance, when a new fragment is created within the activity, this gets called. It also gets called if an activity that was previously destroyed gets recreated. What you can do is use an interface or an anonymous class to set a listener on the new fragment in onAttachFragment like this:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
//Determine which fragment this is by checking its tag
if(fragment.getTag().contains(TextFrag.FRAG_TAG)){
//set a listener on this fragment using an anonymous class or interface
((TextFrag)fragment).setListener(new TextFragButtonListener() {
#Override
public void onButtonClicked() {
count++;
counterTV.setText(String.valueOf(count));
}
});
}
}
Using this technique we are able to avoid the activity having to implement an interface for the callback and thus we avoid any naming conflicts with our callback methods. Also, if the activity is destroyed, once it is recreated the listener will be automatically reset so our callbacks will still work.
There are probably many other ways to do this and I'd love to here anyone's criticisms of this technique and suggestions for any other techniques.

Categories

Resources