NullPointerException when popBackStackImmediate() - android

I have problem with fragment's backStack. Firstly, my structure is: I have activity with fragmentManager, new fragments added with method add() and with adding to back stack. So, when I have at list 2 fragments in backstack and when I have orientation changing, activity is recreated, and fragment restored, but when I call onBackPressed() and call popBackStackImmediate() I get an exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.Fragment.setNextAnim(int)' on a null object reference.
It seem's, that we have null fragment in back stack, but I've checked it, this fragments weren't null. So, can you help solve this problem?
I add fragments like this:
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if (previousFragment != null) {
fragmentTransaction.hide(previousFragment);
}
fragmentTransaction.add(containerViewId, fragment, fragmentTag)
.addToBackStack(fragmentTag)
.commit();
In onBackPressed() I have same behaivor:
if (fragmentManager.getBackStackEntryCount() == 1) {
finish();
} else {
super.onBackPressed();
}
Full stackTrace is:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.Fragment.setNextAnim(int)' on a null object reference
at android.support.v4.app.BackStackRecord.executePopOps(BackStackRecord.java:826)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2587)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2377)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2332)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:851)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:794)
at net.lampa.tablet.nightexpress.view.activities.OrderFinalActivity.onBackPressed(OrderFinalActivity.java:147)
at net.lampa.tablet.nightexpress.view.activities.OrderFinalActivity.onOptionsItemSelected(OrderFinalActivity.java:183)
at android.app.Activity.onMenuItemSelected(Activity.java:2914)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:380)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:195)
at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:108)
at android.support.v7.widget.ToolbarWidgetWrapper$1.onClick(ToolbarWidgetWrapper.java:187)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21153)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
line 164 is:
super.onBackPressed()
Full code of base activity with adding logic:
public abstract class BaseActivity extends AppCompatActivity {
private Unbinder uninder;
protected BaseFragment currentFragment;
protected BaseFragment previousFragment;
protected FragmentManager fragmentManager;
private Map<Integer, ArrayList<String>> filterTagsMap = new TreeMap<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Class cls = getClass();
if (!cls.isAnnotationPresent(Layout.class)) {
return; // Layout annotation is required
}
Annotation annotation = cls.getAnnotation(Layout.class);
setContentView(((Layout) annotation).id());
uninder = ButterKnife.bind(this);
if(fragmentManager == null) {
fragmentManager = getSupportFragmentManager();
}
currentFragment = getDefaultFragment();
inject(); // inject dependencies
setupDefaultValues();
}
public ApplicationComponent getApplicationComponent() {
return ((NEApplication) getApplication()).getApplicationComponent();
}
public abstract void setupDefaultValues();
public void closeCurrentFragmentInBackStack() {
fragmentManager.popBackStackImmediate();
}
public void showFragment(int containerViewId, BaseFragment fragment, boolean needToRefreshBackStack) {
if (needToRefreshBackStack && fragmentManager.getBackStackEntryCount() > 0) {
int indexToDelete = (fragment.getClass().getSimpleName()
.equals(fragmentManager.getBackStackEntryAt(0).getName())) ? 0 : 1;
int backStackFragmentsCount = fragmentManager.getBackStackEntryCount();
for (int i = backStackFragmentsCount - 1; i >= indexToDelete; i--) {
int backStackId = fragmentManager.getBackStackEntryAt(i).getId();
fragmentManager.popBackStack(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
previousFragment = currentFragment;
currentFragment = fragment;
String fragmentTag = fragment.getClass().getSimpleName();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if (previousFragment != null) {
fragmentTransaction.hide(previousFragment);
}
fragmentTransaction.add(containerViewId, fragment, fragmentTag)
.addToBackStack(fragmentTag)
.commit();
}
protected abstract BaseFragment getDefaultFragment();
#Override
protected void onDestroy() {
uninder.unbind();
uninder = null;
super.onDestroy();
}
}

The condition of popping the fragment itself is written logically in reverse. super.onBackPressed() will pop the fragment if it is present in the backstack.So if the getBackStackEntryCount() is greater than zero you need to call the super method or else finish the activity when there is no fragments in backstack.
if (fragmentManager.getBackStackEntryCount() >=0) {
getSupportFragmentManager().popBackStackImmediate()
} else {
super.onBackPressed();
}

Related

How to remove the old fragment from the fragment itself

I am invoking a fragment(GetStartFragment) from an activity(DiscoverActivity) and from the same fragment (GetStartFragment), I am replacing it with another fragment (SelectFragment). Now I need to remove the old fragment (GetStartFragment) when I perform onClick() method as below. I was trying to remove, but the fragment ID returns null. How to remove the old fragment from the fragment itself.
DiscoverActivity.java :
public class DiscoverActivity implements AppCompatActivity {
.....
case R.id.start:
showGetStartFragment();
return true;
.....
private void showGetStartFragment() {
lGetStartFragment = GetStartFragment.newInstance();
lGetStartFragment.show(getSupportFragmentManager(), lGetStartFragment.getTag());
}
}
**FragmentDiscover.java :**
public class GetStartFragment extends BaseBottomSheetDialogFragment {
public static GetStartFragment newInstance() {
return new GetStartFragment();
}
.....
#OnClick(R2.id.getstart_button)
void onGetStartButtonClick() {
boolean isStart = true;
/* Show Fragment 2 */
SelectFragment lSelectFragment = SelectFragment.newInstance();
SelectFragment.show(requireActivity().getSupportFragmentManager(), SelectFragment.getTag());
/*Remove previous fragment - Fragment 1*/
FragmentManager fm = requireActivity().getSupportFragmentManager();
Fragment fragment_ID = fm.findFragmentById(com.misc.exam.R.id.design_getstart);
FragmentTransaction fmt = fm.beginTransaction();
if (fragment_ID != null) {
fmt.remove(fragment_ID).commitAllowingStateLoss();
}
}
#Override
public void onDestroy() {
super.onDestroy();
FragmentManager fm = requireActivity().getSupportFragmentManager();
Fragment fragID = fm.findFragmentById(com.misc.exam.R.id.design_getting_started);
FragmentTransaction fmt = fm.beginTransaction();
if (fragID != null) {
fmt.remove(fragID).commitAllowingStateLoss();
}
}
...
}
Have you tried saving the current fragment in a separate field and replacing it?
Fragment currentFragment;
currentFragment = new NewFragment()
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,currentFragment)

ViewPager cause crash

I have a viewpager. The viewpager has five fragments. And these fragments have child fragments. When I start my app, it crashes. If I comment out resetMenu() function it works. What is the problem?
HomeActivity.java
#Override
public void onPageSelected(int position) {
listeners[beforePagerPosition].onViewPaused();
tabLayout.getTabAt(beforePagerPosition).getIcon().setAlpha(getResources().getInteger(R.integer.tab_unselected_alpha));
beforePagerPosition = position;
tabLayout.getTabAt(position).getIcon().setAlpha(getResources().getInteger(R.integer.tab_selected_alpha));
listeners[position].onViewResumed();
resetMenu();
hideKeyboard();
}
#Override
public void onPageScrollStateChanged(int state) {
}
private void resetMenu() {
if (getSupportFragmentManager() != null) {
ParentFragment parentFragment = (ParentFragment) listeners[viewPager.getCurrentItem()];
FragmentManager fm = parentFragment.getChildFragmentManager();
if (fm != null && fm.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry backStackEntry = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1);
String str = backStackEntry.getName();
BaseMenuFragment fragment = (BaseMenuFragment) fm.findFragmentByTag(str);
fragment.setHasOptionsMenu(true);
}
}
}
OK. I found answer. I added control for ParentFragment isAdded().
ParentFragment parentFragment = (ParentFragment) listeners[viewPager.getCurrentItem()];
if(parentFragment.isAdded()){
....
}

