我有一个应用程序,其中MainActiviy使用底部导航,在全屏碎片之间切换。
我已经学会了通过确保在创建时将每个片段添加到backstack来控制后退按钮导航。
fragmentManager.beginTransaction().add(R.id.contentContainer, fragment, fragment_tag).addToBackStack(fragment_tag).commit();
有一种类型的片段,即加载屏幕片段,我不想将其添加到backstack中,因此在创建片段时我会排除addToBackStack()
方法。
如下面的gif所示,按下后退按钮仍然会出现加载片段,即使它不在后台上(我已经通过调试器确认了这一点)。
如果有人能帮我弄清楚它为什么会出现我真的很感激,它已经困扰了我一个星期而且我没有想法!
以下是代码:
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)
每当你切换到你不想要的那个片段中的另一个片段时,你也可以在切换之前完成该片段。 这可以通过声明片段的说明对象并将该对象作为其实例来完成。 然后,如果它的静态对象为null,则在片段名称的帮助下切换片段检查。 如果它不为null则完成片段