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.
Related
This question already has answers here:
findViewById in Fragment
(37 answers)
Closed 2 years ago.
How to access an attribute in the activity xml?
I want to use it in fragment.
for example : this is my activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:fab="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
fab:layout_constraintTop_toTopOf="parent"
fab:layout_constraintLeft_toLeftOf="parent"
fab:layout_constraintRight_toRightOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/layTitle"
android:layoutDirection="rtl"
android:background="#color/colorPrimary"
android:padding="#dimen/paddingTitle"
android:elevation="#dimen/elevationActivityTitle"
android:layout_alignParentTop="true">
<ImageView
fab:layout_constraintLeft_toLeftOf="parent"
fab:layout_constraintTop_toTopOf="parent"
fab:layout_constraintBottom_toBottomOf="parent"
android:layout_width="#dimen/imgActivityTitle"
android:layout_height="#dimen/imgActivityTitle"
android:id="#+id/btn_back"
android:src="#drawable/ic_back"
android:elevation="5dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="#+id/frag"
android:layout_width="0dp"
android:layout_height="0dp"
fab:layout_constraintBottom_toBottomOf="parent"
fab:layout_constraintTop_toBottomOf="#+id/layTitle"
fab:layout_constraintLeft_toLeftOf="parent"
fab:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
I want findViewByid ImageView in fragment
When I was searching for solution I got the following one:
TextView btn_back= (TextView) getView().findViewById(R.id.btn_back)
But, unfortunately it was not working for me.
You can use the interface to do it :
Create an interface names IBtnClick with one void method: "void onBtnClicked();"
use interface in activity like this:
IBtnClick iBtnClick= (IBtnClick)getActivity();
here getAtivity is our context, if it doesn't work use another scope context.
and use your method in button click listener like this
iBtnClick.onBtnClicked();
Now in your fragment you have to implements interface and
you can add any action you want in Overrided method.
Use either viewModel or interfaces to talk between activity and fragment. don't try to send the button to fragment but trigger it from there. That will prevent any leaks or null pointers.
Ok. You have some options.
Firstly, you can access these variables to directly in fragment like that.
MainActivity.java
TextView backButton;
backButton = findViewById(R.id.btn_back);
Fragment.java
((MainActivity) getActivity()).backButton
You can get current activity with getActivity() but Java compiler cannot understand which activity so have to cast to MainActivity.
Secondly, you can pass your activity instance to fragment. You can use constructors and newInstance() function. You can read that post Best practice for instantiating a new Android Fragment
In my opinion, I prefer second option because you guarantee these variables that include activity class. Some devices occur errors at first option. But second option uses more ram because you are passing your variables to another class.
The choice is yours
Try This code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.recharge_plane_fragment, container, false);
alertdialog_Listview=view.findViewById(R.id.alertdialog_Listview);
return view;
}
You can declare your TextView btn_back as static in activity
public static TextView btn_back;
btn_back = (TextView) findViewById(R.id.btn_back);
and get it in fragment
TextView btn_back = ((MainActivity)getActivity()).btn_back;
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 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.
I need help for my first Android application. I have a single activity with an Action Bar at the top and Navigation Tabs. Because of these navigation tabs I had to convert my Activities into Fragments.
So one of those Activities (which is now a Fragment) held two fragments, one ListFragment on the Left and a DetailFragment on the right, so that the user is able to select an item from the list and directly sees details on the right.
But now as the Activity has been transformed into the Fragment, it's the Fragment that holds the List- and the DetailFragment. This works fine until I want to change the DetailsFragment.
First of all, here is the code of the Fragment holding the other two:
public class BlackboardFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return inflater.inflate(R.layout.fragment_layout, container, false);
}
}
Here is the fragment_layout.xml file (saved in layout-land):
<?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:id="#+id/interfaces"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_marginRight="20dp"
android:layout_weight="0.60"
class="org.fawkesrobotics.app.InterfaceFragment" />
<FrameLayout
android:id="#+id/viewer"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
So, the ListFragment is named InterfaceFragment, here is the code:
public class InterfaceFragment extends ListFragment {
boolean mDualPane;
// some other code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
create_list();
View viewerFrame = getActivity().findViewById(R.id.viewer);
mDualPane = viewerFrame != null
&& viewerFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
// some other code
}
This is, where it doesn't work. The Fragment actually shows the list (that is created in create_list() where the ListAdapter and everything else is set), but my problem is, that viewerFrame is null. I declared the id viewer in the xml file above, so I don't understand why he cannot find the id. Does it "belong" to the BlackBoardFragment or to my overall activity? Because in BlackboardFragment I also tried it with getActivity().findViewById and it worked. Why is InterfaceFragment not able to find it?
I also tested, if he really uses the fragment_layout.xml in layout-land and he does. So he should know the id viewer, but obviously doesn't.
If you need more code or the code from the DetailsFragment, I can post it.
Thank you in advance for your help! :)
use FragmentManager to get Fragment......
ExampleFragment fragment = (ExampleFragment) getActivity().getFragmentManager().findFragmentById(R.id.example_fragment);
Nested Fragments are not supported by the Android SDK. That is, you can't have a "fragment within a fragment" in your Android application. Sorry.
EDIT:
Nested Fragments are now part of the official Android 4.2 SDK! Yay!
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