I have got a project which was developed by another person. In that there is a fragment but I cannot find who is instantiating it. Is it possible that a fragment could be created by the main activity through layout xmls? That is, without instantiating it in Java code using" new Fragment", is it possible to instantiate it through some xml or something? Because I have checked the Fragment constructor usage and it also doesn't show any other class calling it. But when I debug the code, the fragment does get called. If its possible, how can I pass arguments from an activity to this fragment? Because if its created using "Fragment f = new Fragment" I could use the setArguments method or even pass it through constructor. But on a situation like this, how can I pass values from an activity to this fragment? Please advice.
Yes, it's possible, see docs. For instance, from that page:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
Is it possible that a fragment could be created by the main activity
through layout xmls?
Yes, this is a static Fragment. If you check the layout files, you should see a <fragment /> tag:
<fragment
android:id="#+id/example_fragment"
android:name="com.example.staticfragexample.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
There will then be a corresponding MyFragment class. To access it, you would use getFragmentManager().findFragmentById(R.id.example_fragment);
You are saying that the fragments are not instantiated through java code. That means they must have been declared in xml and are instantiated as soon as the activity gets created like this -
<fragment android:name="com.test.SampleFragment"
android:id="#+id/sample_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In this case you can't use setArguments() , but instead use some other mechanism to send data to the fragments .
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'm adding a fragment called ZipInputFragment within the XML file of one of my activity's.
<fragment
android:id="#+id/zipCodeFragment"
android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.markf.application.ZipInputFragment"
tools:layout="#layout/fragment_zip_input" />
This runs fine. The issue is that I have a separate package called non_activity where all of my fragments are stored. So when I place the ZipInputFragment in this package, my application crashes. I know that it's because of this line of code in my widget:
class="com.markf.application.ZipInputFragment"
The issue is that it's not acknowledging the fact that my Fragment is now in a separate package. Does anyone the syntax that would include my non_activity package in the widget? Thank you.
just use this instead then:
<fragment
android:id="#+id/zipCodeFragment"
android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.markf.application.non_activity.ZipInputFragment"
tools:layout="#layout/fragment_zip_input" />
You just need to add the fully qualified package name to get it to work.
In the following snippet,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="#+id/titles" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
</LinearLayout>
what is the class attribute, if it is the our Fragment class, then why is FragmentLayout$ appended to its name, and why is it without android or any namespace?
I can totally see that it is the name of the Fragment class, because TitlesFragment is the Fragment class in the example I took this code from, and com.example.android.apis.app tells me that we have to give this attribute a fully qualified name of the class, but what is FragmentLayout$ part appended to the name of the class, that is TitlesFragment?
I have taken this code from the example in this developer guide, but there is no explanation of the class attribute. I could not find anything relevant here in the reference either. Pardon me if it is something too fundamental.
It does refer to the Fragment class. FragmentLayout$TitlesFragment means that TitlesFragment is a public, static nested class in the FragmentLayout class. If you inspect that class, you'll see that it's a ListFragment subclass.
public static class TitlesFragment extends ListFragment {
The only reference to that notation I've ever seen in the developer pages is on the Custom Components page. (It's under sub-heading 4. Use the Custom Component.)
I am developing an application using 3-pane view layout (a classic “master-detail” flow), following a 2-pane example created by mobile tuts .
The 3-pane layout looks as follows:
<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:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:background="?android:attr/detailsElementBackground"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
android:showDividers="middle"
tools:context=".SListA" >
<!--
This layout is a three-pane layout for the
master/detail flow.
-->
<fragment
android:id="#+id/s_list"
android:name="com.xxxxx.xxxxxx.SListF"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/s_events"
android:paddingLeft="4dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/s_details"
android:paddingLeft="4dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5" />
I have encountered a problem when trying to replace the third fragment panel in the above 3-pane view (in wide/landscape tablet mode). The error is in the last line of code below, to be executed from the middle panel fragment code:
#Override
public void setEventKey(String event_key) {
if (SListA.m3Pane) {
// In three-pane mode, show the details view in this activity by
// adding or replacing the details fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(SDetailsF.ARG_EVENTKEY, event_key);
SDetailsF fragment = new SDetailsF();
fragment.setArguments(arguments);
getFragmentManager().beginTransaction()
.replace(R.id.s_details, fragment).commit();
}
The last line of code above shows a compile error:
Cannot make a static reference to the non-static method
getFragmentManager() from the type Fragment
(Note, I have set the minSdkVersion=11).
An identical code is used to replace the fragment for the second (middle) panel, and I don't understand why it is not working for the third panel. The only difference is, that the replace fragment code for the second (middle) panel is run from within the (first panel) activity code, instead of the (second panel) fragment code.
I was able to replace the above problem code with the following code, without getting the compile error:
fragment.getFragmentManager().beginTransaction()
.replace(R.id.s_details, fragment).commit();
however, this code crashes when executed during the run with an InvocationTargetException message:
"Source not found"
Any ideas how to correct this issue would be highly appreciated.
You must use interfaces. whenever you need to communicate between fragments you must declare an interface in each fragment and that interface has to be implemented by the activity holding it. then on some event within the fragment, you call the method implemented by the activity. and then in that method you carry on neccesary action(in your case to change/replace the fragment) which must reflect in other fragments.
Check this answer of mine. its similar to which you have to follow.
https://stackoverflow.com/a/15296370/1567588
I examine code example from FragmentBasics.zip
http://developer.android.com/training/basics/fragments/communicating.html
There was this code (MainActivity.java):
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
But I never find *article_fragment* declared/set anywhere in the whole classes or layouts(xml) or values. Where does it come from??
This is the XML code used in res\layout-large\news_articles.xml:
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
The fragment with the android:name="com.example.android.fragments.ArticleFragment attribute has the id set as: #+id/article_fragment
The JAVA code where you find the id article_fragment being used is on line 55 for this method: public void onArticleSelected(int position). It checks if your are using a two-pane layout. That is why the XML file mentioned at the top, is in the layout-large foldr (In the example).
R.id.article_fragment layout is used for large screen device in current example
you can find out this layout inside res/layout-large/news_articles.xml
Check the ArticleFragment class in the Project.That class is extend the Fragement and in that class they are inflating xml named article_view.xml.
So,basically they are finding the ID of that class which is extends the Fragment.