Updating a dynamically allocated fragment within a container using FragmentTransaction issue - android

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

Related

Android List is overwriting existing items instead of appending

i'm fairly new to Android and got a problem.
When i'm trying to append a item to my list, it' overwriting the list instead of appending.
Here is my fragment call
Activity:
FoodSearchFragmentContainer fragment = FoodSearchFragmentContainer.newInstance();
Bundle bundle = new Bundle();
bundle.putString("query", query);
fragment.setArguments(bundle);
android.app.FragmentTransaction ft = this.getFragmentManager().beginTransaction();
ft.add(R.id.frFoodSearch, new FoodSearchFragmentContainer());
ft.commit();
My Dialog (fragment)
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(), R.array.measurements_array,
android.R.layout.simple_spinner_item);
The list appender (fragment) FoodSearchFragmentContainer
listView = (ListView) view.findViewById(R.id.lvIngredients);
Ingredients i = new Ingredients();
listView.setAdapter(adapter);
i.setName("test");
ingredients.add(i);
adapter.notifyDataSetChanged();
The order is: i call the dialog, from the dialog the method in the activity and from there append the item to the list.
Also i cant access the bundle in the appender from my activity.
Gratefully
Lukas
Okay i got the solution on my one now.
I Created everytime a new fragment, so they were overlapping.
Now i Create the Fragment in the OnCreate of the Activity and call a Method in the fragment to add a new item

Why are all these fragments receiving the same bundle items?

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?

How to keep the content of a ListView even after changing Fragment through the Drawer

I am using Android Studio and I created an Activity that contains a NavigationDrawerFragment.
One of the Fragments is loading the content of an SQLite database inside a ListView, using a custom ArrayAdapter with complex items.
My problem is that if I select another fragment in the drawer, then come back to this one, the onCreate() of the Fragment is called again, so is the onCreateView(), and the database is loaded once again.
How can I preserve everything without having to load the database nor populate the list again?
I don't have this problem when orientation changes, so I am a bit confused. Maybe it is because I have a layout for both Portrait and Landscape ?
here is the code of the onCreateView()
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getActivity().getActionBar().show();
View view = inflater.inflate(R.layout.fragment_papers, container, false);
allItems = new ArrayList<ItemData>();
getAllItemsFromDB("");
mAdapter = new ItemsList(getActivity(), allItems, this, mDBApi);
// Set the adapter
mListView = (AbsListView) view.findViewById(R.id.list);
mListView.setAdapter(mAdapter);
// Set OnItemClickListener so we can be notified on item clicks
mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(this);
return view;
}
and the getAllItemsFromDB() contains something like
public void getAllItemsFromDB(String query){
Cursor c = sqldb.rawQuery("SELECT * FROM 'Table' " + query, null);
while (c.moveToNext()) {
allItems.add(parseSQL(sqldb, c));
}
c.close();
}
Set your array list static
private static List<ItemData> allItems;
Then check it for null and initialise it only once
if(allItems == null) {
allItems = new ArrayList<ItemData>();
getAllItemsFromDB("");
}
may be ur loading frament everytime as
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, new UrFragment()).commit();
Don't do that, create instance of fragment and use it.
// update the main content by replacing fragments
Fragment fragment = new UrFragment();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

How can I programmatically add multiple list fragments to a single activity?

Apologies as I am an Android novice. I'm trying to programmatically add multiple list fragments to a single activity, but when I do, only one is displayed. How can I display multiple lists in a single action?
The end goal is to read a set of data from an API and categorize it into multiple lists in my application. I would like the data to be horizontally scrolling list fragments, but since that's an additional complication I've started with simple ListFragments. My code looks like this:
activity_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
ItemActivity:
public class ItemActivity extends FragmentActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager manager = getSupportFragmentManager();
ItemListFragment fragment1 = new ItemListFragment();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.fragmentContainer, fragment1);
ItemListFragment fragment2 = new ItemListFragment();
transaction.add(R.id.fragmentContainer, fragment2);
transaction.commit();
}
}
ItemListFragment:
public class ItemListFragment extends ListFragment {
List<String> items = new ArrayList<String>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
items.add("One");
items.add("Two");
items.add("Three");
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, items);
setListAdapter(adapter);
}
}
If you want multiple lists in one Activity, here's what I usually do:
Because every ListView is using independent scrolling view, I usually split the screen for every ListView I have. Example: with 2 ListView i split the height of the screen 50/50 so every ListView have 50% portion of the screen.
If I have to dynamically add ListView to the screen, I use cwac merge adapter to merge the adapter and display it in a single ListView
Another alternative you can just use ViewPager to display ListFragment. This would achieve what you want, which is having multiple listviews in a single activity.
Why wouldnt you do it this way?
FragmentManager fragmentManager = getFragmentManager ();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();
// work here to change Activity fragments (add, remove, etc.). Example here of adding.
fragmentTransaction.add (R.id.myFrame, myFrag);
fragmentTransaction.commit ()
i found this code, so as just one is displayed, commit each transaction in your code.
FragmentManager manager = getSupportFragmentManager();
ItemListFragment fragment1 = new ItemListFragment();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.fragmentContainer, fragment1).commit();
transaction = manager.beginTransaction(); // might be unnescessary
ItemListFragment fragment2 = new ItemListFragment();
transaction.add(R.id.fragmentContainer, fragment2).commit();
if this throws some sort of error, you might need to start another transaction for adding the second fragment.

Two Fragments One Screen