No Activity Error on Fragment after FindFragementByTag

I have a Main Activity with multiple Fragments. And this is how how I switch the fragments
public void openFragment(final Fragment fragment, String title, String tag){
getSupportFragmentManager().popBackStack();
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(R.id.container, fragment, tag)
.addToBackStack(null)
.commit();
getSupportActionBar().setTitle(title);
}
I have an INT variable in the Main Activity that I update from each of the Fragment to determine which Fragment was active before an orientation change
private int ACTIVE_FRAGMENT = 0;
During saved instance on the Activity, I save the INT like so
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(Constants.ACTIVE_EVENT_ID, ACTIVE_FRAGMENT);
super.onSaveInstanceState(outState);
}
And onRestoreInstanceState I go back to the Active Fragment like this
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null){
ACTIVE_FRAGMENT = savedInstanceState.getInt(Constants.ACTIVE_EVENT_ID);
if (ACTIVE_FRAGMENT > 0){
FragmentManager fm = getSupportFragmentManager();
switch (ACTIVE_FRAGMENT) {
case 1:
EventSetupFragmentOne fragmentOne = (EventSetupFragmentOne) fm.findFragmentByTag(Constants.SETUP_FRAGMENT_ONE_TAG);
if (fragmentOne != null) {
openFragment(fragmentOne, getString(R.string.event_setup_title_1), Constants.SETUP_FRAGMENT_ONE_TAG);
}
break;
}
}
}
}
This goes back to the Active Fragment but then throws a No Actitiy found exception
Caused by: java.lang.IllegalStateException: No activity
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1125)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1120)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1929)
What can I do differently to go back to an Active Fragment after a configuration change?
You don't have to call openFragment on configuration changes.
Fragments will be recreated by the framework.
That's why in onCreate method you don't (re)initialize your Fragments if savedInstanceState == null.

