I am trying to implement a ListFragment. The idea is to use CursorLoader. The code for FragmentActivity is
public class XYZ extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
XYZFragment list = new XYZFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
Now I assume that onActivityCreate of XYZFragment should be called but that is not happening instead I am getting a the below image on the Emulator. I am looking for an explanation as to what is happening and what I am doing wrong?
Thanks.
Change onActivityCreate to onActivityCreated and you should get the expected results.
Related
Maybe I don't know the first thing about Android app development and xml (but I have 3 apps at Google Play Store), but what exactly determines what gets executed when?
To try to answer that question, I inserted Log statements in each method in each class file in FragmentBasics, which is the downloaded file from the URL in the Title.
Project structure:
I was surprised to see what the order of execution was.
MainActivity: `````onCreate
HeadlinesFragment: `````onAttach
HeadlinesFragment: `````onCreate
AbsListView: checkAbsListViewlLogProperty get invalid command
ArticleFragment: `````onCreateView
MainActivity: `````fragment_container IS null--two-pane mode
HeadlinesFragment: `````onStart
ArticleFragment: `````onStart
Why/how did onAttach and onCreate in HeadlinesFragment and onCreateView in ArticleFragment get executed before MainActivty "fragment container is Null..."?
And why does HeadlinesFragment not have onCreateView like ArticleFragment, but just onCreate?
I read that setContentView does this: "Set the activity content from a layout resource. The resource will be inflated, adding all top-level views to the activity." Since I was using a "large" device (tablet) and HeadlinesFragment and ArticleFragment are part of large\news_articles.xml, did that xml cause the classes to be instantiated?
I really feel like I know next to nothing about Android development. But I have those three apps ...
Here's the essence of the 3 classes mentioned:
public class MainActivity extends FragmentActivity
implements HeadlinesFragment.OnHeadlineSelectedListener
{
public void onCreate(Bundle savedInstanceState)
{
Log.w("MainActivity","`````onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles); // either normal or large
if (findViewById(R.id.fragment_container) == null)
Log.w("MainActivity","`````fragment_container IS null--two-pane mode");
else
{
Log.w("MainActivity","`````fragment_container not null--one-pane mode");
if (savedInstanceState != null)
return;
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager() .beginTransaction()
.add(R.id.fragment_container, firstFragment)
.commit();
}
}
}
.
public class HeadlinesFragment extends ListFragment
{
public void onCreate(Bundle _savedInstanceState)
{
Log.w("HeadlinesFragment","`````onCreate");
super.onCreate(_savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
}
public void onStart()
{
Log.w("HeadlinesFragment","`````onStart");
super.onStart();
if (getFragmentManager().findFragmentById(R.id.article_fragment) != null)
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
public void onAttach(Activity _activity)
{
Log.w("HeadlinesFragment", "`````onAttach");
super.onAttach(_activity);
}
}
.
public class ArticleFragment extends Fragment
{
public View onCreateView(LayoutInflater _inflater, ViewGroup _container, Bundle _savedInstanceState)
{
Log.w("ArticleFragment", "`````onCreateView ");
View v = _inflater.inflate(R.layout.article_view, _container, false);
return v;
}
public void onStart()
{
Log.w("ArticleFragment","`````onStart");
super.onStart();
}
}
And the essence of the .xml:
article_view.xml
<TextView android:id="#+id/article" />
news_articles.xml
<FrameLayout android:id="#+id/fragment_container" />
news_articles(large).xml
<LinearLayout>
<fragment
android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment">
<fragment
android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"/>
</LinearLayout> [1]: http://i.stack.imgur.com/JPCZu.png
Someone else already did the same as you, but built also a nice image with how in practice the lifecycle happens. Here is a direct reference to the image diagram.
Basically I've got an ActionBarActivity that loads activity_main.xml which contains my ListFragment
My ActionBar has an Add button on it, to add items to the list.
Problem I've run into now, is how do I handle the Add and pass the information to the ListFragment to populate the ListView?
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//activity_main only contains <fragment ... /> to add my ListFragment
}
....
// This is called from onOptionsItemSelected
private void showAddDialog() {
FragmentManager fm = getSupportFragmentManager();
InputDialog inputDialog = new AddInputDialog();
inputDialog.setOnUpdateListener(new InputDialog.onUpdateListener(){
#Override
public void onUpdate(Item item){
//I'm lost at what to do here...
}
});
inputDialog.show(fm, "fragment_dialog_input");
}
EDIT
Got it working using findFragmentById(), and posted answer below.
I kept trying to get it to work using findFragmentByTag() and even though I had a TAG set in my fragment, and when debugging it(the TAG) showed correctly, for some reason would always return null.
Since the fragment is defined in activity_main.xml use findFragmentById using the specified android:id then you can call your public function within the ListFragment to update the list and notify adapter.
private void showAddDialog() {
final FragmentManager fm = getSupportFragmentManager();
InputDialog inputDialog = new AddInputDialog();
inputDialog.setOnUpdateListener(new InputDialog.onUpdateListener(){
#Override
public void onUpdate(Item item){
ListFragment lf = (ListFragment)fm.findFragmentById(R.id.listFragment);
lf.addItem(item);
}
});
inputDialog.show(fm, "fragment_dialog_input");
}
in my app I'm using one activity and two fragments. The app uses a layout with a container so the fragments are added via transactions. The first fragment contains a listview and the other fragment a detail view for the listview items.
Both fragments use setRetainInstance(true). The fragments are added via a replace transaction and addToBackStack(null) is set. The listfragment contains an instance variable which holds some infos for the list. Now I'm changing to detail and press back and the instance variable is null. I read about setRetainInstance and addToBackStack and removed addToBackStack, but even then the instance variable is null.
Does anyone know what I might be doing wrong?
regards,
Thomas
setRetainInstance(true) will tell the FragmentManager to keep the fragment around when the containing Activity is killed and rebuilt for some reason. It doesn't guarantee that the Fragment instance will stick around after a transaction to add or replace. It sounds like your adapter is being garbage collected and you're not creating a new one.
A more generally easy solution would be to make a viewless Fragment to retain your ListAdapter. The way you do this is to create the Fragment, set the retain instance to true, and return null in the method onCreateView(). To add it, just called addFragment(Fragment, String) via the FragmentTransaction. You never remove or replace it, so it will always stay in memory for the length of the app. Screen rotations won't kill it.
Whenever your ListFragment is created, in onCreateView() get the FragmentManager and use either the method findFragmentById() or FindFragmentByTag() to retrieve your retained fragment from memory. Then get the adapter from that fragment and set it as your adapter for the list.
public class ViewlessFragment extends Fragment {
public final static string TAG = "ViewlessFragment";
private ListAdapter mAdapter;
#Override
public ViewlessFragment() {
mAdapter = createAdater();
setRetainInstance(true);
}
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return null;
}
public ListAdapter getAdapter() {
return mAdapter;
}
}
public class MyListFragment extends ListFragment {
final public static String TAG = "MyListFragment";
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View returnView = getMyView();
final ViewlessFragment adapterFragment = (ViewlessFragment) getFragmentManager().findFragmentByTag(ViewlessFragment.TAG);
setListAdapter(ViewlessFragment.getAdapter());
return returnView;
}
}
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle icicle) {
// ... setup code...
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ViewlessFragment adapterFragment = fm.findFragmentByTag(ViewlessFragment.TAG);
if(adapterFragment == null) {
ft.add(new ViewlessFragment(), ViewlessFragment.TAG);
}
ft.add(R.id.fragmentContainer, new MyListFragment(), MyListFragment.TAG);
ft.commit();
}
}
I have a ViewPager with two Fragments which I instantiate in onCreate of my FragmentActivity.
private List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this,Frag_1.class.getName()));
fragments.add(Fragment.instantiate(this,Frag_2.class.getName()));
this.vPagerAdapter = new Adapt(super.getSupportFragmentManager(),fragments);
vPager = (ViewPager) super.findViewById(R.id.pager);
vPager.setAdapter(vPagerAdapter);
My second Fragment has a method inside that I call to update my ListView - refreshList():
public class Frag_2 extends Fragment {
private ListView list;
private ArrayList<data> data;
private boolean firstCreation=true;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRetainInstance(false);
}
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout, container, false);
list = (ListView) view.findViewById(R.id.lst);
//this.setRetainInstance(true);
return view;
}
public void refreshList(ArrayList <data> data){
if(data!=null){
ArrayAdapter<data> adapter = new Item_data_adapter(getActivity(),data);
list.setAdapter(adapter);}
}
}
Called from my FragmentActivity
//Something
Frag_2 fr = (Frag_2) vPagerAdapter.getItem(1);
if (fr.getView() != null) {
fr.refreshList(data);
}
It works fine until I change the orientation of the screen. Correct me if I'm wrong, but I was searching for hours and I didn't find a solution or a good explanation, the FragmentActivity is created only one time and the Fragments are attached to it but the Fragments recreate on configuration changes.
Now, when the orientation changes I don't get the View from onCreateso when I try to get the View from the Fragment it returns null and my refreshList() method isn't called. How can I fix this?
I fixed the problem this way:
In the onCreate of the FragmentActivity
if(savedInstanceState!=null){
frag1 = (frag_1) getSupportFragmentManager().getFragment(savedInstanceState, frag_1.class.getName());
frag2 = (frag_2) getSupportFragmentManager().getFragment(savedInstanceState, frag_2.class.getName());
}
else{
frag1 = (frag_1) Fragment.instantiate(this,frag_1.class.getName());
frag2 = (frag_2) Fragment.instantiate(this,frag_2.class.getName());
}
fragments.add(frag1);
fragments.add(frag2);
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, frag_1.class.getName(), frag1);
getSupportFragmentManager().putFragment(outState, frag_2.class.getName(), frag2);
}
Maybe it's not the best solution in the universe, but it looks like it works...
When u want to refresh the List do something like this :
public void setView() {
Frag_2 fr = (Frag_2) vPagerAdapter.getItem(1);
fragmentManager.beginTransaction().detach(fr).commit();
fragmentManager.beginTransaction().attach(fr).commit();
}
If you are using a dynamic fragment, you need to test first to prevent creating a second instance of a fragment.
To test whether the system is re-creating the activity, check whether the Bundle argument passed to your activity’s
onCreate() is null.
If it is non-null, the system is re-creating the activity. In this case, the activity automatically re-instantiates existing
fragments.
If it's null you can safely instantiate your dynamic fragment. For example:
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
The Fragment class supports the onSaveInstanceState(Bundle) method (but not the onRestoreInstanceState() method) in much the same way as the Activity class.
The default implementation saves the state of all the fragment’s views that have IDs.
You can override this method to store additional fragment state information.
If the system is re-creating the fragment from a previous saved state, it provides a reference to the Bundle containing that state to the onCreate(), onCreateView(), and onActivityCreated() methods; otherwise, the
argument is set to null.
If you want a detailed info, here's a good talk by Ken Jones of Marakana
I am creating an activity that implements a fragment list and a regular fragment showing details of the list. i am having a VERY hard time trying to take the android developer tutorial on fragment cursor and use it.
I am trying to load the title's from my SQLdatabse into the fragment list. And when the item is clicked show details for it. I know i will just store the id of the item in a extra and pick it out in the details fragment to get the information.
Im just kind of confused on the whole ListFragment class.
Here is my source code where im at so far...
public class Main extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = new getFragmentManager();
if(fm.findFragmentById(android.R.id.content)== null){
CursorLoaderListFragment list = new CursorLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class CursorLoaderListFragment extends ListFragment{
SimpleCursorAdapter mAdapter;
String mCurFilter;
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setEmptyText("No Task");
setHasOptionsMenu(true);
}
}
}
If someone has a better way than what im doing PLEASE let me know and provide an example.
Thanks!