I'm trying to add Fragments to a view. I have a String Array and I would like to create a new fragment for each one. Seems like a simple task.
String[] items = getResources().getStringArray(R.array.myArray);
for (int i = 0; i < items.length; i++) {
Bundle bundle = new Bundle();
bundle.putString("category", items[i]);
Fragment frag = new MyFragment();
frag.setArguments(bundle);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.mga_viewParentContainer, frag, "frag_" + i);
ft.commit();
Log.i ("FT commit called on item:", items [i]);
} // for loop
This creates a new fragment for every item in the array, however every fragment is getting the "category" bundle item from the last item in the array. So all fragments inflate the same info.
However, the Log reports all items as theyes should be.
Why is this happening?
Related
I have two fragments ListNav and SwipeNav in Navigation Drawer. ListNav shows list view of data from String array. SwipeNav shows data as swipe views of the same data from string array. I am able to replace fragment from ListNav to SwipeNav. String array data shows 'A to Z'.
My problem is when I click on list in ListNav (Suppose B)then SwipeNav(replaced Fragment) shows swipe views from start(means A). Suppose, if I click on 'D' in ListNav through Fragment replace, SwipeNav will show 'D'. Plz suggest me how I will implement this.
String Array:
<string-array name="tab_titles">
<item>A</item>
<item>B</item>
<item>C</item>
<item>D</item>
<item>E</item>
<item>F</item>
<item>G</item>
<item>H</item>
<item>I</item>
........
</string-array>
I ListNav I use below to replace to SwipeNav with OnItemClickListener:
FragmentManager fm=getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(((ViewGroup)(getView().getParent())).getId(), new SwipeNav());
ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
SwipeNav shows Data as Swipe Views through PagerAdapter from String Array:
tabTitlesArray = context.getResources().getStringArray(R.array.tab_titles);
I thing you are missing one step there and that is you need to send data in String form when swipe fragment is replacing .
Like.
Bundle bundle = new Bundle();
bundle.putString("TAG", "D");
// set Fragmentclass Arguments
SwipeNav fragobj = new SwipeNav();
fragobj.setArguments(bundle);
FragmentManager fm=getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(((ViewGroup)(getView().getParent())).getId(), fragobj);
ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
now on your swipe fragment make sure you are receiving that string that you are passing when creating .
so in onCreateViewmethod .
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String passingString = getArguments().getString("TAG");
return inflater.inflate(R.layout.fragment, container, false);
}
Here TAG just identifying what you are passing .
In my app, I have navigation bottom and container above navigation. I using multiple fragments in one container. When I click on an item of navigation bottom, I replace the fragment to show.
This is my code that shows fragment in the container
private void showFragment(String tabType) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
baseTabBottomFragment = new BaseTabBottomFragment();
//send data to fragment
Bundle bundle = new Bundle();
bundle.putString(KeyConstant.TAB_BOTTOM, tabType);
baseTabBottomFragment.setArguments(bundle);
ft.add(R.id.frame_container, baseTabBottomFragment, tabType);
ft.addToBackStack(tabType);
ft.commit();
}
But I don't know, how to handle when I click on the item navigation bottom, It's always loaded fragment again and again. I want to handle it. If fragment loaded, I don't want to load it again.
Please help me
Sorry for English grammar
1-You can play with that tag tabType e.g:
String prevTab = "";
private void showFragment(String tabType) {
if(!tabType.equal(prevTab)){
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
baseTabBottomFragment = new BaseTabBottomFragment();
//send data to fragment
Bundle bundle = new Bundle();
bundle.putString(KeyConstant.TAB_BOTTOM, tabType);
baseTabBottomFragment.setArguments(bundle);
ft.add(R.id.frame_container, baseTabBottomFragment, tabType);
ft.addToBackStack(tabType);
ft.commit();
}
prevTab = tabType;
}
2-Or from your BottomNavigationView you can check for selected item id, e.g:
if (bottomNav.getSelectedItemId() != R.id.someMenuId)
// replace the fragment
3- Or use onNavigationItemReselcted add from Api 26 to know if the current item got selected again or not.
In my app there's an Activity with three tabs, each of them contains a ListFragment.
The ListFragment must show different contents, but when I pass it through the bundle it gets overwritten and I get three fragments with the same content (the latest one)
Here's the code:
In the onCreate method of the Activity:
{...
Fragment fragment;
Bundle bundle= new Bundle();
for(int i = 0; i<CATEGORIES.size(); i++) {
fragment = new MyListFragment();
bundle.putSerializable("Data", data.get(i));
fragment.setArguments(bundle);
screens.put(CATEGORIES.get(i),fragment);
}
setUpViewPagerAndTabs();
...
... }
protected void setUpViewPagerAndTabs(){
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setOffscreenPageLimit(screens.size()+1);
mAdapter = new PageAdapter(screens, getSupportFragmentManager());
mViewPager.setAdapter(mAdapter);
tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setTabGravity(TabLayout.GRAVITY_FILL);
tabs.setTabMode(TabLayout.MODE_FIXED);
tabs.setupWithViewPager(mViewPager);
}
In the Fragment class this line produces the overwriting of the corresponding data:
ArrayList<String> names = (ArrayList<String>)
b.getSerializable(Activity.DATA);
even if I try to pass the value of the counter (i) to the fragment, in order to pick the right element of data in the fragment, it gets overwritten.
How can I resolve this?
Thank you in advance
Not sure if it is the case. However you are passing the reference to the same bundle (out of the scope) to all the three fragments. It might depend on when the serialization / deserialization takes effect, but if it happen after the loop you have three fragment pointing to the same content of the bundle (the last one).
Try to move the bundle creation inside the loop:
for(int i = 0; i<CATEGORIES.size(); i++) {
Bundle bundle= new Bundle();
fragment = new MyListFragment();
bundle.putSerializable("Data", data.get(i));
fragment.setArguments(bundle);
screens.put(CATEGORIES.get(i),fragment);
}
I am working on a Multipane app, where the left most pane is static and the right pane has dynamically added fragments.
This is the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:name="path.fragments.NavigationFragment"
android:id="#+id/navigation_fragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
tools:layout="#layout/navigation_layout" />
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent" />
</LinearLayout>
Whenever a list item is selected in the left pane, that contains the list of the users, I want to create a new fragment and add it to the container, and fill it with the messages from that user. The rest of my logic works great, but I am just having issues with the dynamically adding new fragments. updating the existing fragment before replacing it using the FragmentTransaction works. However, when I try the code below it doesn't work
DetailsFragment detailsFragment = new DetailsFragment();
Bundle args = new Bundle();
args.putInt(DetailsFragment.ARG_POSITION, position);
detailsFragment.setArguments(args);
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
trans.replace(R.id.fragment_container, detailsFragment);
trans.addToBackStack(null);
trans.commit();
Log.v(TAG, "updating details fragment with argument = " + position);
Cursor cursor = (Cursor) adapter.getItem(position);
Peer peer = new Peer(cursor);
String[] from = new String[] {MessageContract.TEXT};
int[] to = new int[] {android.R.id.text1};
adapter2 = new SimpleCursorAdapter(
this,
android.R.layout.simple_expandable_list_item_1,
null,
from,
to, 2);
detailsFragment = (DetailsFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
ListView LV = (ListView) detailsFragment.getView().findViewById(android.R.id.list);
LV.setAdapter(adapter2);
In summary, what I am trying to do is:
Create a new fragment
add it to the container
and update the view from the MainActivity
Additionally, inside the onCreate in the MainActivity I have this:
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
DetailsFragment firstFragment = new DetailsFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
Please accept my apologies if I am not clear enough, and thank you in advance for your help :)
From the Fragments Developer Guide:
"Calling commit() does not perform the transaction immediately. Rather, it schedules it to run on the activity's UI thread (the "main" thread) as soon as the thread is able to do so."
You have the following lines of code almost immediately after committing the FragmentTransaction.
ListView LV = (ListView) detailsFragment.getView().findViewById(android.R.id.list);
LV.setAdapter(adapter2);
It is most likely that the View for the detailsFragment has not been created yet.
Take a look at the Complete Activity/Fragment Lifecycle. View creation does not occur until after the Fragment is attached to the Activity.
If you need to pass adapter2 from the Activity to the Fragment, you could create a member variable and setter in your Fragment, but don't set the adapter on the ListView until the Fragment's View has been created.
For example, in your Fragment:
// member variable to hold the adapter
private SimpleCursorAdapter mAdapter2;
// setter for the member variable
private void setAdapter2(SimpleCursorAdapter adapter) {
mAdapter2 = adapter;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Now that the view is created, we can set the adapter.
ListView LV = (ListView) view.findViewById(android.R.id.list);
LV.setAdapter(mAdapter2);
}
And in your Activity:
// Create the new Fragment
DetailsFragment detailsFragment = new DetailsFragment();
// Add the Arguments to the Fragment
Bundle args = new Bundle();
args.putInt(DetailsFragment.ARG_POSITION, position);
detailsFragment.setArguments(args);
// Create the adapter
Cursor cursor = (Cursor) adapter.getItem(position);
Peer peer = new Peer(cursor);
String[] from = new String[] {MessageContract.TEXT};
int[] to = new int[] {android.R.id.text1};
adapter2 = new SimpleCursorAdapter(
this,
android.R.layout.simple_expandable_list_item_1,
null,
from,
to, 2);
// Set the Adapter for the Fragment
detailsFragment.setAdapter2(adapter2);
// Do the FragmentTransaction
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.replace(R.id.fragment_container, detailsFragment);
trans.addToBackStack(null);
trans.commit();
I use a master/detail flow and in every fragment I have a viewpager containing some forms but the thing is when I click on some other links from the master list, the content of the viewpager is lost. How to save this content so everytime I click on the same choice I find my data.
My code :
#Override
public void onItemSelected(String id) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
if(id.contains("1"))
{
Bundle arguments = new Bundle();
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setRetainInstance(true);
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment).commit();
}
else if(id.contains("2"))
{
Bundle arguments = new Bundle();
ItemDetailFragment2 fragment = new ItemDetailFragment2();
fragment.setRetainInstance(true);
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment).commit();
}
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, ItemDetailActivity.class);
detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, id);
startActivity(detailIntent);
}
}
EDIT:
I tried to modify my code this way :
if (id.contains("1")) {
Fragment currFragment = fm.findFragmentByTag(lastTag);
ItemDetailFragment newFragment = (ItemDetailFragment) fm.findFragmentByTag("f"+1);
FragmentTransaction ft = fm.beginTransaction();
ft.hide(currFragment);
if(newFragment==null)
{lastTag="f"+1;
newFragment=new ItemDetailFragment();
ft
.replace(R.id.item_detail_container, newFragment);}
else {
ft.show(newFragment);
}
ft.commit();
}
But i got a NullPointerException at android.support.v4.app.BackStackRecord.run(BackStackrecord.java:656)
Try to call setRetainInstance(true) in your fragments onCreate()
As writed here, if you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
You can hide current fragment, instead replacing, then add new. Like that:
int currPage = 0;
public void goToPage(int num) {
FragmentManager fm = getSupportFragmentManager();
Fragment currFragment = fm.findFragmentByTag("f" + currPage);
Fragment newFragment = fm.findFragmentByTag("f" + num);
FragmentTransition ft = fm.beginTransaction();
ft.hide(currFragment);
if(newFragment == null) {
// First use. Create new instance for page.
newFragment = new MyFragment();
ft.add(R.id.item_detail_container, newFragment, "f" + num);
} else {
// Fragment for page already added. Just show it.
ft.show(newFragment);
}
ft.commit();
this.currPage = num;
}
And don't forget to specify tag for initial page fragment too.
You can implement the onPause() or onStop() methods of your fragments and save the data when that methods are called, which happens automatically when they are removed.
Or you could perhaps go with the savedInstanceState.
It's better to save in the arrayList. Onclick store the image id in the arrayList.