Fragments android orientation loss of fragment - android

I'm having a struggle with fragment loss on orientation change.
At first my fragments seem to be responding well to orientation changes and nothing is wrong.
But the exception is when I navigate back to a previously made fragment. Since I'm checking that in my fragmenthandler activity.
I chose to use a big if statement instead of switch cases, since that was more within the grasp of my knowledge. The handling of the if statements works correctly, have been debugging it with log statements and nothing seems to be wrong there. Below is the code for the handling of fragments, pre-used or create a new one.
public void fragmentHandle(Fragment fl, Fragment fr, String tl, String tr) {
Fragment cFragLeft = manager.findFragmentById(R.id.fragment_left);
Fragment cFragRight = manager.findFragmentById(R.id.fragment_right);
Fragment checkLeft = manager.findFragmentByTag(tl);
Fragment checkRight = manager.findFragmentByTag(tr);
FragmentTransaction ft = manager.beginTransaction();
if (cFragLeft != null && cFragRight != null && checkLeft != null
&& checkRight != null) {
ft.detach(cFragRight).detach(cFragLeft).attach(fr).attach(fl)
.addToBackStack(null).commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight == null) {
ft.detach(cFragLeft).detach(cFragRight)
.add(R.id.fragment_left, fl, tl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fr)
.add(R.id.fragment_left, fl, tl).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null
&& checkRight == null && checkLeft != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else {
ft.add(R.id.fragment_left, fl, tl).add(R.id.fragment_right, fr, tr)
.commit();
}
}
When I change orientation, and then navigating back to a previously saved fragment (first if statement) both of my left and right fragments show blank screens. This happens only when case 1 is called, it doesn't seem to remember the fragments that were there, even though the fragmentmanager found them by tag.
I've also been checking my onCreate, and it's shown below.
protected void onCreate(Bundle savedInstanceState) {
// super.onCreate
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_handler);
if (savedInstanceState == null) {
fragmentHandle(nFragLeft, nFragRight, "menuMain", "main");
}
}
Also including a button statement just for completion's sake. Haven't been using onClickListeners and not really sure whether I should prefer this over xml onClick or not.
public void sendDestination(View v) {
nFragLeft = menudestFrag;
nFragRight = destFrag;
String tagLeft = "menuDestination";
String tagRight = "destination";
fragmentHandle(nFragLeft, nFragRight, tagLeft, tagRight);
}
Is there any way to work around this? Only happens after changing orientation.

This happens because your activity is reacreated when orientation is changed.
you can solve this problem adding android:configChanges="orientation|screenSize" in manifest(prevent to recreate activity when orientation was changed):
<activity
android:name="com.example.MyActivity"
android:configChanges="orientation|screenSize" >
</activity>

Related

Why am I getting IllegalStateException: FragmentManager is already executing transactions