I have implemented an Activity that hosts two fragments. I have one fragment on the left and one on the right. The fragment on the left is a list of items to click. When you click on it inflates a new fragment on the right.
One item on the left list is supposed to inflate a different list on the right. I have it working good except when you click that item it inflates the list to cover the entire screen. And I only want it to cover the space on the right side of the screen.
Is there a way I can keep the fragment from covering the entire screen? All the others stay on the right side of the screen, its just this one that doesn't. But this is the only one trying to inflate a ListView on the right side.
Here are the relevant methods of the ListFragemnt that is supposed to be on the right inflated on the right side:
public void onActivityCreated(Bundle inState) {
super.onActivityCreated(inState);
//setContentView(R.layout.results_list_activity);
//mIntent = getIntent();
mActivity = getActivity();
mArrayListRecipe = generateList();
ListView listView = (ListView) mActivity.findViewById(android.R.id.list);
ArrayAdapter<Recipe> adapter = new ArrayAdapter<Recipe>(mActivity,
android.R.layout.simple_list_item_1, mArrayListRecipe);
listView.setAdapter(adapter);
//listView.setOnItemClickListener((OnItemClickListener) this);
}
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.results_list_activity, container, false);
return layout;
}
And here is the XML layout that is inflated called results_list_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="#android:id/list"
android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/>
And here is the layout that contains the frame that I want this ListView placed in main_activity.xml:
<?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:id="#+id/main_layout_land"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:orientation="horizontal" android:layout_alignParentBottom="true">
<!-- Left side fragment -->
<fragment class="com.jasoncrosby.app.recipeorganizer.MainFragment" android:id="#+id/left_fragment"
android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/>
<!-- Right side fragment -->
<FrameLayout android:id="#+id/right_fragment" android:layout_width="0dp" android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
EDIT:
The activity that loads the fragment is simple and only consists of a call to:
setContentView(R.layout.main_activity);
Here is MainFragment.java:
public class MainFragment extends ListFragment {
//*******
// Fields
//*******
/** Indicated weather there is enough room on the screen to use 2 panes. */
private boolean mDualPane;
/** Indicates which item in the main list was clicked. */
private int mCurCheckPosition = 0;
/** The name of the save button. */
private String mStringSaveItemName;
/** The name of the search button. */
private String mStringSearchItemName;
/** The name of the browse button. */
private String mStringBrowseItemName;
/** The name of the random button. */
private String mStringRandomItemName;
/** The {#code Activity} associated with this {#code Fragment}. */
private Activity mActivity;
//*************
// Constructors
//*************
/**
* This constructor is only provided to avoid a compiler warning. It should not be used to crate a new
* {#code MainFragment} object.
*/
public MainFragment(){}
/**
* Used to create a new {#code MainFragment} object with the id.
*
* #param id
*/
//public MainFragment(int id) {
//mIntFragmentId = id;
//mContext = context;
//}
//*******************
// Overridden methods
//*******************
#Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
mActivity = getActivity();
mStringSaveItemName = mActivity.getString(R.string.save_button);
mStringSearchItemName = mActivity.getString(R.string.search_button);
mStringBrowseItemName = mActivity.getString(R.string.browse_button);
mStringRandomItemName = mActivity.getString(R.string.random_button);
// Populate list with our static array of titles.
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1,
new String[] {mStringSaveItemName, mStringSearchItemName, mStringBrowseItemName,
mStringRandomItemName}));
// Check to see if we have a frame in which to embed the details
// fragment directly in the containing UI.
View detailsFrame = mActivity.findViewById(R.id.right_fragment);
mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if (mDualPane) {
// In dual-pane mode, list view highlights selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Make sure our UI is in the correct state.
showDetails(mCurCheckPosition);
}
}
#Override
public void onListItemClick(ListView l, View v, int pos, long id) {
showDetails(pos);
}
//****************
// Private methods
//****************
private void showDetails(int index) {
mCurCheckPosition = index;
if (mDualPane) {
switch (mCurCheckPosition) {
case 0 : inflateFirstUi(); break;
case 1 : inflateSecondUi(); break;
case 2 : inflateThirdUi(); break;
case 3 : inflateFourthUi(); break;
default : inflateFirstUi(); break;
}
} else {
// Otherwise we need to launch a new activity to display
// the dialog fragment with selected text.
Intent intent = new Intent();
//intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
private void inflateFirstUi() {
SaveFragment details = new SaveFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_fragment, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
private void inflateSecondUi() {
SearchFragment details = new SearchFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_fragment, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
private void inflateThirdUi() {
ResultsListFragment details = new ResultsListFragment(Constants.BROWSE_REQUEST);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_fragment, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
private void inflateFourthUi() {
ResultsListFragment details = new ResultsListFragment(Constants.RANDOM_REQUEST);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.right_fragment, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}
The problem is in the showDetails() method in the switch statement. Case 0 and case 1 work just fine and load in the pane I have on the right side of the screen. Case 3 is where I am running into this problem. It loads the UI but its covering the entire screen and not confined to the right side in the pane.
Ok I have the problem solved. In my onActivityCreated() method I made a call to:
listView.setAdapter(adapter);
I went back and compared the fragments that are working with this one that is not working. I found in the ones that work I called this instead:
setListAdapter(adapter);
I changed the non working fragment to call setListAdatpter(adapter) and not listView.setAdapter(adapter). And now the fragment is being shown in the right side of the screen like it should be. Thanks to everyone who helped and was looking into this.

Categories

Resources