Cant add a HeaderView to a ListFragment - android

Here is the code where I add a list to my list fragmet:
public void onAttach(Activity activity) {
super.onAttach(activity);
System.err.println("Fragment Attach");
String[] MyList = {"Item 1","Item 2","Item 3","Item 4","Item 5"};
System.err.println("File Row ID" + Integer.toString(R.layout.file_row));
ArrayAdapter<String> aa = new ArrayAdapter<String>(getActivity(), R.layout.file_row, MyList);
//Trying to add a Header View.
TextView tv = (TextView) activity.findViewById(R.layout.file_row);
tv.setText(R.string.FileBrowserHeader);
this.getListView().addHeaderView(tv);
//Setting the adapter
setListAdapter(aa);
}
However the line this.getListView().addHeaderView(tv); gives me the error
06-11 15:24:46.110: ERROR/AndroidRuntime(8532): Caused by: java.lang.IllegalStateException: Content view not yet created
And the program crashes.
Can anyone tell me what am I doing wrong?

The problem is that you are adding the header view too soon.
The error is being caused by you trying to find views that haven't been created yet.
The life cycle for a fragment is (source: http://developer.android.com/reference/android/app/Fragment.html)
onAttach(Activity) called once the fragment is associated with its activity.
onCreate(Bundle) called to do initial creation of the fragment.
onCreateView(LayoutInflater, ViewGroup, Bundle) creates and returns the view hierarchy associated with the fragment.
onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate.
onStart() makes the fragment visible to the user (based on its containing activity being started).
onResume() makes the fragment interacting with the user (based on its containing activity being resumed).
As you can see, you are trying to use views in onAttach, but the views don't exist until onCreateView! Try moving your code to onActivityCreate, which has happened after the views all exist

Related

How do I update ListView in another Fragment?

I have an Activity which holds a ViewPager with 2 Fragments. One fragment is for adding items to ListView and another fragment is holding the ListView.
I've been trying for almost 3 days now without any positive results. How do I update the other fragment's ListView from the first fragment?
I'm trying to call the method that updates ListView from the Activity that holds ViewPager but it doesn't work.
Calling the method from ViewPager activity :
#Override
public void onPageSelected(int position) {
library.populateListView(getApplicationContext());
aBar.setSelectedNavigationItem(position);
}
This is the populateListView method:
public void populateListView(Context context){
CustomListViewAdapter customAdapter = new CustomListViewAdapter(getDatabaseArrayList(context), getActivity());
if (lView != null)
{
lView.setAdapter(customAdapter);
}
customAdapter.notifyDataSetChanged();
}
However this doesn't work because the lView variable (ListView) is null because the fragment isn't shown at the moment when this is being called.
I am assuming that function populateListView() is a function of the Fragment containing the ListView. You are calling populateListView() on every call to onPageSelected. Should you not check what is the position that is being selected. Anyway the populateListView() method should be a public method of the Fragment containing ListView. And You Can Instantiate The Fragment from the Viewpager adapter in the Activity and than call this method. In That way the listView should not be null.
#Override
public void onPageSelected(int position) {
ListViewFragment frag=(ListViewFragment)adapter.instantiateItem(viewPager, 1);
//here adapter is the ViewPager Adapter and you must supply the viewpager that contains
//the fragments and also the position of the fragment to instantiate.
//For example 0 or 1 etc.
frag.populateListView(getApplicationContext());
aBar.setSelectedNavigationItem(position);
}
Understand Fragments
Please see this link. I have gone in great detail explaining the concept of fragments.
Pay particular attention to the definition of rootview:
public void onActivityCreared(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Do stuff on creation. This is usually where you add the bulk of your code. Like clickListners
// You can define this object as any element in any of your xml's
View rootview = inflater.inflate(R.layout.xml_the_fragment_uses container,false);
rootview.findViewById(R.id.your_id).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Do something
}
});
}
In the above case I defined a button for an on click listener, but you can just as easily define a ListView along with its appropriate methods.
Alternate Solution
A second method could be using getView or getActivity (check the communicating with activity section).
For example:
ListView listView = (ListView) getView().findViewById(R.id.your_listView_id);
OR (more likely solution for your problem)
View listView = getActivity().findViewById(R.id.list);
Please read this post for additional information.
Good Luck.
To do this safely, you have to keep your listview data in a higher level Parent-Activity not the fragments. Make your MainActivityclass singleton class by making the constructor private and create a getInstance method that return the only initialized instance of your `MainActivity.
This will allow you to keep your data instance safe from being re-initialized or lost. Then, in onResume of your fragment re-set the data (get it from the MainActivity) to your listview adapter and call notifyDataSetChanged() method from the adapter instance.
This will do the trick.

Android fragment crashes when app comes from background

I'm having a problem that is starting to give me head-hakes.
My application is basically a FragmentActivity with a navigation drawer and each button of the navigation drawer loads a new fragment.
I'm using android.support.v4 for almost every component in the project.
My issue lies every time my app goes to background and comes back to foreground the oncreate view loads the view again and most of the variables that I use to create the view are null and my app crashes because of that.
Can anyone point me in the right direction to solve this problem? would it be because of the OnsavedInstanceState() method, the onCreateView() doing the variable instantiation, or anything else?
Here's my error log:
java.lang.RuntimeException: Unable to start activity ComponentInfo{pt.gema.welcomeangola/pt.gema.welcomeangola.activities.MainActivity}: java.lang.NullPointerException
Caused by: java.lang.NullPointerException
at java.util.ArrayList.<init>(ArrayList.java:93)
at pt.gema.welcomeangola.activities.ListViewExampleFragment.onCreateView(ListViewExampleFragment.java:103)
One of my fragments OnCreateView() Code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
lvef = this;
View rootView = inflater.inflate(R.layout.activity_items_list, container, false);
getActivity().setTitle("ListViewExample");
btnSearch = (ToggleButton) rootView.findViewById(R.id.btn_search);
btnSearchText = (ImageButton) rootView.findViewById(R.id.btn_search_string);
btnAZ = (ToggleButton) rootView.findViewById(R.id.btn_az);
searchText = (EditText) rootView.findViewById(R.id.search_text);
layoutSearch = (RelativeLayout) rootView.findViewById(R.id.search_layout);
pBar = (RelativeLayout) rootView.findViewById(R.id.progress_bar_layout);
List<Integer> filter= new ArrayList<Integer>();
filter.add(id_type);
OriginalObjectsLocality= new ArrayList<ListPlaceInfo>(Objects);
listview = (ListView) rootView.findViewById(R.id.list_item);
adapter = new WAListAdapter(getActivity().getApplicationContext(),Objects,OriginalObjectsLocality,lvef);
listview.setAdapter(adapter);
return rootView;
}
EDIT
Although the problem was not only in the Arraylist Objects, but the instantiation of another class that I tried to access. laalto answer helped me to find the problem, therefore I consider it the right answer.
From comments:
#Szymon I receive that arraylist in the fragment constructor, and i thisnk my problem is here... public ListViewExampleFragment(ArrayList objects) { super(); this.Objects = objects; this.listPlaceAZ=azListing(new ArrayList(objects)); }
You set up a member variable Objects in a constructor that takes an arraylist param.
Fragments must have a parameterless constructor and the framework will create the fragment calling that empty constructor. So your parameter-taking constructor is not called and Objects is left null, causing a NPE here:
OriginalObjectsLocality= new ArrayList<ListPlaceInfo>(Objects);
If you need to pass parameters to your fragments, use a Bundle set with setArguments() and accessed with getArguments().
But passing an arraylist as a Bundle requires a Parceble and that requires me to change almost all my code, is there any other option?
You could make the member variable static so it survives fragment recreation. But I wouldn't recommend that as it will create a whole set of other problems, such as memory leaks due to incollectible objects.
It's better to rethink the design. For example, you probably don't need to pass an array of objects around. You could just pass an array of identifiers as parameter instead, and query the objects by id when needed.

getActivity().findViewById returns null, called from fragment onActivityCreated

I am following the contacts provider lesson on retrieving contacts and displaying them using fragments. For reference, I have set the API level to 16 (Android 4.1).
I have mostly followed this tutorial to the letter, with a few notable exceptions. For example, I import from mypackage.R rather than android.R.
My problem is in my onActivityCreated handler in my ListContactsFragment:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initializes the loader
getLoaderManager().initLoader(0, null, this);
// Gets the ListView from the View list of the parent activity
View mContactsListView =
getActivity().findViewById(R.layout.contacts_list_view);
mContactsList = (ListView) mContactsListView;
// Gets a CursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.contacts_list_item,
null,
FROM_COLUMNS, TO_IDS,
0);
// Sets the adapter for the ListView
mContactsList.setAdapter(mCursorAdapter);
// Set the item click listener to be the current fragment.
mContactsList.setOnItemClickListener(this);
}
View mContactsListView is null, meaning findViewById didn't work.
My parent activity is a default one created by eclipse. To this, I have done two things:
Replaced import android.app.Activity with android.support.v4.app.FragmentActivity to prevent the ClasscastException that happens if you don't.
Imported my fragment into the XML.
My activity_list_contacts.xml looks like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".ListContactsActivity" >
<fragment android:name="mypackage.ListContactsFragment"
android:id="#+id/contacts_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
the matching activity, just in case:
public class ListContactsActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_contacts);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.list_contacts, menu);
return true;
}
}
and contacts_list_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
So my question is, what am I doing wrong for findViewById not to find my view?
Things I have tried (most of these are accepted answers on questions that almost look like a duplicate to this one):
Reading the document I copied and pasted from word for word.
Trying to getView().findViewById() as suggested in this question. This also returns null.
using findViewById(R.id.contacts_list_view); instead as suggested by this answer. This doesn't return null; instead it causes a ClassCastException in that android.support.v4.app.NoSaveStateFrameLayout could not be cast to android.widget.ListView.
I read that sometimes the fragment callback for creation occurs before being attached to the activity. So, I added the handler to the onAttach method like so:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
View mContactsListView = activity.findViewById(R.id.contacts_list_view);
mContactsList = (ListView) mContactsListView;
// Gets a CursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.contacts_list_item,
null,
FROM_COLUMNS, TO_IDS,
0);
// Sets the adapter for the ListView
mContactsList.setAdapter(mCursorAdapter);
// Set the item click listener to be the current fragment.
mContactsList.setOnItemClickListener(this);
}
You guessed it - still null.
So at this point I am a little lost. I have two questions:
What am I doing wrong (please request additional info in comments if I have not provided enough)?
Is it preferable to leave the adapter setup in onAttach, or where the tutorial says.
I follow the tutorial at developer.android.com and encountered the very same problem.
Finally i noticed that in this part:
// Gets the ListView from the View list of the parent activity
mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view);
it seems trying to find a View, but passing a Layout id as argument.
The View | android reference says :
View IDs need not be unique throughout the tree, but it is good practice to ensure that they are at least unique within the part of the tree you are searching.
So, i changed the ListView id in the contacts_list_view.xml to a unique one:
<?xml version="1.0" encoding="utf-8"?>
<ListView ...
android:id="#+id/contacts_listview_a_unique_id_here"
... />
then try to find it with:
mContactsList = (ListView) getActivity().findViewById(R.id.contacts_listview_a_unique_id_here);
and it works ( in the onActivityCreated callback).
About the cast exception:
As hierarchyviewer shows, the tree is like this:
so if you findViewById passing root element of the view, like in that post, it will return NoSaveStateFrameLayout, which is not the child ListView you want.
getActivity().findViewById() looks for the id you provide as parameter in the hierarchy of views of the Activity. Probably the ListView is returned by onCreateView of your fragment. If you want to perform a findViewById on the fragment views hierarchy, you have to call getView().findViewById(). In this case, since you are extending ListFragment, to retrieve the ListView, as for the ListActivity, you have to use getListView(); another analogy with the ListActivity is the id of the ListView inside the xml file ( android:id="#android:id/list")
Note that ListFragment has a getListView() method to return the ListView widget instance.
Developer docs: ListFragment.getListView
#theme propose a solution, but in order to use android's inner listview here is another way. Because in your "contacts_list_view.xml" file your're using android's inner listview with id=#android:id/list. And in your code you can refer to the list view with findViewById(android.R.id.list), so your onActivityCreated will be like this:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContactsList = (ListView) getActivity().findViewById(android.R.id.list);//(R.layout.fragment_contact_list);
mCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.contact_item_list,null, FROM_COLUMNS, TO_IDS, 0);
mContactsList.setAdapter(mCursorAdapter);
mContactsList.setOnItemClickListener(this);
getLoaderManager().initLoader(0,null,this);
}
Hope it helps, it's good the android developer guide contains such "bug" so that we can be familiar with the api.

how to implement AutoCompleteTextView inside DialogFragment

i have null pointer exception when put this inside DialogFragment in onCreateView method
AutoCompleteTextView med =(AutoCompleteTextView)getActivity().findViewById(R.id.new_autoCompleteT);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity().getBaseContext(), android.R.layout.simple_list_item_1, item);
med.setAdapter(adapter);
It seems that you are trying to retrieve a View from the associated Activity. You should check the call to getActivity(), maybe it's returning null because the activity it hasn't attached yet. Check the onActivityCreated (Bundle savedInstanceState) method, from the doc:
Called when the fragment's activity has been created and this fragment's view hierarchy instantiated. It can be used to do final initialization once these pieces are in place, such as retrieving views or restoring state.

Retaining list in list fragment on orientation change

I have an app using fragments, all of which are contained in a single activity. The activity starts with a fragment containing a menu of buttons, all of which cause various listfragments to replace the original button/menu fragment.
My problem is that upon an orientation change, if the activity is displaying one of the listviews, it goes away and the button menu returns. I understand why this is happening... the activity is destroyed and re-created, but not how to work around it and maintain the list view/current fragment through the orientation change.
I've found setRetainInstance and the example of use here, but I can't figure out how to apply it to my situation with the button menu or the possibility that the fragment I want to retain could be one of several different ones.
Below is code simplified to show the main activity and one of the listfragments.
Any pointers in what to add where to make it so that the list fragment will be retained would be greatly appreciated.
Activity
public class Main extends FragmentActivity {
private MainMenuFragment menu;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
menu = new MainMenuFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.pane, menu).commit();
}
}
ListFragment
public class ItemListFragment extends ListFragment {
private TextView header;
private TextView empty;
private Button add;
public static Cursor itemCursor;
private GroceryDB mDbHelper;
public static long mRowId;
public static CheckCursorAdapter lists;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.common_list, container, false);
header = (TextView) v.findViewById(R.id.header);
empty = (TextView) v.findViewById(android.R.id.empty);
header.setText(R.string.header_item);
empty.setText(R.string.empty_items);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mRowId=0;
mDbHelper = new GroceryDB(getActivity());
mDbHelper.open();
itemCursor = mDbHelper.fetchAllItems();
getActivity().startManagingCursor(itemCursor);
String[] from = new String[] { GroceryDB.ITEM_NAME };
int[] to = new int[] { R.id.ListItem };
lists = new CheckCursorAdapter(getActivity(),
R.layout.listlayout_itemlist, itemCursor, from, to);
setListAdapter(lists);
}
}
how to work around it and maintain the list view/current fragment through the orientation change
You are blindly replacing the fragment every time onCreate() is called. Instead, only add/replace the fragment if savedInstanceState() is null. If it is not null, you are coming back from a configuration change, and your existing fragments will be recreated (or, if they were retained, they are already there).
setRetainInstance(true) means that the fragment itself will be retained across configuration changes, instead of being destroyed/recreated like the activity is. However, it will still be called with onCreateView(). In your code, that means that your data members of ItemListFragment would stick around, but you would still need to call setListAdapter() even if you do not requery the database.
I know that this has been resolved a long time ago, but for the sake of people searching for a solution who have as much issues as I've (repeatedly) had with retaining lists during an orientation change I would like to add that you could also use a custom class which holds the list of data for your listadapter.
This way it keeps the data when recreating the activity (and listfragment) and you can just test to see if it has any data in your oncreate. If the list == null or the list.size < 0 you proceed as usual and get the data whatever way you normally get it. Otherwise you just set your listadapter with the data it already has.
To me this is a lot easier, and seeing as Eclipse automatically creates a similar DummyContent class for your data when creating an android master/detail flow project it basically only requires a change of the oncreate of your listfragment.

Categories

Resources