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();
}
Related
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?
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
I have an activity which hosts two fragments with only one shown at a time. Effectively the user, through different environmental conditions, should be able to toggle between the two at any given time.
There is a LoginFragment which is the first thing the user sees on login, and a LockoutFragment which may replace the LoginFragment after a user logs in and we see their account is locked (naturally).
That is the typical case, but there is a case in which LockoutFragment is presented first, if say, the user is using the app and their account is locked for some reason, and we re-open the host activity (LoginActivity), showing the LockoutFragment, but giving them a button to "Return to login", which toggles appearance of the LoginFragment (also naturally).
Thus, my goal is to allow a user to toggle between the two fragments, whichever is displayed first. My host activity uses the following functions to achieve this effect:
private void showLockoutFragment() {
if (mLockoutFragment == null) {
mLockoutFragment = new LockoutFragment();
}
transitionToFragment(FRAGMENT_LOCKOUT, mLockoutFragment);
}
private void showLoginFragment() {
if (mLoginFragment == null) {
mLoginFragment = new LoginFragment();
}
transitionToFragment(FRAGMENT_LOGIN, mLoginFragment);
}
private void transitionToFragment(String transactionTag, Fragment fragment) {
if (!getFragmentManager().popBackStackImmediate(transactionTag, 0)) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(
R.animator.fade_in, R.animator.fade_out,
R.animator.fade_in, R.animator.fade_out);
ft.addToBackStack(transactionTag);
ft.replace(R.id.fragment_container, fragment, transactionTag);
ft.commit();
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// non configuration change launch
if (savedInstanceState == null) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
// decide which fragment to show
boolean shouldLockout = extras.getBoolean(EXTRA_SHOULD_LOCKOUT);
if (shouldLockout) {
showLockoutFragment();
} else {
showLoginFragment();
}
} else {
showLoginFragment();
}
} else {
// retrieve any pre-existing fragments
mLoginFragment = (LoginFragment)getFragmentManager().findFragmentByTag(FRAGMENT_LOGIN);
mLockoutFragment = (LockoutFragment)getFragmentManager().findFragmentByTag(FRAGMENT_LOCKOUT);
}
}
These functions work together like a charm, with one exception: when, after initial launch of the app, a user
attempts log in,
is taken to the lockout fragment,
reorients the device, and
navigates back to the login fragment,
the login fragment is now present but invisible - as if the popEnter animation was never played. I know it is present because I can still interact with it.
It is also worth noting the following:
I have setRetainInstance(true) on both fragments
This only occurs when a user reorients the device from the lockout fragment
I have tried this on both a simulator and device running Lollipop with same results
Is it possible that the back stack is being corrupted after reorientation?
Thank you!
Ok, so it turns out the issue actually lies in my use of setRetainInstance. According to the docs for that method:
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. [emphasis mine]
While this appears rather cryptic to me, it seems that using setRetainInstance(true) on a fragment that is on the back stack could simply have unintended consequences. In my case, the fragment seemed to be retained, but its popEnter animation was never being called (post-rotation). Again, weird, but I guess just avoid that combination.
I am working on an app and it has code in which I have extended my class with FacebookActivity and I wanted to know what does OPENED_TOKEN_UPDATED mean and when will this portion execute
if (state.equals(SessionState.OPENED_TOKEN_UPDATED)) {
//WHEN THIS PORTION WILL EXECUTE
}
Facebook documents are not so good. See below code also
#Override
protected void onSessionStateChange(SessionState state, Exception exception) {
if (isResumed) { //if its a visible activity
FragmentManager manager = getSupportFragmentManager();
int backStackSize = manager.getBackStackEntryCount(); //get number of entries currently in the back-stack
for (int i = 0; i < backStackSize; i++) {
manager.popBackStack(); //clear fragment back-stack before new fragment is added
}
if (state.isOpened()) {
if (state.equals(SessionState.OPENED_TOKEN_UPDATED)) {
//WHEN THIS PORTION WILL EXECUTE
}else{
//replace fragment on main with promo fragment
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.body_frame, fragments[NextFgragment]).commit();
}
} else if (state.isClosed()) {
//replace fragment on main with login fragment
}
}
}
When you initially open a session, it will be in the SessionState.OPENED state. If you make a reauthorize request, or if the token gets refreshed, then it will be in an OPENED_TOKEN_UPDATED state.
Generally you can treat both OPENED and OPENED_TOKEN_UPDATED as the same, but sometimes (like if you request more permissions), you'll want to know when the token has been updated.
You can just disable this code
if (state.equals(SessionState.OPENED_TOKEN_UPDATED)) {
//WHEN THIS PORTION WILL EXECUTE
}
The documentation the SessionState.OPENED_TOKEN_UPDATED say:
"Indicates that the Session is opened and that the token has changed. In this state, the Session may be used with Request."
that portion of code is useful, if need do something when the token change
I have an Android app which I use to register users on my web site. My first task is to register a user if my shared preferences file shows there is no registered user information.
If my app has a registered user, I provide the following code to simply and automatically switch to a "homepage" activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signin);
if( USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, "") == null && USERPREFERENCES.getString(USERPREFERENCES_USERID, "") == null && USERPREFERENCES.getString(USERPREFERENCES_USERNAME, "") == null){
//setContentView(R.layout.signin);
Toast.makeText(SignIn.this, "testing...", Toast.LENGTH_LONG).show();
}else{
Intent intent = new Intent(SignIn.this, Confirmed.class);
startActivity(intent);
}
... other code
So, from my default activity, signin.java, the app will either switch to the Confirmed activity or stay on and display the signin activity.
My problem is, when the system works and I get switched to the the Confirmed activity, I provide a logout onclick listener which is below:
signout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//USERPREFERENCES.cl
Toast.makeText(Confirmed.this, "signout responding!", Toast.LENGTH_LONG).show();
USERPREFERENCES.edit().clear().commit();
}
});
It responds and clears all my shared preferences variables. But, when I use my menu to manually switch to the sign-in activity, I still get switched back to the Confirmed activity.
This happens even though I can confirm the variables are empty.
This hardly ever will be true:
USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, "") == null
What if you use this instead?
if( USERPREFERENCES.getString(USERPREFERENCES_USERMAIL, null) == null && USERPREFERENCES.getString(USERPREFERENCES_USERID, null) == null && USERPREFERENCES.getString(USERPREFERENCES_USERNAME, null) == null){
//setContentView(R.layout.signin); TRY TO AVOID DOING THIS THING!!!!!
Toast.makeText(SignIn.this, "testing...", Toast.LENGTH_LONG).show();
}else...
Also, as a recommendation... instead of being switching between activities... what if you create just a Signing.java activity and put a ViewFlipper in its layout. That way your app will be not only faster but also easier to maintain.
This is Because When you will switch back to LoginActivity, this will be resumed instead of being created , Means your Login code which you written inOnCreate will not be called because Dthis time Overrider OnResume has been called , not onCreate .
So either write this code again in onResume or call finish() before moving to second activity , so that next time it will call onCreate()
If you navigate back to the first activity, the onCreate is not called again (unless the activity was destroyed for lack of resources). Move the authentication code in onResume.