I wanna get notified when my Fragment reenters before a shared element transition happens, like I can do with the Activities onActivityReenter method. Is there a similar way I can do this for fragments?
While there's no onActivityReenter method in Fragment, what I've found myself doing is calling a public method on my Fragment from the Activity.
Define a public method in your Fragment class:
public void onReenter(Intent data) {
// Do whatever with the data here
}
Then just call it when onActivityReenter is called in your Activity:
#Override
public void onActivityReenter(int resultCode, Intent data) {
super.onActivityReenter(resultCode, data);
ItemFragment fragment = (ItemFragment) getSupportFragmentManager().findFragmentByTag(ITEM_TAG);
if (fragment != null) {
fragment.onReenter(data);
}
}
Related
I have a base Activity in which I have 2 fragments (lets say Fragment1 & Fragment2)
I need to call Fragment2 from Fragment1 for capturing data and then use the same in Fragment1 .
I know the that call of Activity for result can be fetched inside onActivityResult of baseActivity but how will I call the Fragment2 from Fragment1 for result purpose ?
Without MVVM
The simplest built in way i guess is using Fragment.setTargetFragment(#Nullable Fragment fragment, int requestCode)
from fragment 1:
val fragment = Fragment2.newInstane()
fragment.setTargetFragment(this, 10)
from fragment 2 when you wanna get result back tofragment 1:
fun setResultBack(){
val intent = Intent()
// inject anything to intent here
getTargetFragment().onActivityResult(10, Activity.RESULT_OK, intent)
fragmentManager.popBackStack()
}
finally override onActivityResult from fragment 1:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 10){
// handle result here
}
else super.onActivityResult(requestCode, resultCode, data)
}
And this works in all cases even after restoring fragment state or after configuration change.
with MVVM
Use a shared view model as recommanded from documentation
I think you will get the hint from this solution. Because I cant write code for your requirement, but this will give you an idea, what you can do.
public class Fragment1 extends android.support.v4.app.Fragment {
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof ActivityHome) {
((ActivityHome) context).setFragment1(this);
}
}
}
public class ActivityHome extends AppCompatActivity {
Fragment1 fragment1;
public void setFragment1(Fragment1 fragment1) {
this.fragment1 = fragment1;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (fragment1 != null) fragment1.onActivityResult(requestCode, resultCode, data);
}
}
Even you can implement this by using interfaces, but I'll suggest you EventBus as this is tiny library, which makes communication very easy.
(1) Make Data Class
public static class MessageEvent { /* Additional fields if needed */ }
(2) Subscribe for Event
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
(3) Register & Unregister
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
and Trigger from anywhere
EventBus.getDefault().post(new MessageEvent());
I'd recommend looking at this tight coupling and refactoring so the fragments aren't so tightly coupled and the data fetch is similarly decoupled. Have a look at the various resources on MVP, MVC, or MVVP for the concept.
Sometimes, though, you do need to have one fragment's info conveyed to another. The typical/recommended way of doing this is to define a "fragment callback" interface that your fragments require and that interface is implemented by your Activity. So the Activity is the medium for communication (which helps with decoupling the fragments.) You can use a custom base class for your fragments so they have a common definition of the interface.
public class MyBaseFragment extends Fragment {
public interface FragmentCallbacks {
// Use whatever arguments you may require here
void fetchData();
}
protected FragmentCallbacks mCallbacks;
#Override
void onAttach(Context context) {
try {
mCallbacks = (FragmentCallbacks)context;
} catch (ClassCastException e) {
throw new RuntimeException("Activity must implement FragmentCallbacks");
}
}
}
Now in Fragment1 you can call mCallbacks.fetchData() which the Activity can use to call into a public method you create in Fragment2. When it gets the data back (probably async), it can then provide it to Fragment1 via a public method you create.
I am trying to do the following:
Show dialog fragment from an Activity (not Fragment);
Retrieve result in the Activity.
The problem is that onActivityResult in the Activity is not being called.
Here is how I am showing a DialogFragment
DriverRatingDialogFragment dp = new DriverRatingDialogFragment();
// this solution works well in case of showing dialog from a fragment
// but I have to show it from the Activity
// dp.setTargetFragment(getSupportFragmentManager().findFragmentById(R.id.flContent), DriverRatingDialogFragment.REQUEST_CODE);
dp.show(getSupportFragmentManager(), DriverRatingDialogFragment.ARG_RATING);
And here is how I am trying to return result from DialogFragment:
Intent intent = new Intent();
// This solution works well in case of previously setting target Fragment,
// but I have to return result to Activity
// getTargetFragment().onActivityResult(
// getTargetRequestCode(), REQUEST_CODE, intent);
// and with this attempt App crashes
// this.onActivityResult(REQUEST_CODE, Activity.RESULT_OK, intent);
here is where I want to retrieve result in the Activity, but it doesn't get called:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DriverRatingDialogFragment.REQUEST_CODE) {
// todo
}
super.onActivityResult(requestCode, resultCode, data);
}
What's wrong?
I start the DialogFragment the same way you do.
CreatePostDialog createPostDialog = new CreatePostDialog();
createPostDialog.show(getSupportFragmentManager(), "create_post_dialog");
And I send back data using a public method in the activity rather than with intents.
((MainActivity)mContext).logout(Utils.getLocation(v));
The snippets above are copy pasted examples of my working code. Logout in this case is a public method in MainActivity that takes in a point and does what is needs with it.
you can use callback
1.define a callback in you dialog fragment
public Call mCall;
public interface Call {
void returnData(Data data);
}
2.on dialog fragment life circle
#Override
public void onDetach() {
super.onDetach();
mCall = (Call) getActivity();
mCall.returnData(data);
}
3.activity just implements Call
activity implements DiaFragment.Call
onActivityResult is there to receive informations after startActivityForResult is called, which is not your case.
What kind of datas you need to get from the dialog ?
DialogFragment is usually used in a fragment, not an activity. Look at alert dialog.
I have a problem with startActivityForResult, fragments and orientation changes.
I call startActivityForResult() from one fragment, then I open second activity with fragment attached. In this second activity when I change orientation and go back to the first activity then onActivityResult (fragment method) is called. But there's a problem, because a I have something like this:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
getActivity(); //here's a problem because
//getActivity is null but only after orientation change on second activity
}
You will need to call the required super before calling getActivity(), like so:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
getActivity();
}
This solved for me:
private Activity activity;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.activity = getActivity();
}
and use this activity anywhere in the fragment that you need.
All the problem was that I don't declare android:id for the fragment in XML. Android need's ID or TAG to recognize stored fragment and reproduce all element's in it. So guys, remeber - every instance of fragment need unique id or tag!
In my app I have two fragments say fragmentA and FragmentB. When I click on a button in fragmetA, a list is opened in fragmentB. Now when I select an item from list in fragmentB I want the result to be passed to fragmentA. I am using only one TabActivity for all fragments. When list item is selected in fragmentB I am popping out fragmentB from stack so that I can directly go back to fragmentA.
Does anyone knows how to pass result to previous fragment.
Thanks.
Update
Activity is the parent controller and should take responsibility for handling those events raised by its fragments/views, which concern something outside of the scope of fragment/view itself.
A Fragment is to act as a sub-controller of Views it hosts. All the events and communication between its own views, the fragment should handle itself. When there is an event outside of a fragment's scope and responsibilities (like sending data to another fragment), that event should be escalated to its parent controller, the Activity.
Old
From this tutorial : http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
Its better to let the activity apply changes to its fragment than passing values directly between fragments. Let your Activity implement a FragmentListener interface with onQuery(Bundle data) and onResult(Bundle data) methods.
Create a FragmentListener varaible in each of your fragments and then override onAttach() of each fragment as:
public void onAttach(Activity activity) {
super.onAttach(activity);
//---register parent activity for events---
try{
fragmentListener = (FragmentListener) activity;
}catch (ClassCastException e)
{
throw new ClassCastException("Parent activity must implement interface FragmentListener.");
}
}
This will enforce your child fragments to be automatically registered to parent Activity.
Also, remember to release fragmentListener reference in onDetach().
Now you can call your Activity from fragments.
On the other side, your Activity can always search for a fragment using getFragmentManager().findFragmentByTag("fragmentA") or findFragmentById("FragmentA").
If it can find your Fragment, Then it can cast it into your FragmentA class and call its methods. Same can be done with FragmentB or any other fragment..
One of the possible solutions:
public class DetachableResultReceiver extends ResultReceiver {
private Receiver mReceiver;
public DetachableResultReceiver(Handler handler) {
super(handler);
}
public void clearReceiver() {
mReceiver = null;
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
First fragment implements DetachableResultReceiver.Receiver and creates DetachableResultReceiver object:
mReceiver = new DetachableResultReceiver(new Handler());
mReceiver.setReceiver(this);
Then you can pass it to second fragment arguments:
Bundle bundle = new Bundle();
bundle.putParcelable(Consts.EXTRA_RECEIVER, receiver);
fragment.setArguments(bundle);
And use it in second fragment:
receiver = getArguments().getParcelable(Consts.EXTRA_RECEIVER);
receiver.send(Consts.SOME_MESSAGE, someData);
In fragmentB.java set an OnClickListener to perform a method in the main class. Pass an arguement in fragmentB.java to the main class that is the variable, and handle the rest of it in your main class. Though fragments shouldn't really be dependent on activities at all. Fragments were made to plug and play anywhere.
This Example Shows EditTextListener:
myAwesomeActivity.java
fragmentA.java
fragmentB.java
fragmentB.java:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
int x = 3;
EditText ed1 = (EditText) getView().findViewById(R.id.editText1);
ed1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEvent.ACTION_UP == event.getAction()) {
((myAwesomeActivity) getActivity()).myMethod(x);
}
return false;
}
});
}
myAwesomeActivity.java:
publiv void myMethod (int x){
//Do whatever you want with variable
}
All you have to do is implement the correct type of listener, but the main point is shown. In one fragment activity, call a method and pass a variable to the main activity. From the main activity you can send it to your other fragment activity if you'd like.
You can also use SharedPreferences to save some string and after return back to the first fragment load it and clear.
I'm using a TabActivity that calls an ActivityGroup. Tipical approach that brings problems.
Now I'm facing a problem with onActivityResult
in my Activity I have..
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.place_buttontab_community_media);
Button ButtonClick =(Button) findViewById(R.id.addPicture);
ButtonClick.setOnClickListener(new OnClickListener (){
#Override
public void onClick(View view)
{
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// request code
startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "hi");
super.onActivityResult(requestCode, resultCode, data);
}
}
unfortunately onActivityResult is never invoked.
You should listen for the result in the ActivityGroup's onActivityResult(). When a result is received, you have to decide which Activity should receive it and call it's onActivityResult() method with the received values.
I recently encountered this, to consolidate Andras' and virtual82's answer and discussion above (Sorry I can't add comment due to my low rep):
If your activity is started inside an ActivityGroup, which is used by TabActivity for example, then only the ActivityGroup activity can receive method calls to onActivityResult when you start an activity with startActivityForResult(Intent intent, int requestCode) from an activity inside the ActivityGroup.
In my situation, I have a TabActivity that contains FragmentActivitys. A Fragment inside a FragmentActivity may start an activity for result.
In this case the Fragment needs to start the activity with TabActivity as the calling activity:
Intent doSomethingIntent = new Intent();
//getActivity() returns the FragmentActivity, so we need to get the TabActivity with getParent()
Activity tabActivity = getActivity().getParent();
doSomethingIntent .setClass(tabActivity , PostCommentActivity.class);
tabActivity.startActivityForResult(doSomethingIntent , DO_SOMETHING_REQ_CODE);
Then in onActivityResult of the TabActivity, pass the result back to the Fragment.
Activity currentActivity = getLocalActivityManager().getCurrentActivity();
//get the FragmentActivity in order to get the Fragment instance
if(currentActivity instanceof FragmentActivity){
FragmentActivity currentFragmentActivity = (FragmentActivity)currentActivity ;
//R.id.l_container is the layout/viewgroup that contains the Fragment
Fragment currentFragment = currentFragmentActivity.getSupportFragmentManager().findFragmentById(R.id.l_container);
if(currentFragment != null){
//pass result to the Fragment!
frag.onActivityResult(requestCode, resultCode, data);
}
}
I realize that ActivityGroup, TabActivity and LocalActivityManager are deprecated in favor of Fragment and FragmentManager, but my project time constraint didn't allow me enough time to fully upgrade.
Hopefully this post will help someone in this situation, instead of the usual "don't do it" kind of response you see else where.