2017-06-14 29 views
0

我有一個應用程序,其中MainActiviy與在全屏幕片段之間切換的底部導航一起工作。片段顯示在後退按鈕上,儘管沒有添加到後臺堆棧

我學會了通過確保在創建時將每個片段添加到後臺堆棧來控制後退按鈕導航。

fragmentManager.beginTransaction().add(R.id.contentContainer, fragment, fragment_tag).addToBackStack(fragment_tag).commit(); 

有一種類型的片段,加載屏幕片段,我不想加入到堆棧中,所以我創建片段時排除addToBackStack()方法。

如下圖所示,不知何故,即使按下後退按鈕,仍然會出現加載片段,即使它不在背後(我已經用調試器確認過)。

Demo of loading fragment appearing even though not on backstack

如果任何人都可以給我搞清楚爲什麼它顯示出來,我會非常感激一隻手,它已經困擾我一個星期左右,我出出主意!

下面是代碼:

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(); 
    } 
} 

改進的代碼演示 (很抱歉,在留言中加入代碼是可怕的,所以我會在這裏做)

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(); 
    } 
} 

回答

0

每當你切換到來自該片段的另一個片段(您不想要的片段)包括在後臺堆棧中,您可以在切換之前先完成該片段。 這可以通過聲明片段的聲明對象併爲該對象提供實例來完成。 然後,如果您使用片段名稱的幫助來切換片段檢查,那麼它的靜態對象是否爲空。 如果它沒有完成片段

+0

我不知道我明白你的意思。下面的代碼是否符合你所說的內容? –

+0

*對不起,我編輯的問題上面的代碼 –

相關問題