INTRODUCTION
I am using a Database and a ContentProvider to fill a ListFragment. The problem is that ListFragment is not getting refreshed after I delete an item, I have to close and reopen the app to show the added item in the list.
I got this working well before, but after a modification I did in the code, this has started to happen and I don't know how to solve.
DESCRIPTION OF WHAT CAUSES THIS
From my main Activity, I call ListFragment via intent. As a fragment can't be called directly via intent, I call a FragmentActivity which functionality is only to start the ListFragment.
[Main] >> [FragmentActivity] >> [ListFragment]
As I have to handle single or dual pane, I created a layout which contais the Fragment, this is called list_layout.
FragmentActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_layout);
//...
if (mDualPane){
MyPlacesListFragment listFragment = new MyPlacesListFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.list_fragment, listFragment).commit();
}
else {
MyPlacesListFragment listFragment = new MyPlacesListFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(android.R.id.content, listFragment).commit();
}
}
list_layout.xml: (for the single pane view, for the dual pane will be similar but in horizontal orientation and adding a Framelayout container)
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
class="com.final.reversegeocoding.MyPlacesListFragment"
android:id="#+id/list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
The ListFragment uses a ResourceCursorAdapter so I use newVew and bindView methods. I'll only put the code refering to how I inflate the class ass I have checked that other code functionality is well done. Have to say that I inflate android.R.layout.simple_list_item_2 as this is the layout I need to fill the list.
ListFragment:
final class PlacesCursorAdapter extends ResourceCursorAdapter {
private final LayoutInflater mInflater;
public PlacesCursorAdapter(Context context, Cursor c) {
super(context, android.R.layout.simple_list_item_2, c, 0);
//...
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = mInflater.inflate(android.R.layout.simple_list_item_2, parent, false);
//...
CONCLUSION
Before I did't set the setContentView(R.layout.list_layout); in the FragmentActivity and it did it right the refreshing when I deleted an item.
But due to I have to handle single or dual pane, I've been forced to do this and now, when I delete an item, the list refreshes, but because it is contained in the FragmentActivity's list_layout and this is not refreshed until I exit and enter again the activity, the list is not getting refreshed inside of it.
So my question is: How do I modify this code to get the layout refreshing without having to exit the ListFragment?
After modifying your data, call adapter.notifyDataSetChanged(),it will refresh your data
Well, as said in the post I knew that it had something to be with the layouts. I just had to modify the list_layout and the way I call it on FragmentActivity and it has started to work well again.
FragmentActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_layout);
MyPlacesListFragment listFragment = new MyPlacesListFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.list_fragment, listFragment).commit();
}
list_layout.xml: (for the single pane view, for the dual pane will be similar but in horizontal orientation and adding a Framelayout container)
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Framelayout
android:id="#+id/list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Related
I have a ListFragment working without the ListView tag in the XML file resource.
According the following reference, a ListFragment needs an XML file with the ListView tag:
https://developer.android.com/reference/android/app/ListFragment
However, the code below is working fine without one. Does this thread explain why?
Difference between android.app.Fragment and android.support.v4.app.Fragment
Also, I'm working with android-support-v4-app-fragment and android-app-fragment.
Thanks.
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="vertical">
<FrameLayout
android:id="#+id/newFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary">
</FrameLayout>
</LinearLayout>
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragmentA = new FragmentA();
fragmentTransaction.add(R.id.newFrame, fragmentA, "fragmentA");
fragmentTransaction.commit();
}
}
ListFragment:
public class FragmentA extends ListFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String dataArray[] = new String[]{"One", "Two", "Three",};
ListAdapter listAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, dataArray);
setListAdapter(listAdapter);
}
}
The reason it works is because onCreateView in the ListFragment creates a simple ListView for you.
Here is the Android source code for ListFragment:
/**
* Provide default implementation to return a simple list view. Subclasses
* can override to replace with their own layout. If doing so, the
* returned view hierarchy <em>must</em> have a ListView whose id
* is {#link android.R.id#list android.R.id.list} and can optionally
* have a sibling view id {#link android.R.id#empty android.R.id.empty}
* that is to be shown when the list is empty.
*
* <p>If you are overriding this method with your own custom content,
* consider including the standard layout {#link android.R.layout#list_content}
* in your layout file, so that you continue to retain all of the standard
* behavior of ListFragment. In particular, this is currently the only
* way to have the built-in indeterminant progress state be shown.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(com.android.internal.R.layout.list_content,
container, false);
}
Source code for ListFragment
The file list_content.xml contains the following ListView:
<ListView android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
If however your code were to override onCreateView in the ListFragment to provide a custom layout, then your layout would need to explicitly declare a ListView.
Note: It is probably worth mentioning that ListView is currently classified as a "Legacy" view by Android Studio. The majority of new apps should currently be using a RecyclerView under most circumstances.
The following Android Studio command will create a fully working RecyclerView sample list for you:
File -> New -> Fragment (List)
[It currently creates a list derived from Fragment not ListFragment]
I am trying to implement saving and restoring state, but I am running into problems when replacing the main Fragment with a PreferenceFragment and then hitting the back button. My main Fragment consists of a ViewPager with a FragmentPagerAdapter with 3 Fragments to swipe through. None of the Fragment.onCreateView() callbacks for my 3 Fragments are invoked after hitting the back button. I have tried all the solutions I have come over here on SO, but I have not been able to resolve the issue.
Another possibly important thing to note is that the data for my 3 ViewPager Fragments are stored in separate classes which are instanced and accessible through the Activity. The 3 Fragments all contain RecyclerViews for listing a fair amount of data. This is done for cleanliness, but also so that these data persist in the Activity. This is probably not an issue, since it works fine when starting the app, but also since the main issue is that my Fragments are not recreated.
Unexpected behavior:
On application creation, everything works fine, but when I replace my main Fragment (containing a ViewPager with a FragmentPagerAdapter) with another one and then pressing the back button, the Fragments in the ViewPager are not recreated. The onCreateView() of my main Fragment is called.
Questions:
What am I missing? Is there some other callbacks that should be created? Where and how should I recreate the Fragments?
What have I tried:
Changed the FragmentAdapter in use, but I should really use getSupportFragmentManager() as was the solution here.
EDIT: Use of erroneous FragmentManager actually was the source of my troubles. See my answer below.
Add
#Override public int getItemPosition(Object object) {return POSITION_NONE;} to the FragmentPagerAdapter, as suggested here.
Change from FragmentPagerAdapter to FragmentStatePagerAdapter which should not matter here, as far as I understand. The internals of FragmentStatePagerAdapter actually keeps the Fragments, but they are not rendered anew.
Add the main Fragment back using a FragmentTransaction when its onCreateView() is called and in several of the other callbacks of that Fragment, see MMMainFragment below.
Various other things.
My MainFragment class with corresponding XML layout
public class MMMainFragment extends Fragment
{
private MMViewPager mMMViewPager = null;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Note that this method IS called when the back button is pressed.
// I have tried setting the content back to this instance in several places.
View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
// Setup ViewPager.
mMMViewPager = (MMViewPager) view.findViewById(R.id.mm_pager);
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
tabLayout.setupWithViewPager(mMMViewPager);
return view;
}
public MMViewPager getMMViewPager()
{
return mMMViewPager;
}
}
mm_main_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.TabLayout
android:id="#+id/mm_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />
<com.mycompany.myapp.gui.mmpager.MMViewPager
android:id="#+id/mm_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Then, in Activity.onCreate() i simply do:
// Insert Main Fragment.
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.mm_content_frame, new MMMainFragment())
.commit();
where mm_content_frame is a FrameLayout where I replace and remove Fragments. Then, when a button to view the preferences are pushed, I run the following snippet below in the Activity. I add this Fragment to the backstack to be able to use the back button.
public void showSettingsFragment()
{
getSupportActionBar().setTitle(getString(R.string.mm_drawer_settings));
getSupportFragmentManager().beginTransaction()
.replace(R.id.mm_content_frame, new MMPreferencesFragment(), FragmentConstants.SETTINGS_FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
The MMViewPager clas:
public class MMViewPager extends ViewPager
{
private MMActivity mMMActivity;
private MMPagerAdapter mMMPagerAdapter;
public MMViewPager(Context context, AttributeSet attrs)
{
super(context, attrs);
mMMActivity = (MMActivity) context;
mMMPagerAdapter = new MMPagerAdapter(mMMActivity.getSupportFragmentManager(), mMMActivity);
this.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
this.setAdapter(mMMMPagerAdapter);
this.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
}
}
The MMPagerAdapter class, currently a FragmentStatePagerAdapter:
public class MMPagerAdapter extends FragmentStatePagerAdapter
{
private MMActivity mMMActivity;
public MMPagerAdapter(FragmentManager fragmentManager, MMActivity mmActivity)
{
super(fragmentManager);
mMMActivity = mmActivity;
}
#Override
public Fragment getItem(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES:
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST:
return new ShoppingListFragment();
default:
return null;
}
}
#Override
public int getCount()
{
return PagerConstants.NUMBER_OF_PAGES; // 3
}
#Override
public CharSequence getPageTitle(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return mMMActivity.getResources().getString(R.string.mm_title_recipe_filter_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES:
return mMMActivity.getResources().getString(R.string.mm_title_selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST:
return mMMActivity.getResources().getString(R.string.mm_title_shopping_list_fragment);
default:
return "Tab";
}
}
}
For reference, here is also the main layout of the Activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mm_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Source: http://developer.android.com/training/implementing-navigation/nav-drawer.html -->
<!-- The main content view -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Source: http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html -->
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mm_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:layout_alignParentTop="true"
style="#style/MMActionBar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
<!-- The FrameLayout where I replace Fragments -->
<FrameLayout
android:id="#+id/mm_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/mm_toolbar"/>
</RelativeLayout>
<ListView
android:id="#+id/mm_drawer_listview"
android:layout_width="260dp"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingStart="16dp"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#color/mm_white" />
</android.support.v4.widget.DrawerLayout>
After struggling with this for a few days I realized that the problem actually was that I passed the wrong FragmentManager to the ViewPager. It is indeed the FragmentManager returned by Fragment.getChildFragmentManager() that should be used. This makes sense, since it is the Fragment itself that stores the state of the child Fragments in the ViewPager. I am not sure why I couldn't make that work before, but now the LifeCycle of the app works fine with the following setup in my main Fragment's onCreate() method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
mMMActivity = (MMActivity) container.getContext();
mMMActivity.getSupportActionBar().setTitle(getString(R.string.mm_toolbar_title_main_fragment));
// Setup ViewPager.
mViewPager = (ViewPager) view.findViewById(R.id.mm_pager);
mAdapter = new MMPagerAdapter(getChildFragmentManager(), mMMActivity); // <-- This is the key
mViewPager.setAdapter(mAdapter);
mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
mViewPager.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
// Setup TabLayout.
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
tabLayout.setupWithViewPager(mViewPager);
return view;
}
This was also the solution of another question. As a side note, I use this solution to get the Fragments in my ViewPager and it works just fine with the LifeCycle.
Test Application:
In the research of this issue I created a test application for this. The ones interested can clone it from here. It has ugly colors.
As far as, I understand you have solved your problem. I have faced a quite similar issue in the past. Maybe this help others trying something similar.
The thing that helped me in order to retain my fragment state was to replace the
#Override
public Fragment getItem(int position)
{
switch (position)
{
case PagerConstants.PAGE_FILTER_RECIPES:
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES:
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST:
return new ShoppingListFragment();
default:
return null;
}
}
method of my adapter.
I change the Adapter so as to keep a HashMap of Fragment. This way the fragments are created once and then retrieved from the HashMap. Depending on your structure you can use an ArrayList instead of the HashMap.
This is done in an efficient and "smart" way of implementing getItemPosition(Object object) to return POSITION_NONE; or the proper position of the item.
I don't really remember if I had the getChildFragmentManager in the adapter or not.
I guess that I could dig into my filesystem if you need further information :)
I asked a question about how to add a Fragment that contained something drawn using OpenGL ES
here. Someone was kind enough to answer that for me, but unfortunately today I encountered another problem. As I mentioned in my other question, my purpose is to add other Fragments next to the one that contains OpenGL and because I am a beginner in Android development I don't seem to understand how this is done.
Here's what I want: right now, my code is exactly the one from my other question. I also have this Fragment:
public class TextFragment extends Fragment
{
private TextView textview;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.text_fragment,
container, false);
textview = (TextView) view.findViewById(R.id.textView1);
return view;
}
}
together with its layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frag2">
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Fragment Two"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
and I want to add this to my main activity, where right now I only have the OpenGL Fragment. Here's my main activity:
public class FragmentExampleActivity extends FragmentActivity implements ToolbarFragment.ToolbarListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container, new OpenGLES20ActivityFrag())
.addToBackStack(null)
.commit();
}
}
}
and the Fragment that has OpenGL in it and that I have already added to the main activity:
public class OpenGLES20ActivityFrag extends Fragment
{
private GLSurfaceView mGLView;
public OpenGLES20ActivityFrag()
{
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mGLView = new MyGLSurfaceView(this.getActivity());
return mGLView;
}
}
What I tried and failed: using another call to the .add method inside getSupportFragmentManager() or adapting this bit of code for my second Fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frag2, TextFragment)
.addToBackStack(null)
.commit();
that gave me an 'expression expected' error in the add method. I tried adding this constructor to my second Fragment
public TextFragment()
{
super();
}
and then inside the add method I put .add(R.id.frag2, new TextFragment())
which still didn't work.
In order to dynamically add a Fragment to a layout, what you need is a container (like in your case, it was R.id.main_container). Thus, if you want to add multiple fragments, what you need is multiple containers, like so:
<LinearLayout android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent">
<FrameLayout android:id="#+id/main_container_1" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
<FrameLayout android:id="#+id/main_container_2" android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent"/>
</LinearLayout>
(this snippet is from How to split the screen with two equal LinearLayouts? )
And then you would need to add the two Fragments:
if (savedInstanceState == null)
{
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_1, new OpenGLES20ActivityFrag())
.commit();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_container_2, new TextFragment())
.commit();
}
Please note that with multiple Fragments on a single Activity, it's better not to add them to the backstack, because then you'd have to press Back as many times as there are Fragments, and in this case it's more reasonable to navigate between the "views" or states of the application with Activities, and not by replacing the Fragments.
(considering the backstack doesn't change, I don't think the backstack listener needs to be removed, but that's done so that if you press Back, you don't end the Activity, but the Fragments within it first if you have them added to the backstack. But the Activity doesn't end when it contains no fragments, and you'd have an "empty view", hence why that was added.)
Please also check if the rotation works and data is maintained even after the activity reconstruction, because there's a chance you need to set the retain instance state to true explicitly on the Fragments for that to work.
I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.
For navigation right now I have this drop down menu which when an item is clicked launches a new activity:
The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.
My activity that I plan to run the draw from I have a draw layout called fragment_main:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<ListView
android:id="#+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:choiceMode="singleChoice"/>
</android.support.v4.widget.DrawerLayout>
and my activity which loads the drawer layout is:
public class MainDraw extends FragmentActivity {
final String[] data ={"one","two","three"};
final String[] fragments ={
"com.beerportfolio.beerportfoliopro.FragmentOne",
"com.beerportfolio.beerportfoliopro.FragmentTwo",
"com.beerportfolio.beerportfoliopro.FragmentThree"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//todo: load statistics
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
#Override
public void onDrawerClosed(View drawerView){
super.onDrawerClosed(drawerView);
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
tx.commit();
}
});
drawer.closeDrawer(navList);
}
});
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
tx.commit();
}
}
IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?
Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!
First of all read the well written documentation, it answers to your doubts.
I would share my personal pattern to convert existing Activity to Fragment
Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface
public interface FragmentActivityStatus {
public boolean isDrawerOpen();
}
The activity
public class MainActivity extends Activity implements FragmentActivityStatus {
#Override
public boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(drawerList);
}
}
The fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivityStatus = (FragmentActivityStatus)activity;
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
super.onPrepareOptionsMenu(menu);
}
Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()
final Class<?>[] fragments = {
FragmentOne.class,
FragmentTwo.class,
FragmentThree.class};
Then
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
fragments[pos].getName()));
tx.commit();
I have an activity that has a ViewPager in the layout.
I have two fragments which display, one for each tab.
One of the fragments is designed to host other fragments - this is the CustomerMainFragment which inflates fragment_customer_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lyt_customer_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/fragment_customer_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
This then adds CustomerSearchFragment to the FrameLayout which inflates fragment_customer_search.
CustomerSearchFragment also has the following override to switch out the search fragment for a detail fragment on a button press:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btnSearch1 = (Button) view.findViewById(R.id.button1);
if (btnSearch1 != null) {
btnSearch1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// This is important bit
Fragment customerDetailFragment = new CustomerDetailFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.fragment_customer_content, customerDetailFragment).commit();
}
});
}
}
After clicking the button I get the following error:
java.lang.IllegalArgumentException: No view found for id 0x7f080006
(com.chrisbeckyapps.sample:id/fragment_customer_content) for fragment
CustomerDetailFragment{4280b0b8 #0 id=0x7f080006}
I'm new to fragments and understand the concepts, but I'm stumped by this. I originally had the search fragment going straight into the pager, but then replacing it with the detail fragment mean it just showed over the top, and my research led to this being a better solution.
I have wondered about trying to move the search logic to the CustomerMainFragment but this means hooking up a lot of logic and I thought you could embed logic within fragments.
Suggestions?
Sorry, just found such a simple fix.
In my onclick handler, I just had to change from getChildFragmentManager to getFragmentManager