Determine which fragment is showing to update - java

I am updating the view that is currently showing and I Hide/Show two fragments.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if (mTaxLocationListViewFrag.isVisible()) {
if (mGoogleMapFrag == null) {
mGoogleMapFrag = new GoogleMapFragment();
fragmentTransaction.add(R.id.fragment_container, mGoogleMapFrag);
}
fragmentTransaction.hide(mListViewFrag);
fragmentTransaction.show(mGoogleMapFrag).commit();
} else {fragmentTransaction.hide(mGoogleMapFrag).show(mListViewFrag).commit();
}
updateDisplay();
updateDisplay method-
if (mListViewFrag != null && mListViewFrag.isVisible()) {
Log.d(TAG, "Update Location ListView");
mListViewFrag.setLocations(mLocations);
}
if (mGoogleMapFrag != null && mGoogleMapFrag.isVisible()) {
Log.d(TAG, "Update Google Maps Markers");
//Convert locations to markers to display.
}
The problem is I am guessing fragmentTransaction is asynchronous when performing .show and .hide and updateDisplay does not work properly as the it thinks the fragment to be hidden is still showing. Is there a more reliable way to check for which fragment to update?

Add fragmentTransaction.executePendingTransactions() after adding the fragment. From the docs of executePendingTransactions():
It is scheduled to be executed asynchronously on the process's main thread.
If you want to immediately executing any such pending operations, you
can call this function (only from the main thread) to do so. Note that
all callbacks and other related behavior will be done from within this
call, so be careful about where this is called from.

Related

Removing/replacing fragments

I'm starting to work with fragments and ran into a problem. I have file viewing fragments(more than one) and a search fragment. To make things easier I add my file viewing fragment to backstack whenever I go to search so I could easily pop it back if I decide to just close it. Now the problem is that when I press on file in the search fragment so that I travel to the specific fragment in which that file is, the onCreateView of the first fragment(which was in backstack) always gets called even though I replaced it(?) and I don't want that. Here are things I do to change the fragments on search file press:
1.I call pop to get to previous fragment
#Override
public boolean onSearchViewClose() {
if (getActivity() != null) {
getActivity().getSupportFragmentManager().popBackStack();
return true;
}
return false;
}
2. Then I replace the remaining fragment
public void setFragment(Fragment fragment, String tag, boolean clearBackStack, boolean addToBackStack) {
// Insert the fragment by replacing any existing fragment
hideLoadingProgress();
FragmentManager fragmentManager = getSupportFragmentManager();
if (clearBackStack) {
clearBackStack(fragmentManager);
}
FragmentTransaction transaction = fragmentManager.beginTransaction().replace(R.id.content_frame, fragment, tag);
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
while clearing backstack:
public void clearBackStack(FragmentManager fragmentManager) {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); ++i) {
fragmentManager.popBackStack();
}
}
Even if I don't clear backstack the problem persists.Thanks for your answers in advance.
The answer was simple - I should've just used getActivity().getSupportFragmentManager().popBackStackImmediate(); when exiting search view as the earlier method didn't work because it waited for program to return to its event loop (asynchronous).

How to get the current fragment when using FragmentStatePagerAdapter

It is surprisingly difficult to get the current fragment when using either of the pager adapters. With the FragmentPagerAdapter, however, you can look for a fragment with the tag "android:switcher:" + viewId + ":" + id.
Unfortunately, there does not seem to be a standard tag for the FragmentStatePagerAdapter. A related question provided a couple answers which suggested manually keeping a cache of the fragments, which were noted as being inadequate when doing a rotation: the underlying adapter stores state in a bundle and restores it when it is created, causing any simple caching solution to fail.
I found a better solution. getCurrentFragment() cannot be implemented correctly from what I can tell.
My code was previously launching a dialog and then calling back to the Activity which was stored by the dialog at onAttach. The Activity then needed to find the correct Fragment, which was problematic.
The correct solution is to first call setTargetFragment() on the new dialog fragment:
SelectProblemDialogFragment f = SelectProblemDialogFragment.newInstance(args);
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), "select_problem_dialog_fragment");
and then in onAttach(), simply use that as the listener.
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
mListener = (SelectProblemDialogListener) getTargetFragment();
if (mListener == null)
{
mListener = (SelectProblemDialogListener) activity;
}
} catch (ClassCastException e)
{
throw new ClassCastException("Must implement SelectProblemDialogListener");
}
}

