i'm developing an application where I have tabs and fragments within each tab. I got it to work where it shows my listview on launch and when I click a name in the list its goes to the next fragment, but when I hit the back button it doesn't go back to the listview I just get a blank screen. I'm sure it's a simple answer and I have posted some code snippets, if more is needed please let me know. Thank you for your help.
FragmentActivity
public class HomeStackActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmentstackctivity);
// add initial fragment, do not add to back stack, no transition animation
addFragment(new MyCommentActivity(), false, FragmentTransaction.TRANSIT_NONE);
}
void addFragment(Fragment fragment, boolean addToBackStack, int transition) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment1, fragment);
ft.setTransition(transition);
if (addToBackStack)
ft.addToBackStack(null);
ft.commit();
}
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0)
getSupportFragmentManager().popBackStack();
else
finish();
}
}
NameClick
public class nameOnClickListener implements OnClickListener{
private int position;
public nameOnClickListener(int position){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = commentList.get(position);
Log.d("Testing Click", update.get(MyCommentActivity.KEY_UID));
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor edit = sp.edit();
edit.putString("profile_id", update.get(MyCommentActivity.KEY_UID));
edit.putString("profile_type", update.get(MyCommentActivity.KEY_TYPE));
edit.commit();
addFragment(new UserProfileActivity(), true, FragmentTransaction.TRANS
change:
ft.replace(R.id.fragment1, fragment);
to:
ft.add(R.id.fragment1, fragment);
Related
I know this has to be simple and I'm probably not seeing the solution.
Here is the brief description of what I have:
SignInActivity(AppCompatActivity) - handle the Firebase authentication, on success calls the method:
private void onAuthSuccess(FirebaseUser user) {
// Go to MainActivity
startActivity(new Intent(SignInActivity.this, MainActivity.class));
finish();
}
MainActivity(AppCompatActivity) - handle the menus for the application, this menu in particular are fragments with buttons. When a button is clicked I change the fragment that contains other buttons. Something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_left)
.replace(R.id.fragmentContent, MainMenuFragment.newInstance())
.commitNow();
}
getSupportFragmentManager().addOnBackStackChangedListener(new
FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
getSupportActionBar().setHomeButtonEnabled(stackHeight > 0);
getSupportActionBar().setDisplayHomeAsUpEnabled(stackHeight > 0);
}
});
}
public void replaceFragments(Class fragmentClass, boolean isBack) {
Fragment fragment = null;
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (isBack) {
ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
} else {
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
}
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
fragment = null;
e.printStackTrace();
}
if (fragment != null) {
ft.replace(R.id.fragmentContent, fragment);
ft.addToBackStack(fragmentClass.getName());
ft.commit();
}
}
MainMenuFragment(Fragment) - First set of options, several buttons on top of each other. Depending on the button clicked will call MainActivity.replaceFragment passing the next Fragment to go.
public class MainMenuFragment extends Fragment {
private static final String TAG = "MainMenuFragment";
public static MainMenuFragment newInstance() {
return new MainMenuFragment();
}
public MainMenuFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main_menu, container, false);
final Button btnAssets = view.findViewById(R.id.btnAssets);
final Button btnAudit = view.findViewById(R.id.btnAudit);
btnAssets.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(TAG, "BtnAssets_onClick");
((MainActivity)getActivity()).replaceFragments(AssetMenuFragment.class);
}
});
btnAudit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(TAG, "BtnAudit_onClick");
((MainActivity)getActivity()).replaceFragments(AuditMenuFragment.class);
}
});
return view;
}
}
The AssetMenuFragment and the AuditMenuFragment are pretty much the same as the MainMenu, only the text for the buttons and some layout details changes.
When I'm using the app I first signIn, which leads me to the MainActivity, which loads the MainMenuFragment on onCreate. There I'm presented with two buttons, one to go to the AssetMenuFragment and the other to go to the AuditMenuFragment, they replace the fragment with their according layouts.
If I click the Asset button, once the fragment is replaced, because of:
getSupportActionBar().setHomeButtonEnabled(stackHeight > 0);
getSupportActionBar().setDisplayHomeAsUpEnabled(stackHeight > 0);
I'm presented with the back arrow to go back to MainMenuFragment. Everything works as expected.
Now the problem! If I'm in this AssetMenuFragment, with my beautiful back arrow showing on the ActionBar and decided to click the "Square" button on the device, which is probably run the onPause and onStop, and them click on the app again, which will run the onCreate and onStart again, my back arrow disappears, because now int stackHeight = getSupportFragmentManager().getBackStackEntryCount(); is zero.
How can I save my stack and restore it later so I can press back on the AssetMenuFragment and go back to MainMenuFragment.
It is a lot to read, but I'll appreciate the help, thanks!
In the end I knew it had to be something simple.
Both checks are correct.
getSupportActionBar().setHomeButtonEnabled(stackHeight > 0);
getSupportActionBar().setDisplayHomeAsUpEnabled(stackHeight > 0);
The problem was that I didn't check for them on onCreate, only on the getSupportFragmentManager().addOnBackStackChangedListener event.
Here is the MainActivity now:
private ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left)
.replace(R.id.fragmentContent, MainMenuFragment.newInstance())
.commitNow();
}
actionBar = getSupportActionBar();
updateActionBarBackButton();
getSupportFragmentManager().addOnBackStackChangedListener(new
FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
updateActionBarBackButton();
}
});
}
private void updateActionBarBackButton() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
getSupportActionBar().setHomeButtonEnabled(stackHeight > 0);
getSupportActionBar().setDisplayHomeAsUpEnabled(stackHeight > 0);
}
I have a HomeFragment and LoginFragment in the same Activity. At first it shows the HomeFragment and then go to LoginFragment. But when I back to HomeFragment it reloads the HomeFragment.
How to make the HomeFragment not reload when I press back from LoginFragment.
Thanks in advance. Here is my code :
MainActivity.java
private void setEvent() {
img_action_back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
closeKeyboard(getApplication(), img_action_back.getWindowToken());
onBackPressed();
}
});
}
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment)
{
LoginFragment loginFragment = (LoginFragment)fragment;
if(loginFragment.fromDrawer)
Navigator.showHomeFragment(mContext);
else
finish();
}
}
}
Navigator.java
public static void showHomeFragment(Context context) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID, new HomeFragment());
transaction.commit();
}
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.commit();
}
Your issue is that you're recreating a new HomeFragment instance when you press back. You should instead pop the FragmentManager backstack.
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment) {
LoginFragment loginFragment = (LoginFragment)fragment;
if(loginFragment.fromDrawer) {
getFragmentManager(mContext).popBackStack();
} else {
finish();
}
}
}
You also need to make sure you add the LoginFragment to the backstack.
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister)
{
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.addToBackStack(null);
transaction.commit();
}
#SunnySydeUp has a nice answer. I've found some improvement scope in your code.
You need to add the Fragment in the back stack so that the super.onBackPressed call won't recreate the Fragment again like the other answer says.
But as you're really worried about recreating Fragment you might initialize them once and use the instance again during transaction.
So in the onCreate function of your Activity initialize both of them first like this.
private HomeFragment mHomeFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
mHomeFragment = new HomeFragment();
}
Now your onBackPressed may look like
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment) {
if(loginFragment.fromDrawer) {
super.onBackPressed();
} else {
finish();
}
}
}
And the Navigator.java
public static void showHomeFragment(Context context) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID, mHomeFragment);
transaction.commit();
}
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.addToBackStack(null).commit();
}
I am using a SlidingTabLayout to implement the sliding tabs. The problem is, when backing from a fragment to the tab fragment, it disappears.
I am going to show the application flow to make things more clear.
First, I call an Activity whithin a Fragment:
public class ScreenSlidePageFragment extends Fragment{
...
public View onCreateView(args...){
...
gridView.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
switch(position){
case 0:
Intent intent = new Intent(getActivity(), FrequencyActivity.class);
startActivity(intent);
break;
}
}
}
...
}
}
The code of the FrequencyActivity is below:
public class FrequencyActivity extends AppCompatActivity{
...
protected void onCreate(Bundle savedInstance){
...
toolbar.setNavigationOnClickListener(new OnClickListener(){
#Override
public void onClick(View view){
onBackPressed();
}
});
}
final FragmentManager fm = getSupportFragmentManager();
Fragment fragment = Fragment.instantiate(getBaseContext(), "com.example.Fragment.FragmentFrequency");
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.home, fragment, "FragFrequency");
fragmentTransaction.addToBackStack("frequency");
fragmentTransaction.commit();
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish(); //When there is no back stack, finish the activity
} else {
//Testing purpose
int top = fm.getBackStackEntryCount();
Log.d("BACKSTACK", "Backstack count: " + String.valueOf(top));
Log.d("BACKSTACK", "Backstack name: " + fm.getBackStackEntryAt(top - 1).getName());
}
}
});
}
The FragmentFrequency is the one which contains the SlidingTabLayout, the code can be seen below:
public class FragmentFrequency extends Fragment{
...
//Creates ViewPager adapter
adapter = new ViewPagerAdapter(activity.getSupportFragmentManager(), titles, numOfTabs);
//ViewPager
viewPager = (ViewPager) layout.findViewById(R.id.pager);
viewPager.setAdapter(adapter);
//SlidingTabLayout code
...
}
And finally, the ViewPagerAdapter which loads the Fragments of the tabs
public class ViewPagerAdapter extends FragmentStatePagerAdapter{
...
#Override
public Fragment getItem(int position){
if(position == 0)
return new FragmentTab1();
else
return new FragmentTab2();
}
...
}
For example when the first tab is selected, the FragmentTab1 is loaded, which contains:
Fragment f = Fragment.instantiate(getActivity(), "com.example.Fragment.FragmentLaunchingFrequency");
FragmentTransaction tx = getActivity().getSupportFragmentManager().beginTransaction();
tx.replace(R.id.home, f, "FragLaunchingFrequency");
tx.addToBackStack("launchingfrequency");
tx.commit();
The problem is, when the back action is done, the FrequencyActivity loses the reference of the Fragment and it shows a blank. Also, the sliding tabs stop working properly.
Does anyone know how to fix this? I am really out of alternatives.
Thanks
I think you have 2 major questions in your post. Perhaps make another post for the other question.
For now, I can address your question "The problem is, when the back action is done, the FrequencyActivity loses the reference of the Fragment and it shows a blank".
Your code:
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.home, fragment, "FragFrequency");
fragmentTransaction.addToBackStack("frequency");
fragmentTransaction.commit();
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
...
Notes:
You cannot call addToBackStack() and use OnBackStackChangedListener() in the same FragmentManager. This seems complicated.
Code suggestions:
Remove the use of addOnBackStackChangedListener() and see what happens.
Specific code suggestion:
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish(); //When there is no back stack, finish the activity
} else {
// Call FragmentManager.findFragmentByTag(String tag)
// Call FragmentTransaction.show() after getting the Fragment.
}
}
});
Note: Notice the else block manages the Fragment instead of depending on the BackStack (addToBackStack method).
I have a Fragment addtaskfragment in which I am asking the user to provide details like name, place,etc. I have a Button in the same fragment which when clicked goes to another fragment mycontacts gets a string value from that fragment and is passed to addtaskfragment. When mycontacts fragment goes back to the addtaskfragment the values previously entered in the edittexts disappears. I want those values to stay there when the value from mycontacts is returned. How can I do that? My codes are as below. Please guide me step by step.
addtaskfragment.class
public class addtaskfragment extends Fragment {
int mYear, mMonth, mDay;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.friendspage2, container, false);
addcontacts.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Fragment fr;
fr = new mycontacts();
FragmentManager fm = getFragmentManager();
android.app.FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.content_frame, fr);
ft.commit();
}
});
}
}
mycontacts extends Fragment
purple.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v){
Fragment fr;
fr = new addtaskfragment();
FragmentManager fm = getFragmentManager();
android.app.FragmentTransaction ft = fm.beginTransaction();
Bundle args = new Bundle();
args.putString("CID", cid);
Log.i("cid from long click", "" + cid);
fr.setArguments(args);
ft.replace(R.id.content_frame, fr);
ft.commit();
}
});
Your EditText search text is clear it's because you're replace ft.replace(R.id.content_frame, fr); fragment that means it's first called ft.remove(); and then called ft.add(); so your onCreateView() method again called and create a New reference of your EditText so that's why your EditText search text is clear.
Solution is store search text in SharedPreferences on onPause() and load the search text value from SharedPreferences on onResume() on Fragment.
Implement like below:
private void savePreferences() {
SharedPreferences settings = getActivity().getSharedPreferences("settings",
Context.MODE_APPEND);
SharedPreferences.Editor editor = settings.edit();
KeywordValue = inputSearch.getText().toString();
System.out.println("onPause save keyword: " + KeywordValue);
editor.putString("searchText","");
editor.commit();
}
and
private void loadPreferences() {
SharedPreferences settings = getActivity().getSharedPreferences("settings",
Context.MODE_APPEND);
KeywordValue = settings.getString("searchText",
"");
inputSearch.setText(KeywordValue.toString());
keyword_search=KeywordValue;
System.out.println("onResume load keyword: " + KeywordValue);
}
and used in your Fragment like
#Override
public void onPause() {
super.onPause();
savePreferences();
}
and
#Override
public void onResume() {
super.onResume();
loadPreferences();
}
Try to add android:saveEnabled="false" attribute in EditText. It fixed the same problem for me.
<EditText
android:id="#+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
android:saveEnabled="false" />
Here you can see another example:
An SSCCE for this issue is available on GitHub.
For future readers, the original example is on a branch of the same project, and the fix is available in this diff.
This SSCCE has a ListView and a row of buttons. The buttons are supposed to change the data in the ListView, and the listView rows (when clicked) are supposed to open a new fragment and advance the backstack while staying in the same activity.
If do the following things, it produces the following result:
Open the app.
Tap the ListView. - FragmentTransaction.replace(...) with addToBackStack(true)
Tap any of the buttons. - FragmentTransaction.replace(...) with addToBackStack(false)
Tap the back button.
Result:
Both fragments become visible, but I only want the first loaded fragment (ListTwoFragment in code) to display. Is this how fragments are supposed to work? If so, how can I get the desired effect?
MainActivity.java:
public class MainActivity extends FragmentActivity implements ListTwoFragment.Callbacks,
ListThreeFragment.Callbacks {
public static final String KEY_ARGS = "args";
private String curUri = "";
private String curArgs = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
selectContent(false);
}
private void selectContent(boolean addToBackStack) {
Fragment fragment;
if (curUri.isEmpty()) {
// Use default fragment
fragment = new ListTwoFragment();
curUri = ListTwoFragment.class.getName();
}
else {
try {
Class<Fragment> fragmentClass = (Class<Fragment>) Class.forName(curUri);
fragment = fragmentClass.newInstance();
}
catch (Exception e) { // ClassNotFound, IllegalAccess, etc.
return;
}
}
// Configure fragment
Bundle args = new Bundle();
args.putString(KEY_ARGS, curArgs);
fragment.setArguments(args);
attachFragment(fragment, addToBackStack, curUri + ";" + curArgs, R.id.fragment_container);
}
protected void attachFragment(Fragment fragment, boolean addToBackStack, String tag, int replaceId) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(replaceId, fragment, tag);
if (addToBackStack) transaction.addToBackStack(tag);
transaction.commit();
}
#Override
public void onTwoButtonClick(String title) {
curUri = ListTwoFragment.class.getName();
curArgs = title;
selectContent(false);
}
#Override
public void onTwoListClick() {
curUri = ListThreeFragment.class.getName();
curArgs = "";
selectContent(true);
}
#Override
public void onThreeButtonClick(String title) {
curUri = ListThreeFragment.class.getName();
curArgs = title;
selectContent(false);
}
}
I'm working with Fragments to, and the way I'm doing it:
to go forward (add to stack), and backwords (remove from stack) are two different functions
to Add to Stack and change Fragment:
public void changeFragmentAddToStack(Fragment myNewFragment) {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(R.id.main_fragment, myNewFragment);
t.addToBackStack(null);
t.commit();
}
To go back Stack:
public void goBackStackMain() {
FragmentManager man = getSupportFragmentManager();
if(man.getBackStackEntryCount()>0){
man.popBackStack(man.getBackStackEntryAt(0).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
And if you want to do both: To go back stack and change fragment:
public void goBackStackAndReplaceFragment(Fragment myNewFragment) {
FragmentManager man = getSupportFragmentManager();
if(man.getBackStackEntryCount()>0){
int n = man.getBackStackEntryCount();
man.popBackStack(man.getBackStackEntryAt(n-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.replace(R.id.main_fragment, myNewFragment);
t.commit();
}
I hope to help you !