I have an Activity A with a Fragment and an Activity B.
When I click a button inside the Fragment, the Activity B is starting and i assume that the Activity A is onPause.
What I want to do is to click on a button inside the Activity B which will finish() this Activity and then go back to the Activity A.
Doing so would automatically call a method inside the fragment. After research I tried to implements Interface and Fragment transactions but I always get null objects.
How can I make my activities communicating and pass the information to the fragment in this configuration?
Call ActivityB from FragmentA (which is part of ActivityA) as startActivityForResult() instead of startActivity() call.
Using this, you would be able to pass back result from Activity B to Fragment A.
Fragment A (Part of ActivityA) :
// Calling Activity B
Intent intent = new Intent(this, ActivityB.class);
intent.putExtras(b);
startActivityForResult(intent, ANY_ID);
// Overriding callback for result
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ANY_ID && resultCode == Activity.RESULT_OK) {
// Your logic of receiving data from Activity B
}
}
Important Point : The Fragment A is the one making the startActivityForResult() call, but it is part of Activity A so Activity A gets the first shot at handling the result. It has to call super.onActivityResult() so that the callback can come to Fragment A
In Activity A :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// This is required, in order to get onActivityResult callback in Fragment A
}
Setting result back from Activity B :
Intent resultIntent = new Intent();
// You can pass any data via this intent's bundle by putting as key-value pair
resultIntent.putExtra("Key", Value);
setResult(Activity.RESULT_OK, resultIntent);
finish();
Reference :
https://stackoverflow.com/a/22554156/1994950
https://stackoverflow.com/a/6147919/1994950
Start Activity for result
Related
My MainActivity(1) implements FragmentStatePagerAdapter, with SectionPagerAdapter and ViewPager. It works OK, apart from the problem I have to call getItemPosition to update one of the Fragments, which causes the whole thing to be recreated. Anyway...
One of the "tabs", calls a Fragment(2=BaixarOrcamentoFragment.java), which in turn, calls another Fragment(3=FillReasonToBaixaFragment.java), so the user can insert a text.
Fragment(2) implements THE LISTENER that Fragment(3) uses to return a "text value", so Fragment(2) can continue and finish it's tasks.
Here is the code in Fragment(2), that calls Fragment(3):
FragmentManager fragmentManager = getFragmentManager();
FillReasonToBaixaFragment fillFragment = new
FillReasonToBaixaFragment();
Bundle args = new Bundle();
args.putInt("ORCID", baixarModelList.get(masterPosition)
.getOrcGroupID());
fillFragment.setArguments(args);
fillFragment.show(fragmentManager, FILL_REASON_TO_BAIXA);
Then, Fragment(3) gets the bundle ORCID,stars the listener, get some data, shows a text input, and finishes by sending this "text" to the interface:
BaixaItemdoOrcamentoListener listener = (BaixaItemdoOrcamentoListener) this
.getContext();
..and then returning what has collected (text) using this interface (listener):
public interface BaixaItemdoOrcamentoListener
{
void OnFinishedFillReason(String mEditext);
}
However, it's not returning back to Fragment(2), which called Fragment(3), where I implemented the method to receive this returning value:
#Override
public void OnFinishedFillReason(String mEditext)
{}
It shows a cast error, saying that .MainActivity cannot be cast to .FillReasonToBaixaFragment$BaixaItemdoOrcamentoListener
I went on and DECLARED the OnFinishedFillReason inside the MainActivity, which implements FillReasonToBaixaFragment.BaixaItemdoOrcamentoListener.
Be aware now, that the actual implementation of the tasks are in Fragment(2).
Guess what: when I enter the text in Fragment(3) and press ( android:imeOptions="actionDone"), it returns to the MainActivity, NOT to the Fragment(2), the one that has called Fragment(3).
MainActivity doesn't know the existence of any of the the Views inside Fragment(2), a priori, which will, in turn, update all these views ONCE received the "text" from Fragment(3).
Perhaps I didn't search thoroughly, but I couldn't find anything that resembles this situation.
How can I make it happen?
Afternoon everyone.
I figured it out how to make this happen.
First: I removed OnFinishedFillReason interface from Fragment(2), and therefore, from Fragment(3).
In Fragment(2), I started the Fragment(3) like this, setting a setTargetFragment:
FragmentManager fragmentManager = getFragmentManager();
FillReasonToBaixaFragment fillFragment = new
FillReasonToBaixaFragment();
fillFragment.setTargetFragment(BaixaOrcamentoFragment.this,
FILL_REASON_TO_BAIXA);
Bundle args = new Bundle();
args.putInt("ORCID", baixarModelList.get(masterPosition)
.getOrcGroupID());
fillFragment.setArguments(args);
fillFragment.show(fragmentManager,
this.getClass().getSimpleName());
The target fragment is the Fragment(2) itself.
Then I added in Fragment(2):
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == FILL_REASON_TO_BAIXA && resultCode == Activity.RESULT_OK)
{// code in here }
In Fragment(3), I added in the onCreate(Bundle savedInstanceState) method, the following:
targetFragment = getTargetFragment();
if (targetFragment instanceof BaixaOrcamentoFragment)
{
orcID = getArguments().getInt("ORCID");
}
At the end of the returning elements, like this:
Intent returnData = new Intent();
Bundle bundle = new Bundle();
bundle.putString("EDITTEXT", txtEdited);
returnData.putExtras(bundle);
targetFragment.onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, returnData);
this.dismiss();
return true;
And then, inside the onActivityResult I collected the data, like this:
Bundle returnedValues = data.getExtras();
String mRazao = returnedValues.getString("EDITTEXT").trim();
And that was it.
But I still have a problem: I have 6 tabs. Tab 4 is never selected as currentItem, inside the method below:
#Override
public void onPageSelected(int position)
{
mViewPager.setCurrentItem(position);
}
I am using rxJava to fetch data from the database and show it in a recyclerview. The relevant code is shown below
function updateUI(){
ContactsLab contactsLab = ContactsLab.get(getActivity());
Subscription sub = contactsLab.getContactList().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toList()
.subscribe(onContactsReceived());
mCompositeSubscription.add(sub);
}
ContactsLab is a singleton that returns an Observable of Contact objects.
onContactsReceived function is shown below
private Observer<List<Contact>> onContactsReceived(){
return new Observer<List<Contact>>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(List<Contact> contacts) {
if(mContactsAdapter == null) {
mContactsAdapter = new ContactsAdapter(contacts);
mRecyclerView.setAdapter(mContactsAdapter);
} else{
mContactsAdapter.setContactList(contacts);
mContactsAdapter.notifyDataSetChanged();
}
}
};
}
The updateUI function is called in my fragment onResume but the view is updated only the first time. If I come back to this fragment from any other fragment (having added more items to db), onResume is called, updateUI runs and onContactsReceived also runs but returns immediately without calling onNext or onComplete.
I think this has something to do with the way rxJava handles observables but no idea how to fix it (read about defer but couldn't understand much). Can somebody please help?
Edit:
The getContactList function look like this :
public rx.Observable<Contact> getContactList() {
List<Contact> contacts = new ArrayList<>();
ContactCursorWrapper cursorWrapper = queryContacts(null, null);
try{
cursorWrapper.moveToFirst();
while (!cursorWrapper.isAfterLast()){
contacts.add(cursorWrapper.getContact());
cursorWrapper.moveToNext();
}
} finally {
cursorWrapper.close();
}
return rx.Observable.from(contacts);
}
Basically it queries the database and maps the returned Cursor into my Contact class(which is a POJO). I added the rx.Observable.from to get an observable that was later collated using toList and updated into the adapter.
I used this approach avoid having to call notifyDataSetChanged after getting each item (and call it only once after getting all that).
What's the right approach to minimize the number of notifyDataSetChanged calls and also, refresh each time onResume is called?
Your observable contactsLab.getContactList().toList() has terminated.toList() collects all emissions from a source observable to a list and emits the entire list once the source Observable terminates (see the documentation). You aren't going to observe any more emissions from it.
I used Google Places AutocompleteFragment for loading address from user. The selected address from the dropdown list working fine inside Activity. But when i use fragment instead of Activity, its not working.
According to Google Places API for Android, if your fragment is nested within another fragment, your app must also forward onActivityResult() calls from the containing fragment to work around a known limitation of android.support.v4.app.Fragment. This is shown in the following snippet:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
autocompleteFragment.onActivityResult(requestCode, resultCode, data);
}
There are dozens of this kind of threads, but i couldnt find and solution for my problem (at least working one).
So i have 3 fragments in my viewpager, my last (3rd) fragment is basically a friendlist, i also have a button with which i open a new fragment, where i handle search/request etc. In that fragment i have "back button", i get back to my "3rd" fragment in a viewpager with getFragmentManager().popBackStack(). How can i pass boolean value or something back to the "3rd" fragment?
I tried with manually calling onPause and onResume methods of the 3rd fragment, but then my list is empty. Also no methods of the 3rd fragment is called when i popbackstack.
This is my code
3rd fragment
This is how i open new fragment
ImageButton friendsButton = (ImageButton) v.findViewById(R.id.friendsButton);
friendsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SearchActivity configDetailSectionFragment = new SearchActivity();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.item_detail_container, configDetailSectionFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
and this is how i get back to the 3rd fragment
ImageButton backButton=(ImageButton)rootView.findViewById(R.id.backButton);
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().popBackStack();
} });
My methods for filling exlistview work fine, just no methods are called after popbackstack, so i could update my list.
What should i change and when and where should i call listadapter.notifyDataSetChanged()?
Using add fragment method with addBackStack, on poBackStack previous fragment's onResume will not get called. So if you are using addBackStack and popBackStack, and you want to call onResume of your previous fragment then you have to use replace instead of add. So your code just changes like -
transaction.replace(R.id.item_detail_container,configDetailSectionFragment);
once you did this you can use your onResume method to refresh list.
I have an activity which loads a data list from the server using loader callbacks. I have to list out the data into a fragment which extends
SherlockListFragment
i tried to commit the fragment using
Fragment newFragment = CategoryFragment.newInstance(mStackLevel,categoryList);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.simple_fragment, newFragment).commit();
in onLoadFinished and it gives an IllegalStateException saying
java.lang.IllegalStateException: Can not perform this action inside of onLoadFinished
I have referred the example in actionbar sherlock, but those examples have loaders within the fragments and not the activity.
Can anybody help me with this o that I can fix it without calling the loader from the fragment!
Atlast, I have found a solution to this problem. Create a handle setting an empty message and call that handler onLoadFinished(). The code is similar to this.
#Override
public void onLoadFinished(Loader<List<Station>> arg0, List<Station> arg1) {
// do other actions
handler.sendEmptyMessage(2);
}
In the handler,
private Handler handler = new Handler() { // handler for commiting fragment after data is loaded
#Override
public void handleMessage(Message msg) {
if(msg.what == 2) {
Log.d(TAG, "onload finished : handler called. setting the fragment.");
// commit the fragment
}
}
};
The number of fragments depend on the requirement.
This method can be mainly used in case of stackFragments, where all fragments have different related functions.
As per the Android docs on the onLoadFinished() method:
Note that normally an application is not allowed to commit fragment transactions while in this call, since it can happen after an activity's state is saved. See FragmentManager.openTransaction() for further discussion on this.
https://developer.android.com/reference/android/app/LoaderManager.LoaderCallbacks.html#onLoadFinished(android.content.Loader, D)
(Note: copy/paste that link into your browser... StackOverflow is not handling it well..)
So you simply should never load a fragment in that state. If you really don't want to put the Loader in the Fragment, then you need to initialize the fragment in your onCreate() method of the Activity, and then when onLoadFinished occurs, simply call a method on your fragment.
Some rough pseudo code follows:
public class DummyFragment {
public void setData(Object someObject) {
//do stuff
}
public class DummyActivity extends LoaderCallbacks<Object> {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment newFragment = DummyFragment.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.simple_fragment, newFragment).commit();
getSupportLoaderManager.initLoader(0, null, this)
}
// put your other LoaderCallbacks here... onCreateLoader() and onLoaderReset()
public void onLoadFinished(Loader<Object> loader, Object result) {
Fragment f = getSupportLoaderManager.findFragmentById(R.id.simple_fragment);
f.setData(result);
}
Obviously, you'd want to use the right object.. and the right loader, and probably define a useful setData() method to update your fragment. But hopefully this will point you in the right direction.
As #kwazi answered this is a bad user experience to call FragmentTransition.commit() from onLoadFinished(). I have found a solution for this event by using ProgressDialog.
First created ProgressDialog.setOnDismissListener(new listener) for watching the onLoadFinished().
Further i do progressDialog.show() before getLoaderManager().restartLoader().
And eventually place progressDialog.dismiss() in onLoadFinished().
Such approach allow do not bind main UI thread and Loader's thread.
public class FrPersonsListAnswer extends Fragment
implements
LoaderCallbacks<Cursor>{
private ProgressDialog progressDialog;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_persons_list, container, false);
//prepare progress Dialog
progressDialog = new ProgressDialog(curActivity);
progressDialog.setMessage("Wait...");
progressDialog.setIndeterminate(true);
progressDialog.setOnDismissListener(new OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
//make FragmentTransaction.commit() here;
//but it's recommended to pass control to your Activity
//via an Interface and manage fragments there.
}
});
lv = (ListView) view.findViewById(R.id.lv_out1);
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view,
final int position, long id) {
//START PROGRESS DIALOG HERE
progressDialog.show();
Cursor c = (Cursor) parent.getAdapter().getItem(position);
// create Loader
getLoaderManager().restartLoader(1, null, curFragment);
}
});
return view;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
switch (loader.getId()) {
case 1:
//dismiss dialog and call progressDialog.onDismiss() listener
progressDialog.dismiss();
break;
default:
break;
}
}