strange NullPointerException inside Activity

In my Android app, I have found an error that happens in about 0.5% of attempts. This error is generate from an NullPointerException but this is strange.
Below is summarized the code the generates that exception:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
mInfo = intent.getParcelableExtra(EXTRA_MESSAGE_INFO); // mInfo is a private member of Activity
if(mInfo == null)
finish();
setContentView(R.layout.activity_fermata_details);
first = savedInstanceState.getBoolean(IS_FIRST_LOAD, true); // also onSaveInstanceState is used for maintain that value across configuration changes.
if(first){
first = false;
// a new fragment is inflated in the FrameLayout of the Activity
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
DetailsFragment fragment = new DetailsFragment();
transaction.replace(R.id.content_fragment, fragment);
transaction.commit();
}
}
The Activity also has the following public method:
public int getIdInfo(){
return mInfo.getId(); // here occurs the NullPointerException.
}
The method getIdInfo() is called from the Fragment in this way:
(MyActivity)getActivity()).getIdInfo();
I think that is a strange behavious, since the NullPointerException tells that mInfo should be null. But the condition in the onCreate:
if(mInfo == null)
finish();
should ensure that if the mInfo is null the Activity is finished.
Where can be the error? Can be related to Garbage Collector behaviour?
NOTE: I'm not able to get much more infomation about the error (the error was detected 3 times over 600 attempts), since I have retrieved this exception form Google Analytics, that gives my just this row:
NullPointerException (#MyActivity:getIdInfo:122) {main}
I think you are getting not-fully created Activity: one that has detected null and called finish(). Please consider the fact that finish() does not cause the onCreate() method to stop.
I suggest:
mInfo = intent.getParcelableExtra(EXTRA_MESSAGE_INFO);
if(mInfo == null) {
finish();
return;
}
One could suspect that FragmentManager will not try to attach any fragments to activity that called finish() on itself but I'm not able to find any documentation of that behavior.

Refreshing a view inside a fragment

I have searched the numerous questions that look like this one, but haven't found my answer in any of them.
I have an activity that has 3 tabs accessible through the action bar. I achieved this by adding 3 fragments that inflate a custom view I made extending the view class.
At the moment the database changes, I try to refresh the view in my tab by calling invalidate()/postinvalidate(), but this does not work. The same is true for calling onCreateView of the fragment just as many other options I considered.
When I go to another tab and go back, however, the change has been made and my view is updated as it should be.
How can I simulate the same thing that happens when changing to another tab? What does happen. I tried to look at the Fragment lifecycle (tried to call onCreateView()) to figure it out but it just doesn't want to refresh/redraw as it should.
The data is loaded properly, as the data is changed when I change to another tab.
I deleted some of the code as it is no longer relevant. I implemented Cursorloaders instead of my own Observer pattern to notify a change. This is my main activity right now.
The question is what should I do now if I want to redraw the view inside these fragments. If I apply fragmentObject.getView().invalidate() it does not work. I'm having the same problem as before, but now my Observer to notify a change in the database is properly implemented with loaders.
public class ArchitectureActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ActionBar actionbar = getActionBar();
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab EditTab = actionbar.newTab().setText("Edit");
ActionBar.Tab VisualizeTab = actionbar.newTab().setText("Visualize");
ActionBar.Tab AnalyseTab = actionbar.newTab().setText("Analyse");
Fragment editFragment = new EditFragment();
Fragment visualizeFragment = new VisualizeFragment();
Fragment analyseFragment = new AnalyseFragment();
EditTab.setTabListener(new MyTabsListener(editFragment));
VisualizeTab.setTabListener(new MyTabsListener(visualizeFragment));
AnalyseTab.setTabListener(new MyTabsListener(analyseFragment));
actionbar.addTab(EditTab);
actionbar.addTab(VisualizeTab);
actionbar.addTab(AnalyseTab);
ArchitectureApplication architectureApplication = (ArchitectureApplication)getApplicationContext();
architectureApplication.initialize();
getLoaderManager().initLoader(0, null, this);
getLoaderManager().initLoader(1, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id == 0){
return new CursorLoader(this, GraphProvider.NODE_URI , null, null, null, null);
} else if (id == 1){
return new CursorLoader(this, GraphProvider.ARC_URI , null, null, null, null);
}
return null;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Reloading of data, actually happens because when switching to another tab the new data shows up fine
Log.e("Data", "loaded");
}
public void onLoaderReset(Loader<Cursor> loader) {
}
}
Don't try to call onCreateView() yourself... it's a lifecycle method and should be called only by the framework.
Fragments are re-usable UI components. They have their own lifecycle, display their own view, and define their own behavior. You usually don't need to have your Activity mess around with the internal workings of a Fragment, as the Fragment's behavior should be self-contained and independent of any particular Activity.
That said, I think the best solution is to have each of your Fragments implement the LoaderManager.LoaderCallbacks<D> interface. Each Fragment will initialize a Loader (i.e. a CursorLoader if you are using a ContentProvider backed by an SQLite database), and that Loader will be in charge of (1) loading the data on a background thread, and (2) listening for content changes that are made to the data source, and delivering new data to onLoadFinished() whenever a content change occurs.
This solution is better than your current solution because it is entirely event-driven. You only need to refresh the view when data is delivered to onLoadFinished() (as opposed to having to manually check to see if the data source has been changed each time you click on a new tab).
If you are lazy and just want a quick solution, you might be able to get away with refreshing the view in your Fragment's onResume() method too.
I had a similar (although not identical) problem that I could solve in the following way:
In the fragment I added
public void invalidate() {
myView.post(new Runnable() {
#Override
public void run() {
myView.invalidate();
}
});
}
and I called this method from the activity when I wanted to refresh the view myView of the fragment. The use of post() ensures that the view is only invalidated when it is ready to be drawn.
I've found a workaround to refresh the view inside my fragment. I recreated (new CustomView) the view every time the database has been updated. After that I call setContentView(CustomView view). It looks more like a hack, but it works and nothing else that I tried does.
Although my problem was not actually solved, I gave the reward to Alex Lockwood. His advice on Loaders made my application better and it caused me to keep looking for a solution that I eventually found.
I had the same issue.
My solution was detach fragment and attach it again.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment f = getFragment(action);
if(forceUpdate)
{
fragmentTransaction.detach(f);
fragmentTransaction.attach(f);
}
fragmentTransaction.replace(R.id.mainFragment, f);
fragmentTransaction.commit();
currentAction = action;
The fastest solution working for me:
#Override
public void onPause() {
super.onPause();
if (isRemoving() && fragmentView != null) {
((ViewGroup) fragmentView).removeAllViews();
}
}