How to pass Arguments to Fragment from Activity

I have an activity which instantiates and populates a Fragment through a adopter class. I want to pass values/objects to this Fragment adopter class so that i can dictate layout etc.
I have tried passing bundles using the setArgument method but my code bombs out and not sure why. As i understand, i can collect the bundle at the onCreate method in the adopter class. I see the arguments are set correctly immediately after the commented code but alas it crashes after that and i don't understand why. If i remove the bundle code it doesnt crash. :(
Below is my code, does anybody know where i should put the bundle code, commented out?
public class LoginScreen extends ActionBarActivity {
private final String ARG_SELECTED_LAYOUT_ID = "selectedLayoutId";
private final int DEFAULT_LAYOUT = R.layout.layout_list;
private int mSelectedLayoutId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
mSelectedLayoutId = DEFAULT_LAYOUT;
if (savedInstanceState != null) {
mSelectedLayoutId = savedInstanceState.getInt(ARG_SELECTED_LAYOUT_ID);
}
addLayoutTab(
actionBar, R.layout.layout_list, R.drawable.ic_list, "list");
addLayoutTab(
actionBar, R.layout.layout_grid, R.drawable.ic_grid, "grid");
addLayoutTab(
actionBar, R.layout.layout_staggered_grid, R.drawable.ic_staggered, "staggered");
addLayoutTab(
actionBar, R.layout.layout_spannable_grid, R.drawable.ic_spannable, "spannable");
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(ARG_SELECTED_LAYOUT_ID, mSelectedLayoutId);
}
private void addLayoutTab(ActionBar actionBar, int layoutId, int iconId, String tag) {
ActionBar.Tab tab = actionBar.newTab()
.setText("")
.setIcon(iconId)
.setTabListener(new TabListener(layoutId, tag));
actionBar.addTab(tab, layoutId == mSelectedLayoutId);
}
public class TabListener implements ActionBar.TabListener {
private LayoutFragment mFragment;
private final int mLayoutId;
private final String mTag;
public TabListener(int layoutId, String tag) {
mLayoutId = layoutId;
mTag = tag;
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mFragment = (LayoutFragment) getSupportFragmentManager().findFragmentByTag(mTag);
if (mFragment == null) {
mFragment = (LayoutFragment) LayoutFragment.newInstance(mLayoutId);
ft.add(R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
/*
Bundle bundle = new Bundle();
bundle.putInt("noTiles", 4);
mFragment.setArguments(bundle);
*/
mSelectedLayoutId = mFragment.getLayoutId();
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
}
}
Logcat
03-03 11:56:53.675 32507-32507/weebuns.predictionking E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: weebuns.predictionking, PID: 32507
java.lang.RuntimeException: Unable to start activity ComponentInfo{weebuns.predictionking/weebuns.predictionking.LoginScreen}: java.lang.NullPointerException: Attempt to invoke virtual method 'void weebuns.predictionking.LayoutFragment.setArguments(android.os.Bundle)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void weebuns.predictionking.LayoutFragment.setArguments(android.os.Bundle)' on a null object reference
To pass argument to your fragment, it's recommended to create a public static method to instanciate your fragment like :
public static MyFragment newInstance(String firstArg) {
MyFragment f = new MyFragment ();
Bundle args = new Bundle();
args.putString("ARG1", firstArg);
f.setArguments(args);
return f;
}
And then retrieve your args in onCreateView() method like :
String arg = getArguments().getString("ARG1");
Use the static method to instanciate a new fragment with your arguments in other fragment or activities like :
MyFragment f = MyFragment.newInstance(myArg);
It's hard to say without seeing the logcat, but surely you are missing to call the commit() method on the FragmentTransaction. Also remember to set the arguments to the fragment before calling ft.commit().
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mFragment = (LayoutFragment) getSupportFragmentManager().findFragmentByTag(mTag);
if (mFragment == null) {
Bundle bundle = new Bundle();
bundle.putInt("noTiles", 4);
mFragment = (LayoutFragment) LayoutFragment.newInstance(mLayoutId);
mFragment.setArguments(bundle);
//ft.add(R.id.content, mFragment, mTag).commit();
ft.add(R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
mSelectedLayoutId = mFragment.getLayoutId();
}
In your activity
Bundle args = new Bundle();
args.putString("Menu", "Your String");
Fragment detail = new FragmeantOne();
detail.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, detail).commit();
In your Fragment.. in onCreateView()
String menu = getArguments().getString("Menu");
someTextView.setText(menu);
You can use Bundle to transfer data from activity to fragments or transfre data from fragment to fragment.
Bundle bundle = new Bundle();
DisplayDataList fragment = new DisplayDataList();
bundle.putString("listitem", "Buyer,Seller");
fragment.setArguments(bundle);
getFragmentManager().beginTransaction().replace(
R.id.frame_container, fragment).addToBackStack(null).commit();
Where you want to recieve that bundle i.w in DisplayDataList Fragment class. You have to use getArguments() method.
DisplayDataList
Bundle bundle = getArguments();
String getdata = bundle.getString("listitem");
System.out.println("Data got-->>> " + getdata);
Hope this will help you.
You can use Bundle to transfer data between activity and fragment/Dialog fragment
Send data From Your Activity to Fragment() -> onClickBtn()
val f = PaymentBottomSheet()
val bundle = Bundle()
bundle.putString("coins", walletBinding.edCoins.text.toString())
bundle.putString("method", getJsonData()[adapter.selected].method)
bundle.putString("name", walletBinding.edNickName.text.toString())
bundle.putString("acc", walletBinding.edAccountId.text.toString())
f.arguments = bundle
f.show(supportFragmentManager, f.tag)
Retrieved Data from Your Activity (Your fragment code look like this) onCreateView()
val bundle = Bundle(arguments)
coins = bundle.getString("coins")
method = bundle.getString("method")
name = bundle.getString("name")
id = bundle.getString("acc")
Timber.d("Coins $coins, Method $method, Name $name, ID $id")
Thank You

Android rotation: Why fragment not restored by TAG?

I have an activity called MainActivity with a fragment inside called 'HomeFragment'. I want to restore fragment after screen rotation, however something seems missing.
I followed below process:
save fragment by using getSupportFragmentManager().putFragment(...) during onSaveInstanceState
try to restore fragment by fragmentManager.findFragmentByTag during onCreate (after checking that bundle is not null)
However findFragmentByTag returns null.
Here is code snippets inside MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the action bar to show a dropdown list.
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
setupNavigationSpinner(actionBar);
FragmentManager fragmentManager = getSupportFragmentManager();
if (savedInstanceState != null) {
if (fragmentManager.findFragmentByTag("frag") != null) {
// findFragmentByTag always return null
homeFragment = (HomeFragment) fragmentManager
.findFragmentByTag(HomeFragment.ARG_ITEM_ID);
contentFragment = homeFragment;
}else{
homeFragment = new HomeFragment();
switchContent(homeFragment, HomeFragment.ARG_ITEM_ID);
}
} else {
homeFragment = new HomeFragment();
switchContent(homeFragment, HomeFragment.ARG_ITEM_ID);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
getSupportFragmentManager().putFragment(outState, "frag",homeFragment );
}
public void switchContent(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
while (fragmentManager.popBackStackImmediate())
;
if (fragment != null) {
FragmentTransaction transaction = fragmentManager
.beginTransaction();
transaction.replace(R.id.content_frame, fragment, tag);
// Only ArticlDetailFragment is added to the back stack.
if (!(fragment instanceof Fragment)) {
transaction.addToBackStack(tag);
}
transaction.commit();
contentFragment = fragment;
}
}
There is no reason to call
getSupportFragmentManager().putFragment(outState, "frag",homeFragment );
A Fragment state will automatically be restored. The problem is that you're not calling the super of your onSaveInstanceState. It should simply look like this:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
Also, you're setting the tag as HomeFragment.ARG_ITEM_ID then looking for "frag". Does HomeFragment.ARG_ITEM_ID == "frag"?

Categories

Resources