I have an application where the MainActiviy works with a bottom navigation which switches between full screen fragments.
I've learnt to control the back button navigation by ensuring I add each fragment to the backstack when created.
fragmentManager.beginTransaction().add(R.id.contentContainer, fragment, fragment_tag).addToBackStack(fragment_tag).commit();
There is one type of fragment, the loading screen fragment, that I do not want added to the backstack so I exclude the addToBackStack() method when creating the fragment.
As shown in the gif below Somehow the loading fragment still appears when pressing the back button even though it is not on the backstack (I've confirmed this with the debugger).
If anyone could give me a hand in figuring out why it is showing up I'd be really grateful, it has plagued me for about a week and I'm out of ideas!
Here is the code:
package *package name*;
import *all import statements*
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<ArrayList> {
BottomNavigation mBottomBar;
private FloatingActionButton fab;
private FirebaseDatabase database;
private DatabaseReference DB_Storage_Ref, DB_Master_Ref;
FragmentManager fragmentManager;
CustomBottomBarSelectionListener bbListener;
CustomBackStackChangeListener cBSCL;
ArrayList<IngredientCard> master = new ArrayList<>();
ArrayList<IngredientCard> all = new ArrayList<>();
ArrayList<IngredientCard> fridge = new ArrayList<>();
ArrayList<IngredientCard> freezer = new ArrayList<>();
ArrayList<IngredientCard> pantry = new ArrayList<>();
ArrayList<IngredientCard> ingredient_imports = new ArrayList<>();
int arraysLoaded = 0;
boolean loadingComplete = false;
ArrayList<String> storageLocationList = new ArrayList<>();
Map<String, ArrayList<IngredientCard>> storageLocationMapLists = new HashMap<>();
final String[] tag = {null};
boolean backButtonPressed = false;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Establish FirebaseDatabase Instance and required DB References
database = FirebaseDatabase.getInstance();
DB_Storage_Ref = database.getReference("Storage");
DB_Master_Ref = database.getReference("Master");
// These Storage location must match branch titles in Firebase JSON database
// Create a list of all Storage Room Titles (matching realtime database branch names)
storageLocationList.add("All");
storageLocationList.add("Fridge");
storageLocationList.add("Freezer");
storageLocationList.add("Pantry");
// Create a hashmap mapping all storage room arrays to the associated storage room titles.
storageLocationMapLists.put("All", all);
storageLocationMapLists.put("Fridge", fridge);
storageLocationMapLists.put("Freezer", freezer);
storageLocationMapLists.put("Pantry", pantry);
// Associate UI to Variables
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
mBottomBar = (BottomNavigation) findViewById(R.id.BottomNavigation);
fragmentManager = getSupportFragmentManager();
bbListener = new CustomBottomBarSelectionListener(this);
mBottomBar.setOnMenuItemClickListener(bbListener);
cBSCL = new CustomBackStackChangeListener(this);
fragmentManager.addOnBackStackChangedListener(cBSCL);
// Load arrays with data from Firebase Database.
populateArrays();
// Customise UI config where necessary
setSupportActionBar(myToolbar);
mBottomBar.setDefaultSelectedIndex(2);
tag[0] = PLAN_FRAGMENT_TAG;
fragmentManager.beginTransaction().add(R.id.contentContainer, new PlanFragment(), tag[0]).commit();
// Set onClick Listener for FAB button. The FAB should change/animate as user switches between BottomBar options
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment fragment;
// Find the IngredientsFragment in the Fragment Manager
fragment = fragmentManager.findFragmentByTag(INGREDIENT_FRAGMENT_TAG);
// If the Fragment exists and is visible then carryout action
if (fragment != null && fragment.isVisible()) {
Intent SelectIngredient = new Intent(getBaseContext(), Ingred_MasterList.class);
Bundle args = new Bundle();
args.putParcelableArrayList(ARG_INGREDIENTS_LIST, master);
args.putStringArrayList(ARG_STORAGE_LOCATIONS, storageLocationList);
SelectIngredient.putExtras(args);
startActivity(SelectIngredient,args);
}
}
});
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if(getIntent().getExtras() != null) {
Bundle args = getIntent().getExtras();
if (args.containsKey(INGREDIENT_IMPORTS)) {
ingredient_imports = (args.getParcelableArrayList(INGREDIENT_IMPORTS));
bbListener.switchFragment(LOADING_FRAGMENT_TAG, new LoadingFragment());
fragmentManager.popBackStackImmediate();
distributeItems(ingredient_imports);
}
}
}
private void distributeItems(ArrayList<IngredientCard> array) {
for(IngredientCard ingredient : array){
DB_Storage_Ref.child("All").child(ingredient.getItemName()).setValue(ingredient);
DB_Storage_Ref.child(ingredient.getStorageLocation()).child(ingredient.getItemName()).setValue(ingredient);
}
ingredient_imports.clear();
}
private void populateArrays() {
// Cycle through storageLocationList array and add the storage location title (which must match a branch name on the Firebase Database.
for (int i = 0; i < storageLocationList.size(); i++) {
Bundle args = new Bundle();
args.putString(TEMP_BUNDLE_STORAGE_TITLE, storageLocationList.get(i));
// For each storage location create a loader to retrieve its data from the Firebase Database
getSupportLoaderManager().initLoader(i, args, this);
}
// Create a loader that retrieves the master list of food icons
getSupportLoaderManager().initLoader(MASTER_LIST_ARRAY_ID, null, this);
}
#Override
public Loader<ArrayList> onCreateLoader(int id, Bundle args) {
String DBbranch;
if (args == null) {
//If bundle args don't exist assume we want data from 'Master' branch of DB
DBbranch = "Food_Items";
return new IngredientsListLoader(this, DB_Master_Ref, DBbranch, this);
} else {
//If bundle args exist, extract them and add them as IngredientListLoader variable
DBbranch = args.getString(TEMP_BUNDLE_STORAGE_TITLE);
return new IngredientsListLoader(this, DB_Storage_Ref, DBbranch, this);
}
}
#Override
// Should be called after loadInBackground has completed but seems to return earlier. The method returnResults has been created in IngredientsListLoader to deal with this.
public void onLoadFinished(Loader<ArrayList> loader, ArrayList data) {
if (loader.getId() == MASTER_LIST_ARRAY_ID) {
// if MASTER_LIST Loader set master ArrayList to data
master = data;
} else {
// cycle through each item in storageLocationList Array (the Array position -eq loader id) and replace Array in storageLocationList position with data Array
for (int i = 0; i < storageLocationList.size(); i++) {
if (loader.getId() == i) {
storageLocationMapLists.put(storageLocationList.get(i), data);
}
}
}
}
#Override
public void onLoaderReset(Loader<ArrayList> loader) {
}
#Override
public void onBackPressed() {
backButtonPressed = true;
if (fragmentManager.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping fragment backstack");
fragmentManager.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
}
void bottomBarUpdate(){
Fragment currentBackStackFragment = getBackstackFragment();
if(currentBackStackFragment instanceof Ingredients_BottomBarFrag || currentBackStackFragment instanceof LoadingFragment){
mBottomBar.setSelectedIndex(0,true);
return;
}
if(currentBackStackFragment instanceof MealsFragment){
mBottomBar.setSelectedIndex(1,true);
return;
}
if(currentBackStackFragment instanceof PlanFragment){
mBottomBar.setSelectedIndex(2,true);
return;
}
if(currentBackStackFragment instanceof ShoppingFragment){
mBottomBar.setSelectedIndex(3,true);
return;
}
if(currentBackStackFragment instanceof SettingsFragment){
mBottomBar.setSelectedIndex(4,true);
return;
}
}
private Fragment getBackstackFragment(){
String fragmentTag;
if(fragmentManager.getBackStackEntryCount() > 0) {
fragmentTag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
}else{
fragmentTag = PLAN_FRAGMENT_TAG;
fragmentManager.beginTransaction().add(R.id.contentContainer, new PlanFragment(), tag[0]).commit();
}
return fragmentManager.findFragmentByTag(fragmentTag);
}
}
class IngredientsListLoader extends AsyncTaskLoader {
private DatabaseReference DBRef;
private String DBBranch;
private ArrayList<IngredientCard> food_Items_List = new ArrayList<>();
private MainActivity ma;
IngredientsListLoader(Context context, DatabaseReference instance, String DBBranch, MainActivity main) {
super(context);
DBRef = instance;
this.DBBranch = DBBranch;
ma = main;
forceLoad();
}
#Override
public ArrayList<IngredientCard> loadInBackground() {
food_Items_List.clear();
DBRef = DBRef.child(DBBranch);
CustomListener cl = new CustomListener(ma);
DBRef.addValueEventListener(cl);
Log.v("TAG", "Returning LIST of size " + food_Items_List.size());
return cl.returnResults();
}
}
class CustomListener implements ValueEventListener {
private ArrayList<IngredientCard> food_Items_List = new ArrayList<>();
private MainActivity ma;
CustomListener(MainActivity main){
ma = main;
}
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> children = dataSnapshot.getChildren();
food_Items_List.clear();
for (DataSnapshot child : children) {
IngredientCard ingredientCard = child.getValue(IngredientCard.class);
food_Items_List.add(ingredientCard);
Log.v("ValueEventLisenter", "Accessing Firebase!");
}
returnResults();
removeLoadingScreen();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
ArrayList<IngredientCard> returnResults() {
return food_Items_List;
}
void removeLoadingScreen(){
//If all arrays have been loaded and the ingredient_import array has been cleared...
if(ma.arraysLoaded == ma.storageLocationList.size() && ma.ingredient_imports.size() == 0) {
ma.loadingComplete = true;
// tag[0] represents the tag of the currently displayed fragment. It changes to the first parameter of the switchFragment method each time it is called.
//If the displayed fragment is the LOADING_FRAGMENT switch it out for the INGREDIENT_FRAGMENT
if (ma.tag[0] == LOADING_FRAGMENT_TAG) {
ma.bbListener.switchFragment(INGREDIENT_FRAGMENT_TAG, new Ingredients_BottomBarFrag());
}
}else{
//For each loader that completes and calls this method, the values of arraysLoaded increases until it matches the number of loaders expected to return.
ma.arraysLoaded++;
}
}
}
class CustomBottomBarSelectionListener implements OnMenuItemSelectionListener {
private MainActivity ma;
CustomBottomBarSelectionListener(MainActivity main){
ma = main;
}
#Override
public void onMenuItemSelect(#IdRes int tabId, int position, boolean fromUser) {
//if this is triggered via pressing the back button, then simply return as fragmentManager.popBackStack() will handle switching fragments.
if(ma.backButtonPressed){
ma.backButtonPressed = false;
return;
}
switch (tabId) {
case R.id.menu_ingredients:
//if items have not completed loading show loading screen
if(!ma.loadingComplete && ma.ingredient_imports.size() == 0){
switchFragment(LOADING_FRAGMENT_TAG, new LoadingFragment());
}else{
switchFragment(INGREDIENT_FRAGMENT_TAG, new Ingredients_BottomBarFrag());
}
break;
//TODO: Have RecyclerView scroll position restored when fragment comes back into view
case R.id.menu_meals:
switchFragment(MEAL_FRAGMENT_TAG, new MealsFragment());
break;
case R.id.menu_plan:
switchFragment(PLAN_FRAGMENT_TAG, new PlanFragment());
break;
case R.id.menu_groceries:
switchFragment(SHOPPING_FRAGMENT_TAG, new ShoppingFragment());
break;
case R.id.menu_settings:
switchFragment(SETTINGS_FRAGMENT_TAG, new SettingsFragment());
break;
}
}
#Override
public void onMenuItemReselect(#IdRes int i, int i1, boolean b) {
//TODO Add reselect code
}
protected void switchFragment(String fragTag, Fragment frag) {
// Sets a reference of current fragments Tag
ma.tag[0] = fragTag;
if(ma.tag[0]== LOADING_FRAGMENT_TAG){
//load LOADING_FRAGMENT but DONT add to backstack
ma.fragmentManager.beginTransaction().add(R.id.contentContainer, frag, ma.tag[0]).commit();
}else {
//Add every other fragment to backstack
ma.fragmentManager.beginTransaction().add(R.id.contentContainer, frag, ma.tag[0]).addToBackStack(ma.tag[0]).commit();
}
}
};
class CustomBackStackChangeListener implements FragmentManager.OnBackStackChangedListener{
private MainActivity ma;
CustomBackStackChangeListener(MainActivity main){
ma = main;
}
#Override
public void onBackStackChanged() {
//If BackStackChanged is triggered due to anything other than pressing the back button, return.
if(!ma.backButtonPressed){
return;
}
ma.bottomBarUpdate();
}
}
IMPROVED CODE DEMONSTRATION
(sorry, adding code in the comments is horrible so I'll do it here)
protected void switchFragment(String fragTag, Fragment frag) {
// Sets a reference of current fragments Tag
ma.tag[0] = fragTag;
if(ma.tag[0]== LOADING_FRAGMENT_TAG){
//load LOADING_FRAGMENT but DONT add to backstack
ma.fragmentManager.beginTransaction().add(R.id.contentContainer, frag, ma.tag[0]).commit();
}else {
Fragment fragment = ma.getSupportFragmentManager().findFragmentByTag(LOADING_FRAGMENT_TAG);
if(fragment != null && fragment.isVisible()){
ma.fragmentManager.beginTransaction().remove(fragment);
}
//Add every other fragment to backstack
ma.fragmentManager.beginTransaction().add(R.id.contentContainer, frag, ma.tag[0]).addToBackStack(ma.tag[0]).commit();
}
}
whenever you are switching to another fragment from that fragment which you do not want too include in the backstack you can finish that fragment first before switching.
That can be done by declaring the stating object of the fragment and giving that object its instance.
then where you are switching the fragment check with the help of the fragment name if its static object is null or not.
if its not null finish the fragment
Related
I have an Android app with a SlideMenu.
This is most of the MainActivity where I have the sideMenu:
public class MainActivity extends ActionBarActivity
{
private ActionBarDrawerToggle sideMenuToggle;
private DrawerLayout sideMenuLayout;
private SideMenuAdapter mAdapter;
...
private class DrawerItemClickListener implements ListView.OnItemClickListener
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
Bundle data = new Bundle();
data.putString("title", options.get(posit).getString("visual_name"));
if(posit == Constants.INDEX_USERNAME)
goToView(ViewManager.EDITPROFILE, data);
else if((posit >= Constants.INDEX_SECTION_1) && (posit <= Constants.INDEX_SECTION_5))
{
Bundle category = options.get(posit);
data.putString("title", category.getString("visual_name"));
data.putString("visual_name", category.getString("visual_name"));
data.putString("name", category.getString("name"));
data.putInt("photo", category.getInt("photo"));
data.putInt("ico", category.getInt("cat_ico"));
data.putBoolean("is_root", true);
goToView(ViewManager.CATEGORYFRAGMENT, data);
}
}
}
public Fragment goToView(ViewInfo _viewInfo, Bundle bundle)
{
try
{
if((this != null) && !this.isFinishing())
{
ViewManager myVMgr = viewMgr;
return myVMgr.show(_viewInfo, bundle, false);
}
} catch (IllegalStateException e) {
}
return null;
}
}
And this is most of the code of the ViewManager where I switch between sections (they are fragments):
public class ViewManager
{
private Activity context = null;
...
public Fragment show(ViewInfo _newFragInfo, Bundle _data, boolean back)
{
Fragment currentFragment = null;
Fragment newFragment = null;
final FragmentManager fm = context.getFragmentManager();
ViewInfo _lastFragInfo = lastViewData != null ? lastViewData.getViewInfo() : null;
// In this app we must support changing between same fragment class
//if((_lastFragInfo != null) && _newFragInfo.getIdView().equalsIgnoreCase(_lastFragInfo.getIdView())) {
// return null;
//}
FragmentTransaction ft = fm.beginTransaction();
if(_newFragInfo.getIsRoot())
{
Iterator<ViewData> iter = viewStack.iterator();
ViewData viewData;
while (iter.hasNext())
{
viewData = iter.next();
if(!viewData.getViewInfo().getIsRoot())
{
currentFragment = fm.findFragmentByTag(viewData.getViewInfo().getIdView());
if (currentFragment != null)
{
ft.remove(currentFragment);
}
}
iter.remove();
}
}
// Hide current fragment
if (_lastFragInfo != null)
{
currentFragment = fm.findFragmentByTag(_lastFragInfo.getIdView());
if (currentFragment != null)
{
if(!back)
ft.detach(currentFragment);
else
ft.remove(currentFragment);
}
}
// Show new fragment
if (_newFragInfo != null)
{
if(_newFragInfo.getIsRoot() || back) // only tabs are reusable fragment
newFragment = fm.findFragmentByTag(_newFragInfo.getIdView());
if (newFragment == null)
{
newFragment = Fragment.instantiate(context, _newFragInfo.getClaseView().getName());
ft.add(R.id.frame_content, newFragment, _newFragInfo.getIdView());
}
else
{
ft.attach(newFragment);
}
if(_data == null)
_data = new Bundle(1);
if(!_data.containsKey("title"))
_data.putString("title", context.getResources().getString(_newFragInfo.getTitle()));
if(!_newFragInfo.getIsRoot() && !back)
{
viewStack.add(lastViewData);
}
((BaseFragment)newFragment).setData(_data);
}
lastViewData = new ViewData(_newFragInfo, _data);
ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
ft.commitAllowingStateLoss();
fm.executePendingTransactions();
return newFragment;
}
}
In the sideMenu, "Edit profile" option has its own Fragment and Section 1 to Section 5 use the same fragment but I load a different content.
This is working well so far and when the user opens the sidemenu and changes between sections they are loaded correctly.
But now I've uploaded my code to use target sdk 26 and it doesn't compile because ActionBarActivity is no longer available so I've made these changes:
public class MainActivity extends ActionBarActivity
to this
public class MainActivity extends AppCompatActivity
and inside ViewManager
private Activity context = null;
to this
private AppCompatActivity context = null;
This now compiles but when I change between sections the data of the new section is no loaded and I keep watching the content of the previous section.
Some examples:
1)
1.1) I select Section1 (I see the content of Section1)
1.2) I select Edit Profile (I see the content of Edit profile)
1.3) I select Section3 (I see the content of Section3)
2)
2.1) I select Section1 (I see the content of Section1)
2.2) I select Section3 (I don't see the content of Section3 but the content of Section1).
Checking this I've seen that the problem is because with AppCompatActivity, onCreate, onCreateView and onResume methods of my CategoryFragment are called in steps 1.1, 1.3 and 2.1 but not in step 2.2.
Am I missing something? Should I make a new change so the Fragments transactions are made correctly with AppCompatActivity?
-- EDIT --
The setData method of the Fragment is this:
public void setData(Bundle _newdata)
{
data = _newdata;
}
I've thought to change it to this:
public void setData(Bundle _newdata)
{
data = _newdata;
// Some code to force the refresh/reloading of the Fragment
}
But I'm not sure if this is a tricky way to fix this, and by the way, I don't know how to force a fragment to reload itself.
I have tried every post in StackOverflow and have not been successful, i have a FragmentTabHost activity with tabs A B C D E
When i go to tab A and then go to tab B everything is ok, but if i return to tab A is blank, then return to tab B is also blank!!
A -> B -> A = Blank -> B = blank
I followed this post to get it working Dynamically changing the fragments inside a fragment tab host?, but the transition between tabs is not working.
I have tried changing my BaseContainerFragment to use getSupportFragmentManager instead of getChildFragmentManager but was unsuccessful, also removing addToBackStack(null) at this point im out of ideas, any help here will be appreciated, thanks.
This is the mainActivity that contain code for creating tabs using fragment.
public class ActivityMain extends FragmentActivity {
public static final String TAB_1_TAG = "tab_1";
public static final String TAB_2_TAG = "tab_2";
public static final String TAB_3_TAG = "tab_3";
public static final String TAB_4_TAG = "tab_4";
public static final String TAB_5_TAG = "tab_5";
private FragmentTabHost mTabHost;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.getTabWidget().setDividerDrawable(null);
mTabHost.getTabWidget().setStripEnabled(false);
mTabHost.addTab(mTabHost.newTabSpec(TAB_1_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_account)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_2_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_discounts)), FragmentPromotionsContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_3_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_payment)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_4_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_gas)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_5_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_rest)), FragmentAccountContainer.class, null);
}
#Override
public void onBackPressed() {
boolean isPopFragment = false;
String currentTabTag = mTabHost.getCurrentTabTag();
Log.e("ActivityMain", "currentTabTag: " + currentTabTag);
if (currentTabTag.equals(TAB_1_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_1_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_2_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_2_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_3_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_3_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_4_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_4_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_5_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_5_TAG)).popFragment();
}
Log.e("ActivityMain", "isPopFragment: " + isPopFragment);
if (!isPopFragment) {
finish();
}
}
}
This is my BaseContainerFragment that allows backtracking and replacment of fragments
public class BaseContainerFragment extends Fragment {
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.replace(R.id.container_framelayout, fragment);
transaction.commit();
getChildFragmentManager().executePendingTransactions();
}
public boolean popFragment() {
Log.e("test", "pop fragment: " + getChildFragmentManager().getBackStackEntryCount());
boolean isPop = false;
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
isPop = true;
getChildFragmentManager().popBackStack();
}
return isPop;
}
}
This is container for the first Tab (this tab holds 2 activities, one is main, and another is called on listview Click)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
new asyncGetFeedClass(context).execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_cardholder, container, false);
toolbar = (Toolbar) v.findViewById(R.id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("TARJETAS");
list = (ListView) v.findViewById(R.id.list);
// Click event for single list row
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
FragmentAccount fragment = new FragmentAccount();
// if U need to pass some data
Bundle bundle = new Bundle();
if (listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID) != null) {
bundle.putString("idBankAccount", listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID));
bundle.putString("idGiftCard", "0");
} else if (listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID) != null) {
bundle.putString("idGiftCard", listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID));
bundle.putString("idBankAccount", "0");
} else {
bundle.putString("idBankAccount", "0");
bundle.putString("idGiftCard", "0");
}
fragment.setArguments(bundle);
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, false);
}
});
return v;
}
The main class for Tab #1
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
Bundle bundle = this.getArguments();
idBankAccount = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_BANKACCOUNTS_ID, "0"));
idGiftCard = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_GIFTCARDS_ID, "0"));
if(idBankAccount > 0){
new asyncGetBankTransactions(context).execute();
} else if(idGiftCard > 0) {
new asyncGetGiftCardTransactions(context).execute();
} else {
new asyncGetX111Transactions(context).execute();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_account, container, false);
toolbar = (Toolbar) v.findViewById(id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("MI CUENTA");
toolbar.setNavigationIcon(R.drawable.icon_user);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
goToCards();
}
});
layoutAccount = (LinearLayout) v.findViewById(id.layoutAccount);
layoutGetCredit = (LinearLayout) v.findViewById(id.layoutGetCredit);
layoutTransactions = (LinearLayout) v.findViewById(id.layoutTransactions);
btnAccount = (Button) v.findViewById(id.btnMyBalance);
btnGetCredit = (Button) v.findViewById(id.btnGetCredit);
btnSendCredit = (Button) v.findViewById(id.btnSendCredit);
btnTransactions = (Button) v.findViewById(id.btnTransactions);
list = (ListView) v.findViewById(id.list);
btnTransactions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.VISIBLE);
}
});
btnGetCredit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.VISIBLE);
layoutTransactions.setVisibility(View.GONE);
}
});
btnAccount.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.VISIBLE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.GONE);
}
});
return v;
}
private void goToCards() {
FragmentCardHolder fragment = new FragmentCardHolder();
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, true);
}
I think the problem is in hidden part of code where you add first fragment to container (FragmentAccountContainer and FragmentPromotionsContainer classes). I suggest you to create abstract method in BaseContainerFragment.class with signature by example
protected abstract Fragment getFirstFragment();
So concrete container class will override this method and return new instance of a first fragment to super class and then in parent class add it to fragment container with using add transaction.
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
addFragment(getFirstFragment(), false);
}
}
Note you should check if savedInstanceState is null before adding fragment to avoid dublicates in case activity recreation by system.
In nested fragments you could use replace like you did it ((BaseContainerFragment) getParentFragment()).replaceFragment(___, true);
Also i have a few suggestions for you code. You couldn't just avoid overriding onBackPressed in activity like #NecipAllef suggests, because of known bug with default back logic and child fragment manager , but you could simplify call to popFragment like
#Override
public void onBackPressed() {
String currentTabTag = mTabHost.getCurrentTabTag();
boolean isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(currentTabTag)).popFragment();
if (!isPopFragment) {
super.onBackPressed();
}
}
And for setting bundles to fragment i suggest use fabric method pattern, like
public class TestFragment extends Fragment {
public static Fragment newInstance(String text){
Fragment fragment = new TestFragment();
Bundle args = new Bundle();
args.putString("text", text);
fragment.setArguments(args);
return fragment;
}
}
Ps: i created for you a simple project with described logic
Why are you keeping track of Fragments and popping them by yourself? You don't need to do that, and you shouldn't override onBackPressed(). Let FragmentManager handle the fragment transactions.
If you have fragments inside an activity, use
FragmentManager fManager = getFragmentManager();
or if you want to support devices prior to Android 3.0, use
FragmentManager fManager = getSupportFragmentManager();
if fragments are inside another fragment, then use
FragmentManager fManager = getChildFragmentManager();
After you have fManager, to show a fragment, use
fManager.beginTransaction().add(R.id.fragment_parent, new FirstTabFragment()).commit();
where fragment_parent is the parent view which you want to place your fragments.
When you want to switch to next fragment, use
fManager.beginTransaction().replace(R.id.fragment_parent, new SecondTabFragment())
.addToBackStack(null)
.commit();
Since you add it to back stack, you will see your first fragment when you press back. That's it.
Moreover, as you can easily realize this will cause your fragments to be created from scratch every time, you can prevent this by initializing them once and reuse them.
HTH
How to return a value from Asynctask (Different class) to activity back from which you called the Asynctask , here i have followed the intsruction given in following link How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class? #HelmiB's
I have done everything and its returning the result to activity's method processFinish() also , the problem is lost the activity control or focus, i could not do further actions using the Asynctask's result, as because all my activity's members becomes null.
How to proceed ?
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (pDialog != null)
{
pDialog.cancel();
}
if (StringUtil.hasValue(responseXmlString))
{
if (Integer.parseInt(AppUtil.getXpathValue("Result/ErrorNo",AppUtil.buildDocument(responseXmlString))) == 0)
{
asyncResponse.processFinish(responseXmlString);
}
}
#Override
public void processFinish(Object output)
{
Log.d("Response From Asynchronous task:", (String) output);
displayView(position,(String) output);
}
private void displayView(int position,String responseXml)
{
// update the main content by replacing fragments
boolean isFragment = true;
Fragment fragment = null;
/*//For Handling back press
if (getIntent().getBooleanExtra("FromPassBook", false) )
{
getIntent().putExtra("FromPassBook", false);
position = 7;
}
if (getIntent().getBooleanExtra("toCustomerAcocunts", false) )
{
getIntent().putExtra("toCustomerAcocunts", false);
position = 1;
}*/
switch (position)
{
case 0:
fragment = new ChartFragment();
setTitle(getResources().getString(R.string.wealth));
break;
default:
break;
}
if (fragment != null && isFragment)
{
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft =fragmentManager.beginTransaction();
fragmentStack.push(fragment);
//passing data to fragment
if (StringUtil.hasValue(responseXml))
{
Bundle bundle = new Bundle();
bundle.putString("responseXml", responseXml);
fragment.setArguments(bundle);
}
ft.replace(R.id.frame_container, fragment).commit();
// update selected item and title, then close the drawer
listView.setItemChecked(position, true);
listView.setSelection(position);
mDrawerLayout.closeDrawer(listView);
}
else
{
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
private void callService(int position)
{
String input = "";
AsyncCallWS asyncCallWS;
switch (position)
{
case 0:
input = "<Parm><ProcessID>101</ProcessID><MobileNo>" + mobileNumber + "</MobileNo></Parm>";
asyncCallWS = new AsyncCallWS(MainActivity.this, input, position,new MainActivity());
asyncCallWS.execute();
break;
}
Asynctask class constructor
`AsyncResponse asyncResponse;
public AsyncCallWS(Context context,String input,int position,AsyncResponse response )
{
this.context = context;
this.inputToservice = input;
this.position = position;
asyncResponse = response;
}`
Well, your problem is in this line of code:
asyncCallWS = new AsyncCallWS(MainActivity.this, input, position,new MainActivity());
Or, particularly, in the statement new MainActivity(). You create a new instance of MainActivity class here and then you use it as a callback. Obviously, it will have all the fields non-initialized. Use MainActivity.this instead of new MainActivity(). And please remember that Android manages all of your activities. You never want to create one yourself.
Hi i have a listview sidebar and i am displaying fragments based on user selection in listview.
This is how i am replacing fragments
public void switchFragment(Fragment fragment, boolean addBackStack) {
try {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
currentFragment = fragment;
//if (addBackStack)
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
}
}
This is my sample fragment code.Now when i replace fragments i am saving instance state in onpause and restoring it in onresume but it only works when i press back button. When i manually navigate back to fragment from listview ,fragment state is not restored.Why?
public class Fragment1 extends BaseFragment {
int currentFragmentInd = 1;
private Button startButton;
private Button endButton;
private long savedStartTime;
private TextView setStartText;
private TextView setEndText;
private String starttime;
private String endtime;
public int getIndex() {
MyApplication.getApplication().setCurrentChild(0);
MyApplication.getApplication().setCurrentGroup(0);
return currentFragmentInd;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState !=null)
{
}
}
#Override
public void onResume() {
super.onResume();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
setEndText.setText(endtime);
setStartText.setText(starttime);
}
#Override
public void onPause() {
super.onPause();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
starttime=setStartText.getText().toString();
endtime=setEndText.getText().toString();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
FrameLayout frameLayout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.layout1, null, false);
((MainActivity) getActivity()).openList(0, 0);
if (savedInstanceState == null) {
}
startButton= (Button) contentView.findViewById(R.id.button);
endButton= (Button) contentView.findViewById(R.id.button2);
endButton.setEnabled(false);
setStartText= (TextView)contentView.findViewById(R.id.MAtextView2);
setEndText= (TextView)contentView.findViewById(R.id.MAtextView3);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
}
});
endButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
setEndText.setText(now.hour+" : "+now.minute);
}
});
return contentView;
}
}
Late replay but might help somebody else.
This happens because when you click a listview item you create a new inctance of that fragment.
"I assume the fragment you send to switchFragment(Fragment fragment), is created using a 'new' keyword."
Therefore this new instance of a fragment doesnt hold your old data.
This is how I solved this. There are probably better ways, but since nobody replied, I will give my solution.
When you replace the fragment (ft.replace, fragment), give a string reference to that transaction: -ft.replace(R.id.content, fragment, "FRAGMENT_NAME");
When you add the fragment to the backstack with addToBackStack(null); put the name of your fragment where you have null.: -ft.addToBackStack("FRAGMENT_NAME");
Create a method which tells you if that fragment has already been created, and therefore exists in the back stack.:
public boolean isTagInBackStack(String tag){
Log.i(TAG, "isTagInBackStack() Start");
int x;
boolean toReturn = false;
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
Log.i(TAG, "backStackCount = " + backStackCount);
for (x = 0; x < backStackCount; x++){
Log.i(TAG, "Iter = " + x +" "+ getSupportFragmentManager().getBackStackEntryAt(x).getName());
if (tag == getSupportFragmentManager().getBackStackEntryAt(x).getName()){
toReturn = true;
}
}
Log.i(TAG, "isTagInBackStack() End, toReturn = " + toReturn);
return toReturn;
}
Now before you create a new instance of that fragment check in the backstack if a backstack item named "FRAGMENT_NAME" exists.
if it exists, use that item (fragment) instead of creating a new one.
if (isTagInBackStack("FRAGMENT_NAME")){
Log.i(TAG, "Tag is in BackStack!!!! frag is = " + getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity_container, getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
transaction.addToBackStack("FRAGMENT_NAME");
transaction.commit();
}else{
Create the fragment (this happens the first time.
}
I have a listview in one of my fragments and it empties when I leave that fragment.
Why is it happening?
That fragment:
public class ListActivity extends ListFragment {
public void ToastLoadShout(String msg) {
Toast.makeText(getActivity(), msg.toString(), Toast.LENGTH_LONG).show();
}
private static View View;
HttpClient client;
HttpPost httppost;
HttpGet httpget;
JSONObject json;
List<List<String>> items;
List<item> markers = new ArrayList<item>();
MobileArrayAdapter adapter;
ListView list;
ProgressBar listload;
Button relist;
Preferences pref;
String datadata = "";
String savedlat="0.0";
String savedlon="0.0";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_list, container, false);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
try {
pref = new Preferences(getActivity());
list = (ListView) getView().findViewById(android.R.id.list);
listload = (ProgressBar) getView().findViewById(R.id.listload);
HashMap<String, String> loc = pref.getData();
ToastLoadShout(loc.get(Preferences.LAT) + ","
+ loc.get(Preferences.LON));
if (loc.get(Preferences.LAT) != "0.0" && loc.get(Preferences.LAT) != null)
{
//adapter.deleteList();
//list.destroyDrawingCache();
if (loc.get(Preferences.LAT) != savedlat && loc.get(Preferences.LON)!=savedlon){
new Load().execute();
savedlat=loc.get(Preferences.LAT);
savedlon=loc.get(Preferences.LON);
}
}
else
ToastLoadShout("Get Location First.");
relist = (Button) getView().findViewById(R.id.relist);
relist.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
listload.setVisibility(View.INVISIBLE);
HashMap<String, String> loc = pref.getData();
ToastLoadShout(loc.get(Preferences.LAT) + ","
+ loc.get(Preferences.LON));
if (loc.get(Preferences.LAT) != "0.0" && loc.get(Preferences.LAT) != null){
adapter.deleteList();
list.destroyDrawingCache();
new Load().execute();}
else
ToastLoadShout("Get Location First.");
}});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// get selected items
//String selectedValue = (String) getListAdapter().getItem(position);
String selectedValue = markers.get(position).getTitle();
Toast.makeText(getActivity(), selectedValue, Toast.LENGTH_SHORT).show();
}
}
And the MainActivity which holds the fragments:
public class Fragments extends FragmentActivity {
Fragment newFragment;
Button Add;
public void ToastLoadShout(String msg) {
Toast.makeText(this, msg.toString(), Toast.LENGTH_SHORT).show();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments);
//Set Custom actionBar<
getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getActionBar().setCustomView(R.layout.titlebar);
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
//Set Custom actionBar>
ListActivity fragment = new ListActivity();
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_place, fragment,"Nearby");
transaction.commit();
turnGPSOn();
Add = (Button)findViewById(R.id.add);
Add.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent Intent = new Intent(Fragments.this,Add.class);
Bundle bndlanimation =
ActivityOptions.makeCustomAnimation(getApplicationContext(),
R.anim.animation,R.anim.animation2).toBundle();
startActivity(Intent, bndlanimation);
}
});
/* For putting commas in attractin's checkIns
String number = "1345";
int amount = Integer.parseInt(number);
DecimalFormat formatter = new DecimalFormat("#,###");
ToastLoadShout(formatter.format(amount));*/
}
public void onSelectFragment(View view) {
String fragTag="";
boolean needNew=false;
if (view == findViewById(R.id.map))
{
Fragment f = getSupportFragmentManager().findFragmentByTag("Map");
if (f==null){
newFragment = new MainActivity();
needNew=true;
fragTag="Map";
}
else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_place, f, "Map"); //or whatever other string you want to use
transaction.addToBackStack(null);
transaction.commit();
}
}
else if (view == findViewById(R.id.nearby))
{
Fragment f = getSupportFragmentManager().findFragmentByTag("Nearby");
if (f==null){
newFragment = new ListActivity();
needNew=true;
fragTag="Nearby";
}
else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_place, f, "Nearby"); //or whatever other string you want to use
transaction.addToBackStack(null);
transaction.commit();
}
}
if (needNew) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_place, newFragment, fragTag);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
Already tried loading the list on OnCreate. Doesn't work at all.
Thanks for your assistance.
EDIT:
Load.Class:
class Load extends AsyncTask<String, Integer, Boolean> {
#Override
protected void onPreExecute() {
listload.setVisibility(View.VISIBLE);
}
#Override
protected Boolean doInBackground(String... params) {
try {
items = DownloadList();
if (items != null)
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
#Override
protected void onPostExecute(Boolean res) {
// TODO Auto-generated method stub
if (res) {
ArrangePutMarkers();
adapter=new MobileArrayAdapter(getActivity(), markers);
list.setAdapter(adapter);
} else {
ToastLoadShout("Error");
ToastLoadShout(datadata);
}
listload.setVisibility(View.INVISIBLE);
}
}
As i can see you recreate the loc object every time the fragment is displayed.
It obviously makes you fragment to reload the data.
Keep your adapter in activity as a field. Destroying everything in your fragment when you leave it is a normal thing. setRetainInstance() works only on configuration changes, such as screen rotating. Recreate your list every time you came back in your fragment (in onActivityCreated(), for example, like you do know) and supply it with your stored adapter with saved data in it. You can gain access to your activity inside a fragment, for example, by casting (MainActivity)getActivity(), as an Activity which is passed to this fragment in this case is actually your activity.