Fragment onResume doesn't get called after Fragment is being detached and then re-attached

I'm trying to get the handle on all the new ActionBar and Fragments API.
I have an main activity, and I want it to manage two different tabs.
I'm using the ActionBarSherlock in order to support older version than ICS.
Each tab contains its own Fragment (each one is a subclass of SherlockListFragment)
I got it to work basically nice, but I have a problem that I'm sure that is stupid, but I can't figure it out yet:
On the first time each Fragment is shown, everything is OK, the list is populated and so the MenuItems in the ActionBar.
But the second time you see a tab (After swicth and switch-back), Neither the list get populated, nor the ActionBar MenuItems.
This is how I'm switching the tabs:
#Override
public void onTabSelected(Tab tab, FragmentTransaction transaction) {
SherlockListFragment toAttach = // Find the right fragment here...
if (toAttach != null) {
if (toAttach.isAdded() == false) {
transaction.add(R.id.tab_placeholder, toAttach,
REMINDER_FRAGMENT_TAG);
} else {
transaction.attach(toAttach);
}
}
}
And onTabUneselect I'm detaching the Fragment:
#Override
public void onTabUnselected(Tab tab, FragmentTransaction transaction) {
SherlockListFragment toDetach = // Find the right fragment
if (toDetach != null) {
transaction.detach(toDetach);
}
}
I'm populating the lists and the ActionBar menu in onResume:
#Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
fillRemindersList();
}
I also tried it in onStart and onCreateView but it didn't help...
So what am I missing here?
And if there are others issues in my code that I'm unaware of, please do tell.
Thanks!
EDIT:
I just confirmed that onResume dosen't get called after I switch tabs, which is definetly wrong since I'm detaching and re-attaching them...
Am I switching tabs the wrong way?
Try using transaction.remove(fragment) in onTabUnselected and transaction.replace in onTabSelected.
Doing the beginTransaction() and commit() outside of this code I assume or did you forget?
You can see a trick used here from the samples as well:
https://github.com/JakeWharton/ActionBarSherlock/blob/master/samples/fragments/src/com/actionbarsherlock/sample/fragments/FragmentTabs.java

Categories

Resources