I am trying to understand the following fragment code:
public class FragmentA extends Fragment {
public FragmentA(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if(container!=null)
// WHY IS THIS CODE EXECUTED? I did not set container variable
// why if I pass null insted of Container, I get the same result?
return inflater.inflate(R.layout.fragment_a,container,false);
}
}
This ViewGroup container I never initialized or something. How does android know what is my container?, if I never initialized it.
The other thing that is not clear to me is when I call inflate and if instead the container I write null, the result is same.
return inflater.inflate(R.layout.fragment_a,null,false);
I am working with ViewPager in my main activity.
I create the fragment is this way in the FragmentPagerAdapter:
Fragment fragment = new FragmentA();
These are my xml files:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"/>
and for the fragment :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFCC00" >
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:text="THIS IS FRAGMENT A" />
</FrameLayout>
EDIT:
I found this on the developer site:
The container parameter passed to onCreateView() is the parent
ViewGroup (from the activity's layout) in which your fragment layout
will be inserted. The savedInstanceState parameter is a Bundle that
provides data about the previous instance of the fragment, if the
fragment is being resumed (restoring state is discussed more in the
section about Handling the Fragment Lifecycle).
But, is still not clear for me. I did not pass any ViewGroup to the Fragment in the Fragment call....
The container is supplied externally to the fragment. It's either generated automatically when you use the <fragment> tag in xml, or it's passed as part of the FragmentTransaction when you initialize the fragment in code.
Related
I have an SchoolActivity that has two buttons:
-Primary (adds the PrimaryFragment)
-Secondary (adds the SecondaryFragment)
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.yuv.mycollege.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="#+id/button_primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Primary"/>
<Button
android:id="#+id/button_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Secondary"/>
</LinearLayout>
<!-- Main content area for fragments-->
<FrameLayout
android:background="#color/colorPrimary"
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="4dp"/>
</LinearLayout>
Both the fragments has content and footer area, which are themselves are fragments (PrimaryContentFragment, PrimaryFooterFragment, SecondaryContentFragment, SecondaryFooterFragment)
I am adding the fragments from the activity using:
public void onClick(View view) {
Button button = (Button)view;
Toast.makeText(this, "Going to Add Children", Toast.LENGTH_SHORT).show();
switch(view.getId()) {
case R.id.button_primary:
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_container, new PrimaryFragment())
.addToBackStack("primary")
.commit();
break;
case R.id.button_secondary:
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_container, new SecondaryFragment())
.addToBackStack("secondary")
.commit();
break;
}
}
And, finally adding the each children fragments using:
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#color/colorAccent"
android:layout_weight="8"
></FrameLayout>
<FrameLayout
android:id="#+id/footer_container"
android:background="#color/colorPrimaryDark"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
></FrameLayout>
</LinearLayout>
The children fragments adding:
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.primary_fragment, container, false);
getChildFragmentManager()
.beginTransaction()
.add(R.id.content_container, new PrimaryContentFragment())
.add(R.id.footer_container, new PrimaryFooterFragment())
.addToBackStack("primarychildren")
.commit();
return view;
}
I am adding the similar logics for the another fragment also and so on for the rest which is working.
THE PROBLEM:
The solution as stated is working but seems as very raw naive approach. Could anybody suggest the better architecture I could follow such as:
all the fragments (Primary/Secondary/...) uses the same designs so
can I create some base class to inherit common features
all the footer are similar for all of fragments but with simple text change
(might be like breadcrumb) So, Can I use same footer for all
fragments with some settext method ...
how can I effectively communicate between activity and fragments
BEING A NEW ANDROID DEV, MY ONLY CONCERN IS AM I DOING THE RIGHT WAY !!!
Try this using bundle :-
ContentFragment content=new ContentFragment();
content.setArguments( ( new Bundle()).putString("value","primary"));
getChildFragmentManager()
.beginTransaction()
.add(R.id.content_container, content)
.add(R.id.footer_container, new PrimaryFooterFragment())
.addToBackStack("primarychildren")
.commit();
Similarly For secondary use :-
ContentFragment content=new ContentFragment();
content.setArguments( ( new Bundle()).putString("value","secondary"));
Use same content and footer fragments just set the texts by using bundle arguments
All the fragments (Primary/Secondary/...) uses the same designs so can
I create some base class to inherit common features
If they are using the same design and with slight changes in their content, then there's no need of create different Fragment classes. You can just pass necessary values from your calling Activity or Fragment to populate the contents in each of your child fragments.
All the footer are similar for all of fragments but with simple text
change (might be like breadcrumb) So, Can I use same footer for all
fragments with some settext method ...
If they are just simple text changes, then please use the setArguments and getArguments methods to pass values between Fragments rather creating different Fragment classes.
Here's how you can pass values between fragments. And here's how you can pass data from your Activity to Fragment.
How can I effectively communicate between activity and fragments
Please follow the two links above to communicate between Activity and Fragment.
Update
Following up the comment, as you have said that the PrimaryFragment and SecondaryFragment are mostly likely, I would suggest you to have one single Fragment having all these. Instead of having PrimaryFragment, SecondaryFragment and a CommonFragment, you might consider a single Fragment having the footer Fragment as well. When you are about to launch an instance of that Fragment, just pass necessary values to populate data as the contents of those Fragment.
Please let me know if I have not clarified enough.
I am building a view with 2 fragments inside it. Inflating the layout works fine. But I got another problem. Before creating these fragments I need to pass some data to those fragments (FragmentListSchema and FragmentSchemaDetail).I thought of application variables but I guess that's not the proper way. How can I do it ?
Thanks in advance
Inflate :
public class FragmentSchemaTotal extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_schema,container);
return view;
}
}
Layout :
<?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">
<fragment android:name="kine.gui.FragmentListSchema"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="kine.gui.FragmentSchemaDetail"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
Use Fragment setArguments to pass data
FragmentSchemaTotal fragment = new FragmentSchemaTotal();
Bundle bundle = new Bundle();
bundle.putString("data","your data");
fragment.setArguments(bundle);
Why not use loaders inside of the fragments to have them load the data themselves? The usefulness of the Loader kind of depends on what type of data you are dealing with, though. It's tough to suggest the proper way without knowing more.
If Loaders don't fit the bill, you could have the activity find the fragment and set the data on them. Until that happens, the fragment could display a spinner instead of it's view. Once the data is set, the fragment can then display it's views.
The last alternative is to create the fragments in code and pass arguments to them. This isn't quite as flexible though. I would recommend the loader.
I had a ListActivity where when I pressed one button, I started an intent showing another ListActivity. Now, I have to change these ListActivities to ListFragments. I do this to inflate the layout:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/**Inflate the layout for this fragment*/
return inflater.inflate(R.layout.recent_calls, container, false);
}
And to start the intent, now, as I a fragment, I do this:
CallDetailActivity Fragment_detail = new CallDetailActivity();
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.recent_calls, Fragment_detail);
transaction.addToBackStack(null);
transaction.commit();
My question is, where I put R.id.recent_calls in replace, I'm calling to the FrameLayout id of the same layout that I have initialized with the onCreateView. Is this ok? Or there would be another way to replace the actual layout with another when using fragments? something like the intent does for the activites.
UPDATE--
I'm having an error on the .replace as it is showing me "The method replace(int, fragment) in the type fragmenttransaction is not applicable for the arguments (int, CallDeateilActivity)"
UPDATE 2--
<FrameLayout
android:id="#+id/recent_calls"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"/>
<TextView
android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Call log is empty"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"/>
Seems like you've mixed Fragments from support library and Fragments from native android.
You have to choose what min. version of sdk you want to support and only then decide what Fragments to use.
My question is, where I put R.id.recent_calls in replace, I'm calling to the FrameLayout id of the same layout that I have initialized with the onCreateView. Is this ok?
I prefer to use more OOP - you can create one controller that handle translations etc from you fragment send callback to switch fragments.
Check this question too: Fragment add or replace not working
Common Fragment to be used in 2 Activity's
public class Ads extends Fragment {
private View rootView ;
private MoPubView adView ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.ads, container, true) ;
adView = (MoPubView) rootView.findViewById(R.id.adView) ;
adView.setAdUnitId(LogoQuizUtil.MOPUB_AD_UNIT);
adView.loadAd();
return rootView;
}
}
Fragment layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ad_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom" >
<com.mopub.mobileads.MoPubView
android:id="#+id/adView"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:gravity="bottom" />
</LinearLayout>
Other Layout where I include this fragment
<fragment
android:id="#+id/ads"
android:name="myPackage.Ads"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom" />
I get a dont get a exception when I call setContentView (R.layout.ads) the first time, when the second activity calls the fragementactivity (Ads) I get a exception at setContentView (R.id.ads).
Exception I get is
08-07 09:38:30.359: W/System.err(975): java.lang.RuntimeException: Unable to start activity ComponentInfo{mypackage/mypackage.SecondActivity}: android.view.InflateException: Binary XML file line #263: Error inflating class fragment
Lets start at the beginning. An activity is not a fragment and a fragment is not an activity.
You need an activity to contain/control fragments. To do that you extend Activity (for Honeycomb+ development) or use the support library and extend FragmentActivity. In this class you would use setContentView to set the layout that will contain the fragment(s).
You then, from that class, call the fragment manager (getFragmentManager or getSupportFragmentManager depending on which class you have extended). The fragment manager is then used to create, attach and detach the fragments.
The fragment, not being an activity, is coded differently. It should have an onCreateView method that inflates the layout for the fragment and returns it to the fragment manager from the activity.
Then it is typical to use onActivityCreated for the rest of your code (or whatever else is needed).
Making the changes to use fragments properly may or may not solve all of your problems, as I see you are using some kind of custom widget in your layout and the issue could be there too.
I'm currently getting into the fragment API of the Android 3.0 Preview and have built the following minimal coding:
I have an Activty, which shall embed Fragment(s), which is currently implemented like this:
public class Cockpit extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cockpit);
}
public static class InfoFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
ViewGroup infoFragmentRoot = (ViewGroup) getActivity().findViewById(
R.id.infoFragmentRoot) ;
return inflater.inflate(R.id.infoFragment, container, false);
}
}
}
The corresponding layout of the activity:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<fragment android:name="test.android.ui.cockpit.Cockpit$InfoFragment"
android:id="#+id/infoFragment"
android:layout_weight="1"
android:layout_width="10dp"
android:layout_height="match_parent" >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent" android:padding="12dp" android:id="#+id/infoFragmentRoot" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
/>
</LinearLayout>
</fragment>
Now, I do not understand why the ViewGroup container in the onCreateView() in the internal class InfoFragment is a nullpointer, nor do I understand,
why
ViewGroup infoFragmentRoot = (ViewGroup) getActivity().findViewById(
R.id.infoFragmentRoot) ;
returns also null.
Thanks for feedback.
You've got a few problems here. First of all, you don't want to be adding tags inside of the <fragment> tag. Think of the fragment tag as a placeholder. The fragment's onCreateView() method is responsible for defining the view hierarchy of your fragment, not the activity's layout XML file. What you can do though is create a separate layout XML file to be just the fragment's layout. Then inside of onCreateView(), you take the inflater passed in, and do something like this:
View v = inflater.inflate(R.layout.frag1, container, false);
TextView text1 = (TextView) v.findViewById(R.id.text1);
text1.setText( myTextData );
return v;
Notice that the attach parameter to inflate() is false? Android will take care of attaching the returned view to your container later.
Your activity's view hierarchy is not guaranteed to be in existence until the fragment gets the onActivityCreated() callback. So the attempt to get infoFragmentRoot could return null inside of onCreateView(). But I'm not even sure what is going on when that tag is buried inside of your <fragment>.
In this particular case, where you've embedded the tag in your activity's layout, the onInflate() callback of your fragment will be called with the rest of the attributes from your tag. The theory is that you could add those attributes to the arguments bundle on your fragment, then retrieve those values later in onCreateView() (using setArguments() and getArguments()). I say in theory because it appears there's a bug in the code that handles a configuration change (e.g., landscape to portrait), resulting in onInflate() being called after onCreateView() when the fragment is being reconstructed after a configuration change. See defect report http://code.google.com/p/android/issues/detail?id=14796.
For now, I recommend you extract your fragment's layout to a separate layout XML file (e.g., frag1.xml), the use my code above to inflate that layout in onCreateView(). And don't worry about any attributes being passed to onInflate().
You also do not want to use onCreate to instantiate your layout, that all will be handled within the parent activity. Saving the bundle is about all ive done there so far