There are a several questions addressing that error but none of them have had a fix that worked for this situation.
Whats happening is that we are popping a fragment (which is nested inside a featureFragment) and making a call back to the activity to remove the feature fragment and un-hide a different fragment containing a googleMap.
DetailFragment:
if (mConversationId != null) {
try {
return getLayerClient().getConversation(mConversationId);
} catch (LayerException exc) {
getFragmentManager().popBackStack();
((MainActivity) getActivity()).LayerConnectionLost();
return null;
}
Main Activity:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Only hide the map fragment in order to keep the Google Map in memory as long as possible to avoid being charged
if (fragment != mMapFragment && !mMapFragment.isHidden()) {
transaction.hide(mMapFragment);
}
// Detach the other fragment views
if (fragment != mToolsFragment && !mToolsFragment.isDetached()) {
transaction.detach(mToolsFragment);
}
if (mMessageFragment != null ) {
if (fragment != mMessageFragment && !mMessageFragment.isDetached()) {
transaction.detach(mMessageFragment);
}
}
mVisibleFragment = fragment;
if (fragment == mMapFragment) { // map fragment is hidden, so show
transaction.show(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
} else { // other fragments are detached, so attach
transaction.attach(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
}
getSupportFragmentManager().executePendingTransactions();
The Stack Trace is giving the error on the last line
getSupportFragmentManager().executePendingTransactions();
But in testing I found that it is actually this line of code which is precipitating the error.
transaction.show(fragment).setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out).commit();
Any ideas what I'm dong wrong?

Async Task Updating UI

I am currently getting a java.lang.IllegalStateException: Fragment HomeFragment{b862cf1} not attached to Activity error when the following async method updates my home page.
new AsyncTask<HomeCardHolder, Void, List<HomeCardModel>>() {
HomeCardHolder mHomeCardHolder;
#Override
protected List<HomeCardModel> doInBackground(HomeCardHolder... params) {
if (params != null && params.length > 0) {
mHomeCardHolder = params[0];
}
return getPunchCardDeals();
}
#Override
protected void onPostExecute(List<HomeCardModel> punchCard) {
if (isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null) {
mCardAdapter.refreshDataForCards(mHomeCardHolder.getSectionName(), punchCard);
}
}
}.execute(homeCardHolder);
The thing is getPunchCardDeals() retrieves the list of deals but also updates UI elements. I've read that UI elements cannot be updated on a background thread, so what is the proper structure here?
Thanks,
Otterman
edit:
I am checking if the fragment is added.
boolean isActivityAlive() {
return null != getActivity() && isAdded() && !getActivity().isFinishing();
}
You should ensure fragment is added with the activity when you are updating view. For this you should use fragment.isAdded().
So replacing
if (isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null)
with
if (isAdded()&&isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null)
may help you.

Android - Saving RecyclerView content works in one Fragment but not the other

So I've used the same method in Fragment to other Fragments but only the first Fragment which I got it working saved its state and items inside the Adapter.
Here's the code in onViewCreated checking whether the Adapter is null and if not, use the same old adapter :
if(getArguments() != null){
Parcelable savedRecyclerLayoutState = getArguments().getParcelable(utilities.BUNDLE_RECYCLER_LAYOUT);
if (getArguments().getString("finishPost") != null && getArguments().getString("finishPost").equals("true")){
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
} else {
if(savedRecyclerLayoutState == null){
lobiAdapter = new LobiAdapter(this.getActivity());
lobiAdapter.setDisplay(width, height);
recyclerView.setAdapter(lobiAdapter);
refreshView();
} else {
if (lobiAdapter != null && lobiAdapter.getItemCount() > 0) {
manager.onRestoreInstanceState(savedRecyclerLayoutState);
if(lobiAdapter == null){
lobiAdapter = new LobiAdapter(this.getActivity());
lobiAdapter.setDisplay(width, height);
}
recyclerView.setAdapter(lobiAdapter);
lobiProgressBar.setVisibility(View.GONE);
} else /*if (lobiAdapter != null && lobiAdapter.getItemCount() == 0)*/{
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
}
}
}
} else {
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
}
And here's my onPause() method in the same Fragment which saves RecyclerView's state :
#Override
public void onPause() {
super.onPause();
getArguments().putParcelable(utilities.BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() > 1){
((NewsfeedActivity)getActivity()).hideToolbarBottom();
}
}
Weird thing is, it saves the adapter's values, but I don't know when or how. When I debug it, lobiAdapter shows that it has items in it, whereas, other Fragments which uses same method of saving state has no item.
Am I actually doing it wrong or I am unaware of something which I've done to save the state and items?

Load fragment on InfoWindowClick

So I'm trying to do the following in android xamarin.
When you press on a map element an infowindow is shown.When you press that window and an event is linked to the element the app goes to another fragment that is described by the action object. The problem is the moment I press the infowindow the whole app freezes and nothing happens. In the logs I can't see anything and the app stops at the following line:
pager.Adapter = pagerAdapter;
Added a breakpoint there and after saying "step over" the ide doesn't break anymore and the app freezes (no user interaction possible).
So let me start by giving all the relative code and a little explanation.
So first I'll show you what happens on the infowindow click. This happens on a SupportMapFragment that has it's own listener.
void GoogleMap.IOnInfoWindowClickListener.OnInfoWindowClick (Marker p0)
{
InfoPopup ip = CustomJsonConverter.Convert<InfoPopup> (p0.Title);
if (ip == null || ip.Goto == null || !(this.Activity is MainView))
return;
MainView main = (this.Activity as MainView);
p0.HideInfoWindow ();
switch (ip.Goto.type) {
case "InfoFragment":
Info info = InfoController.Items.Find (x => x.Index == ip.Goto.id);
if (info != null) {
main.RunOnUiThread (() => {
main.ShowInfosFragment ();
main.ShowInfoFragment (info);
});
}
break;
case "FaqFragment":
FaQ faq = FaQController.Items.Find (x => x.Index == ip.Goto.id);
if (faq != null) {
main.RunOnUiThread (() => {
main.ShowFaqsFragment ();
main.ShowFaqFragment (faq);
});
}
break;
}
}
I tested it with an action of InfoFragment which gives back an item so that's good. Then it goes to the main.ShowInfosFragment() which is where it freezes. So that method is really simple. This function is in the Activity holding the fragments (thanks Cheesebaron).
public void ShowInfosFragment(){
ShowFragment(1, new InfosFragment (){ InfoSelectedAction = ShowInfoFragment });
}
So the problem giving function is the following. This function is in the Activity holding the fragments (thanks Cheesebaron).
protected void ShowFragment(int index, Android.Support.V4.App.Fragment fragment, bool ignoreType = false){
RemoveSubMenu();
if (pagerAdapter.Count <= index || ignoreType || !(pagerAdapter.GetItem (index).GetType().Equals(fragment.GetType()))) {
pagerAdapter.AddFragment (index, fragment);
SetSubMenu (fragment);
pager.Adapter = pagerAdapter;
}
pager.SetCurrentItem (index, true);
DrawerButtonLayout ();
}
I've used this function a lot and it has always worked to move to the other fragments from the menu or at startup to set the mapfragment.
Anyone sees what's the problem with my code? Tried tons of things but can't seem to figure this one out with my friend google.
Thanks already for reading.
Kind regards,
Karl

FragmentTransaction crashes after checking if (Fragment != null)

I was following this tutorial on youtube and ran into a problem with the end result.
The goal of the tutorial was to introduce Fragment Transactions by having these buttons that add/remove/replace fragments in a layout below the buttons. It all went smoothly until the end.
If I hit the "Add A" button, it adds the Frag_A in the layout below, if you hit remove, it disappears. However, if you hit "Add A" 2+ times, then you need to hit remove 2+ times in order to get rid of all of them. Likewise, if you hit "Add A" and then "Add B", you need to hit "Remove A" first before you can see fragment B.
At the end of the video, they included an if statement in the "Remove" methods to check whether or not the Fragment was already there.
public void RemoveA (View v) {
FragmentA FA = (FragmentA) manager.findFragmentByTag("A");
FragmentTransaction transaction = manager.beginTransaction();
if (FA != null) {
//remove the transaction and commit
}
else {
//Toast a message to say the FragmentA doesn't exist yet
}
}
So I thought I could add a similar thing into the "AddA" method to check if a FragmentA exists, then toast a message to say it's already there, and if it doesn't exist, to add it to avoid having so many of them get created when you only need 1.
public void AddA (View v) {
FragmentA FA = (FragmentA) manager.findFragmentByTag("A");
FragmentTransaction transaction = manager.beginTransaction();
if (FA != null) {
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show());
}
else {
transaction.add(FA);
transaction.commit();
}
}
However, this made it so when you hit "AddA", the program just gives an unexpected error and quits. Looking at the Logcat (I just started learning last week so I don't know what everything means), I noticed a line mentioning a nullpointerexception at:
transaction.add(FA);
Meanwhile, IntelliJ is saying that:
if (FA != null)
Is always true, and FA == is always false. I also tried appending && isvisible but that didn't make a difference either. Is there any ideas as to why this would happen?
This is my first post and I couldn't find an answer for this on google/searching.
Looking at your code:
if (FA != null) { // here FA is not null
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show()); }
else { // here, FA is null!
transaction.add(FA); // same as transaction.add(null);
transaction.commit();
}
In the else block, you're missing some code where you would create a new fragment in FA, before adding it to the transaction.
if (FA != null) {
Toast.maketext(this, "Fragment already exists", Toast.LENGTH_SHORT).show()); }
else {
// Create a new fragment here and set it to FA
transaction.add(FA);
transaction.commit();
}

Categories

Resources