How to pass data from Activity to Fragment using bundle - android

I am completely new to Android and I have to submit this app to end my college degree, but I am having this problem where my fragment crashes my entire app.
To explain what I have done up until now is, I have a LoginActivity where I sent the userId through the Intent and have the id of the current user on my DashboardActivity (and I can display it), but in the DashboardActivity I have a bottom navigation bar that navigates to my FormFragment and my DataFragment.
Right now, want I would love to pass the userId value of the current user from the DashboardActivity to my DataFragment, so that I can display dynamically the user data according to the userId.
So with it, I found that the best option is to use bundle, but I don't now why (because I am completely new to this) my app crashes every time I switch from my FormFragment to my DataFragment.
Can you help me? I am desperate xD
This is my DashboardActivity code:
public class PainelActivity extends AppCompatActivity {
private Button buttonLogout;
private TextView textViewId;
private Object DashboardFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_painel);
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_dashboard)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
// gets the id from the Intent
Intent get = getIntent();
String userId = get.getStringExtra(LoginActivity.EXTRA_ID);
// Send the id to the Fragments
Bundle bundle = new Bundle();
bundle.putString("userId", userId);
Fragment fragment = new Fragment();
fragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.add(R.id.nav_host_fragment, fragment).commit();
// see the id on the screen
textViewId = findViewById(R.id.textViewId);
textViewId.setText(userId);
// logout
buttonLogout = findViewById(R.id.logoutButton);
buttonLogout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openMainActivity();
}
});
}
public void openMainActivity() {
Intent HomePage = new Intent(this, MainActivity.class);
startActivity(HomePage);
Toast.makeText(PainelActivity.this, "Terminou a sua sessão.",
Toast.LENGTH_LONG).show();
}
}
And this is my DataFragment code:
public class DashboardFragment extends Fragment {
INodeJS myAPI;
private TextView textViewResult;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
//Init API
Retrofit retrofit = RetrofitClient.getInstance();
myAPI = retrofit.create(INodeJS.class);
textViewResult = root.findViewById(R.id.text_view_result);
// gets the id from the activity
if (getArguments() != null) {
String userId = getArguments().getString("userId");
int uid = Integer.parseInt(userId);
Call<List<DataResult>> call = myAPI.executeGetData(uid);
call.enqueue(new Callback<List<DataResult>>() {
#Override
public void onResponse(Call<List<DataResult>> call, Response<List<DataResult>> response) {
if (response.code() == 200) {
List<DataResult> DATA = response.body();
for (DataResult data: DATA) {
String content = "";
content += "Data: " +data.getDta() + "\n";
content += "Hora: " +data.getHora() + "\n";
content += "Glicémia: " +data.getIndiceGlicemia() + "\n";
content += "Insulina: " +data.getInsulina() + "\n";
content += "Medicação: " +data.getMedicacao() + "\n\n";
textViewResult.append(content);
}
}
}
#Override
public void onFailure(Call<List<DataResult>> call, Throwable t) {
textViewResult.setText(t.getMessage());
}
});
}
return root;
}
}
Thank you and have a nice day!
(I just edited my code and updated my question, right now, my app doesn't crash but I don't see the data.)

While creating bundle:
Bundle bundle = new Bundle();
bundle.putString("userId", userId);
Fragment fragment = new Fragment();
fragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_placeholder_id, dataFragment, "anyTagName").commit();
The To get the data in your fragment:
if (getArguments != null) {
String userId = getArguments().getString("userId");
}

When you create a new fragment you have an auto-generated function called newInstance.
So what you need to do is:
public static MyFragment newInstance(String param1, String param2) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString("ARG_NAME", param1);
args.putString("OTHER_ARG_NAME", param2);
fragment.setArguments(args);
return fragment;
}
Then, in your activity:
String str1 = "foo";
String str2 = "bar";
MyFragment myFragment = MyFragment.newInstance(str1, str2);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction()
.replace(R.id.nameOfActivityLayout, myFragment);
transaction.addToBackStack(null); // Add this line if you want to add the fragment to the back-stack
transaction.commit();
And back to your fragment, in your onCreate method :
String str1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
str1 = getArguments().getString("ARG_NAME");
}
}
The variable str1 will now have the value "foo" and you're free to use it in your fragment. Of course you can put other types in the bundle, like integers, booleans, arrays etc.

You are using the jetpack navigation component, so its easier than before.
You just have to pass the bundle to the navigation controller
Do:
navController.setGraph(R.navigation.graph, YOUR_BUNDLE);
then in your start fragment:
Bundle b = getArguement();
String id = b.Get string("I'd");

Related

changing fragment's textview from activity does not update UI

