I'm getting this error while trying to launch a Fragment from a first Fragment :
java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.FragmentTransaction android.app.FragmentManager.beginTransaction()' on a null object reference
Here's the method where I'm getting the error :
#Override
public void onClick(View v) {
Fragment fragment = new PropertyFragment();
if (fragment != null) {
FragmentManager fragmentManager = fragment.getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransaction.replace(R.id.rent_viewer, fragment);
fragmentTransaction.addToBackStack(null);
// Commit the transaction
fragmentTransaction.commit();
}
}
Precisely, the following code instruction is causing the error :
fragmentManager.beginTransaction();
Here's how the class and the nested class look like :
public class RentFragment extends Fragment {
...
#SuppressWarnings("unused")
private OnRentFragmentInteractionListener mListener;
public RentFragment() {
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnRentFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnRentFragmentInteractionListener");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_rent, container, false);
myOnClickListener = new RentOnClickListener(getActivity());
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnRentFragmentInteractionListener {
public void onRentFragmentInteraction(Uri uri);
}
private static class RentOnClickListener implements View.OnClickListener {
private final Context context;
private RentOnClickListener(Context context) {
this.context = context;
}
#Override
public void onClick(View v) {
Fragment fragment = new PropertyFragment();
if (fragment != null) {
FragmentManager fragmentManager = fragment.getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransaction.replace(R.id.rent_viewer, fragment);
fragmentTransaction.addToBackStack(null);
// Commit the transaction
fragmentTransaction.commit();
}
}
}
}
FragmentManager will be null until it is attached to the activity.So use below code ,
If it is a nested Fragment use this.getChildFragmentManager() for your fragment class else use getActivity().getFragmentManager() or getActivity().getSupportFragmentManager().
Declare your FragmentTransaction and initialize it like this
FragmentTransaction fm;
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
fm = getFragmentManager();
}
Then on your code, you can call this
Fragment fragment = new PropertyFragment();
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.rent_viewer, fragment).addToBackStack(null).commit();;
Use the Customize Method in Base Class and use it.
public void replaceFragment(Fragment newFragment, Bundle bundle, boolean isAddToBackStack) {
String tag = newFragment.getClass().getSimpleName();
if (bundle != null) {
newFragment.setArguments(bundle);
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (getCurrentFragment() != null) {
ft.hide(getCurrentFragment());
}
ft.add(R.id.frameContainer, newFragment, tag);
newFragment.setRetainInstance(true);
if (isAddToBackStack) {
ft.addToBackStack(tag);
}
try {
ft.commitAllowingStateLoss();
} catch (Exception ex) {
ex.printStackTrace();
// ft.commitAllowingStateLoss();
}
}
You have to set context of activity as shown below on code HomeActivity.this.getSupportFragmentManager();
private Fragment fragment;
private FragmentManager fragmentManager;
fragmentManager=HomeActivity.this.getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
transaction.add(R.id.main_container, new ProfileFragment()).commit();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransaction.replace(R.id.rent_viewer, fragment);
fragmentTransaction.addToBackStack(null);
// Commit the transaction
fragmentTransaction.commit();
Related
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)
I am making a simple demo project using Fragments, in which i am calling SecondFragment from FirstFragment on button click.
And i called SecondFragment without any issue, but i getting view of both the Fragments SecondFragment and FirstFragment
So where i am doing mistake ?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new FirstFragment()).commit();
}
}
public static class FirstFragment extends Fragment {
Button buttonCallSecondFragment;
public FirstFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_first, container,
false);
buttonCallSecondFragment = (Button) rootView.findViewById(R.id.button1);
buttonCallSecondFragment.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentManager fm = getFragmentManager();
SecondFragment fragment = new SecondFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.container, fragment);
ft.commit();
}
});
return rootView;
}
}
public static class SecondFragment extends Fragment {
public SecondFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_second, container,
false);
return rootView;
}
}
}
You need to remove the first fragment, you can do that either by usingreplace or first calling remove then add
To be able to press the back button add the transaction to the back stack,you do that by calling addToBackStack(tag) on your fragment manager. Tag may be null.
You are adding fragment on already displaying fragment in your android app.
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.container, fragment);
ft.commit();
Do not add fragment but replace fragment when already one fragment is loaded on activity.
So for implementing that :
Please add your code in OnCreate() and add below code to your click listener :
FragmentManager fm = getFragmentManager();
SecondFragment fragment = new SecondFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragment);
ft.addToBackStack(null);
ft.commit();
Thank you.!!
if you want to replace fragment you should call replace in place of add :
buttonCallSecondFragment = (Button) rootView.findViewById(R.id.button1);
buttonCallSecondFragment.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentManager fm = getFragmentManager();
SecondFragment fragment = new SecondFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
}
});
You are adding the fragment so it will come on top of another fragment give background color to rootview of scecond fragment
you can also load that as Nested fragment
public void addFragB() {
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
SecondFragment 2ndfrag = new SecondFragment();
childFragTrans.add(R.id.fragA_LinearLayout, 2ndfrag);
//childFragTrans.addToBackStack("");
childFragTrans.commit();
}
No, you cannot communicate between fragments like this. You need to communicate between fragments via the container activity.
I suggest, you make a navigation method in your activity and switch/call between fragments from there.
Below is a small snippet:
In your container activity:
Fragment fragment;
public void navigateTo(Fragment newFragment,boolean addToBackStack) {
this.fragment = newFragment;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.fragment_container, newFragment);
if(addToBackStack)
ft.addToBackStack(fragment.getClass().getSimpleName());
ft.commit();
}
If you are going from Fragment1 to Fragment2 then in your Fragment1 do the following:
((YourContainerActivity) getActivity()).navigateTo(new Fragment2(),false); //if you want to add to back stack make 2nd argument true.
private void selectItem(int position) {
selectedPosition = position;
// update the main content by replacing fragments
fragment = null;
fragmentStack = new Stack<Fragment>();
switch (position) {
case 0:
fragment = new Fragment1();
break;
case 1:
fragment = new Fragment2();
break;
}
//redirect the screen
if (fragment != null) {
fragmentManager = getSupportFragmentManager();
redirectScreen(fragment);
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(title[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
}
I tried to create a ProgressFragment to be used in the whole application:
public class ProgressFragment extends Fragment {
private static final String KEY = "info";
private CustomProgressView mProgress;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(...);
mProgress = (CustomProgressView) root.findViewById(...);
mProgress.start();
if (getArguments() != null) {
mProgress.setText(getArguments().getString(KEY, ""));
}
return root;
}
public void setInformation(String text) {
if (mProgress != null) {
if (this.isHidden()) {
show(getActivity().getSupportFragmentManager());
}
mProgress.setText(text);
} else {
Bundle b = new Bundle();
b.putString(KEY, text);
setArguments(b);
}
}
public void hide(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
ft.hide(this);
ft.commit();
}
private void show(FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
ft.show(this);
ft.commit();
}
}
The ProgressFragment is expected to attached(added) to the activity once, and there should be only one instance, then the client should only update its' information or hide it.
Usage in client(activity):
class MainActivity...{
ProgressFragment mFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFragment = ProgressFragment.newInstance(null);
FragmentTransaction fs = getSupportFragmentManager().beginTransaction();
fs.add(id, mFragment);
fs.commit();
}
//close the progress
mFragment.hide()
//show the progress
mFragment.setInformation("..")
}
However it does not worked as expected, sometime the progress view will show only at the first call or it does not even show.
Did I miss anything?
Don't set your onCreateView as #Nullable;
Use commitAllowingStateLoss();
Instead of just add(), use addToBackStack(). When you want to close it, just use the FragmentManager popBackStack():
getActivity().getSupportFragmentManager().popBackStack();
How can I attach existing fragment, when change back tab in ActionBar.NAVIGATION_MODE_TABS?
class MyTabsListener implements ActionBar.TabListener {
public Fragment fragment;
public MyTabsListener(Fragment fragment) {
this.fragment = fragment;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.detach(fragment);
}
}
You can verify if your fragment was loaded previously in the fragment stack. Also could store all your used fragments to reuse it
public void setFragment(Fragment fragmentInstance) {
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
Fragment oldFragmentInstance = fragmentManager
.findFragmentById(R.id.curr_fragment);
boolean onlyAttach = false;
List<Fragment> prevFragments = getSupportFragmentManager()
.getFragments();
for (Fragment fragment : prevFragments) {
if (fragment == null) {
continue;
}
if (fragment.getClass().equals(fragmentInstance.getClass())) {
onlyAttach = true;
fragmentInstance = fragment;
break;
}
}
fragmentTransaction.detach(oldFragmentInstance);
if (onlyAttach) {
fragmentTransaction.attach(fragmentInstance);
} else {
fragmentTransaction.replace(R.id.curr_fragment,
fragmentInstance);
fragmentTransaction.addToBackStack(null);
}
try {
fragmentTransaction.commit();
}
}
Hope this helps
Yesterday I downloaded new HoloEverywhere library.
Currently, I have problem with tab navigation after screen rotation.
My Home Activity:
public class MainActivity extends Activity implements TabListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setUpTabs();
}
private void setUpTabs() {
String[] titles = {
"First", "Second",
};
ActionBar supportActionBar = getSupportActionBar();
for (int i = 0; i < titles.length; i++) {
ActionBar.Tab tab = supportActionBar.newTab();
tab.setText(titles[i]);
tab.setTag(MyFragment.TAG);
tab.setTabListener(this);
supportActionBar.addTab(tab, false);
}
supportActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
supportActionBar.setSelectedNavigationItem(0);
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction fragmentTransaction) {
final String fragmentTag = tab.getTag().toString();
Fragment fragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
if (fragment == null) {
fragment = new MyFragment();
fragmentTransaction.add(android.R.id.content, fragment, fragmentTag);
} else {
fragmentTransaction.attach(fragment);
}
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag((String) tab.getTag());
if (fragment != null) {
fragmentTransaction.detach(fragment);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
}
}
And my Fragment class.
public class MyFragment extends Fragment {
public static final String TAG = MyFragment.class.getCanonicalName();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = new View(getActivity());
view.setBackgroundColor(Color.BLACK);
return view;
}
}
When I rotate the screen fragment not displaying. It displays when i select tab (which is not currently selected) manually.
I just solve the problem.
I post my code here and see if those can help you :D
if (savedInstanceState == null){
TabHomeFragment homeFragment = new TabHomeFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, homeFragment, "home_fragment").commit();
}else{
TabHomeFragment homeFragment = (TabHomeFragment) getSupportFragmentManager().findFragmentByTag("home_fragment");
}
Those code are located in OnCreate method.
When the Device rotate and Ortiention change, the fragment will recreate again. So add a if clase to check if there is already one here.
But I am using normal Fragment in Android. Hope it can help you a little.