I am attempting to do something that seems common practive (f.i. here, second answer). But while the data is transmitted and can be put f.i. into a viewmodel, android does not seem to care that I have changed the text of a textview. This is my code (I prefer databinding over findviewbyid):
Activity:
#Override
public void onItemSelected(String param) {
MainFragment oFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragmentTag);
if(oFragment != null) {
oFragment.SetText(param);
getSupportFragmentManager()
.beginTransaction()
.replace(oBinding.mainContainer.getId(), oFragment)
.addToBackStack(null)
.commit();
}
}
Receiving Fragment:
public void SetText(String param) {
String sInput = oBinding.MyInputField.getText().toString();
oBinding.TextviewIWantToChange.setText(param);
Entry oEntry = Manager.CreateEntry(sInput, param);
viewmodel.Insert(oEntry);
}
The old fragment instance shows up, the right param is transmitted and viewmodel insertion works smoothely. But the textview is not updated. Any ideas?
Pass your data with Fragment arguments and read another fragment getArguments
#Override
public void onItemSelected(String param) {
MainFragment oFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragmentTag);
if(oFragment != null) {
Bundle bundle=new Bundle();
bundle.putString("param",param);
oFragment.setArguments(bundle);
getSupportFragmentManager()
.beginTransaction()
.replace(oBinding.mainContainer.getId(), oFragment)
.addToBackStack(null)
.commit();
}
}
// Call this on which fragment you have need
public void SetText(String param) {
String sInput = oBinding.MyInputField.getText().toString();
oBinding.TextviewIWantToChange.setText(param);
Entry oEntry = Manager.CreateEntry(sInput, param);
viewmodel.Insert(oEntry);
}
Call thi methhod on OnCreateView
final Bundle bundle = getArguments();
String param = bundle.getString("param");

Xamarin.android - Pass data to my fragment

i have a List with my services, and on item selected i'm passing service type to my activity ServiceDetail like this:
ServiceActivity
void item_selected(object sender, AdapterView.ItemClickEventArgs e) {
MenuContentItem selectedItem = (MenuContentItem)item[e.Position];
if(selectedItem.Title == "COLLO") {
var activity_go = new Intent(this, typeof(ServiceDetailActivity));
activity_go.PutExtra("service_type", "Collo");
StartActivity(activity_go);
}
if (selectedItem.Title == "SPALLA") {
var activity_go = new Intent(this, typeof(ServiceDetailActivity));
activity_go.PutExtra("service_type", "Spalla");
StartActivity(activity_go);
}
}
ServiceDetailActivity
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ServiceDetail);
//enable navigation mode to support tab layout
this.ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
AddTab("Introduzione", Resource.Mipmap.Icon, new IntroduzioneFragment());
//intent data
string text = Intent.GetStringExtra("service_type") ?? "Data not available";
IntroduzioneFragment fragment = new IntroduzioneFragment();
// set data to pass to my fragment
Bundle bundle = new Bundle();
bundle.PutString("text", text);
fragment.Arguments = bundle;
}
// MY FRAGMENT - I would like "CUSTOM" my fragment "IntroduzioneFragment" like this:
class IntroduzioneFragment : Fragment {
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Menu.Tab, container, false);
var sampleTextView = view.FindViewById<TextView>(Resource.Id.textView);
var imageView = view.FindViewById<ImageView>(Resource.Id.image_view);
imageView.SetImageResource(Resource.Mipmap.slide1);
// Get type of service
var test = Arguments.GetString("text");
if (test == "Collo") {
sampleTextView.Text = "is collooooo";
} else {
sampleTextView.Text = "is not collo";
}
return view;
}
}
I don't want create one activity for each service, i would like just have one "Service activity detail" and custom text and image by service type.
ERROR: when I select item service:
System.NullReferenceException - Object reference not set to an instance of an object. on:
var test = Arguments.GetString("text");
You have two ways of doing that.
If that is the activity that holds the fragment, you can call
this.Activity
inside fragment and just call any method of the activity after casting
AwesomceActivty castetActivity = (AwesomeActivity)this.Activity;
castetActivity.AwesomeMethod(12);
Or you can do that by using Delegates:
Define delegates in your Fragment class
namespace Awesome.Android {
public class AwesomeFragment : Fragment {
public delegate void OnAwesomePress (int number);
public event OnAwesomePress sendOnAwesomePressEvent;
}
}
You can assign it when you create a Framgent
AwesomeFragment fragment = new AwesomeFragment ();
fragment.OnAwesomePress += OnAwesomePress;
After that, you implement OnAwesomePress in your activity
private void OnAwesomePress (int number) {
}
Now, when you call sendOnAwesomePressEvent in your Fragment, that event will be passed to Activity.
sendOnAwesomePressEvent (10);

Fragment showing on back button press, even though not added to backstack

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

Set Fragment EditText from the parent Activty

I have a ProfileFragment class which contains two setters:
public void setPseudo(String pseudo){
textPseudo.setText(pseudo);
}
public void setEmail(String email){
textEmail.setText(email);
}
And in my Activity I would like to call these functions:
user = new ProfileFragment();
if (intent != null) {
user.setPseudo(intent.getStringExtra(USER_PSEUDO));
user.setEmail(intent.getStringExtra(USER_EMAIL));
}
It says "can't resolve method...".
Does it mean I can't do this?
Are you sure you don't have a Profile class with setters? Not a Fragment?
Fragments generally don't use setters, they use arguments.
Reason being: If you call setEmail, and then you called to some view setText within the new Fragment, you get a NullPointerException because that TextView was never initialized
Fragment profileFragment = new ProfileFragment();
Bundle args = new Bundle();
if (intent != null) {
args.putAll(intent.getExtras());
}
profileFragment.setArguments(args);
// Show new Fragment
getSupportFragmentManager()
.replace(R.id.content, profileFragment)
.commit();
And inside your Fragment's onCreateView, you can now use this, for example
final Bundle args = getArguments();
String pseudo = "";
if (args != null) {
pseudo = args.getString(YourActivity.USER_PSEUDO);
}
textPseudo.setText(pseudo);

FragmentTabHost getting empty fragments after popBackStack

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

Categories

Resources