Is that possible I can add a fragment view on the activity view without specifying "fragment" view component in activity's layout xml file? Which function should I look for?
Well, the UI of the fragment has to go somewhere. If you want the entire "content view" to be the fragment, add the fragment to android.R.id.content:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getSupportFragmentManager().findFragmentById(android.R.id.content)==null) {
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new ToDoRosterListFragment())
.commit();
}
}
Otherwise, somewhere in the activity's view hierarchy, you need a container (usually a FrameLayout) in which to place the fragment's UI. Typically, we do that by putting the container in the layout resource.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, MyFragment.newInstance())
.commit();
//Some of your code here
}
android.R.id.content is the container of entire app screen.
It can be used with Fragment:
The android.R.id.content ID value indicates the ViewGroup of the entire content area of an Activity.
The code above will insert the View created by Fragment into the ViewGroup identified by android.R.id.content.
Simply, while creating a fragment we have to replace or add fragment's view with a view present in our application. To replace or add a fragment's, we normally add a Framelayout or any other layout view(as a fragmentview container) in activity or in a fragment.
Now, If you want to replace or add a fragment's view without adding a extra view container in your activity. You can simply do it by accessing the view's provided by AppCompatActivity or Activity.
Now, you can create a fragment, without adding a view container in your activity you can create as,
YourFragment fragment = new YourFragment();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, fragment); //here, android.R.id.content is a view on which your fragment's view is replaced
transaction.commit();
You need to have a layout in your activity to contain the fragment (preferably a FrameLayout).
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:name="com.gdevelopers.movies.movies.FragmentMoreMovies"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Then in activity put this code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new YourFragment())
.commit();
}
}
If you don't want to go with #CommonsWare answer, then you'll have to provide a container by code. then function you need is....
setContentView(View)
Yep. If you check the activity class code, you'll see that setContentView can be called with an INT (layouts are identified with ints), or with Views. Therefore, you can create a viewgroup instance on the fly, keep a reference to it (you would need to do the same with an XML generated view), and add your fragments there. This is possible because XML files are just arguments which the view factory class, Inflater, uses to find which view subclasses has to instantiate, using a set of parameters provided in the XML. And obviously, you can do that by hand. Just pick whatever layout class you want to use, for example, FrameLayout, and:
public class Activity extends AppCompatActivity{
private FrameLayout root;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
root = new FrameLayout(this);
root.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
setContentView(root);
//go on
}
}
use android container instead of custom container like
fragmentTransaction.replace(android.R.id.content,yourFragment);
1.use getWindow().getDecorView() to get a DecorView(FramLayout)
2.add a container view to DecorView
3.add Fragment to the container view
LinearLayout llRoot = findViewById(R.id.parent);
FragmentManager fragMan = getSupportFragmentManager();
FragmentTransaction fragTransaction = fragMan.beginTransaction();
YourFragment yourFragment = (YourFragment)fragMan.findFragmentByTag("yourFragment");
if (yourFragment == null) {
yourFragment = new YourFragment();
}
fragTransaction.replace(llRoot.getId(), yourFragment, "yourFragment");
fragTransaction.commit();
llRoot is a LinearLayout which contains different view object of your activity
If you don't want to allocate a specific place in the view to the fragment container you can always use the RelativeLayour. I guess without a container we cant place a fragment in a view.
If you have any View or say root view from the activity window, get its Id using view.getId() (relevant to android:id) and pass that id to the fragmentTransaction.add() as the container id. Here's just a sample:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = new View(this);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(view.getId(), new Fragment());
}
Related
In my project, I want to set visibility of fragments buttons from MainActivity. But the problem is, it gives NullPointerException(). I also maked listBtn & gridBtn as static. I used below code :
FirstFragment fragment = (FirstFragment)getSupportFragmentManager().findFragmentById(R.id. <frameLayout Id>);
main_page_fragment.listBtn.setVisibility(View.GONE);
main_page_fragment.gridBtn.setVisibility(View.GONE);
You cannot access to your fragment view from Activity class because activity uses its own view (ex: R.layout.activity_main). Rather you can set visibility in your corresponding fragment class which will do the same job.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.details, container, false);
Button listBtn = (Button)view.findviewById(R.id.listBrn);
Button gridBtn = (Button)view.findviewById(R.id.gridBrn);
listBtn.setVisibility(View.GONE);
gridBtn.setVisibility(View.GONE);
return view;
}
Fragment onCreateView callback is called after onCreate method of activity, so i think you have tried to get access from it. That views will be accessible only after onResumeFragments callback is called, you should perform your actions with fragments there.
Another tip is that you strongly should not call views of fragments directly like you did or via static reference to views that's the worst. You should avoid such dependencies on fragments inner implementation. Instead of it, better is create some method like setInitialState (the name depends on your business logic) and just call it from activity.
So result code:
In activity:
private FirstFragment fragment;
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init fragment here
}
#Override
protected void onResumeFragments() {
super.onResumeFragments();
fragment.setInitialState();
}
In fragment:
//this will be called on fragment #onResume step, so views will be ready here.
public void setInitialState() {
listBtn.setVisibility(View.GONE);
gridBtn.setVisibility(View.GONE);
}
If you add your fragments dynamically from MainActivity like so:
YourFragment fragment = new YourFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, fragment, YOUR_TAG)
.commit();
Then you can define method in your fragment like so:
public void hideButtons()
{
yourBtn.setVisibility(View.GONE);
}
And call it from activity:
fragment.hideButtons();
I struggle with this for several hours and I found a much simpler solution.
Inside the fragment, simply make a public function (outside the on create view method) with the behavior that you want.
fun hideElement() {
binding.button.visibility = View.GONE
}
And then in main activity access to the fragment and call the function.
binding.bottomNavigation.setOnNavigationItemSelectedListener {
when (it.itemId){
R.id.someFragment -> someFragment.hideElement()
}
}
I am trying to add a fragment, then find a view inside said fragment, and add a view into it. However I keep getting a NullPointerException on this statement
FrameLayout container2 = (FrameLayout) fragment.getActivity().findViewById(R.id.content_frame);
Here is my code. Can someone tell me how to fix this please? thanks
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = new FragmentNavigationDrawer();
ViewGroup decor = (ViewGroup) getActivity().getWindow().getDecorView();
View child = decor.getChildAt(0);
decor.removeView(child);
fragmentTransaction.add(decor.getId(), fragment);
fragmentTransaction.commit();
FrameLayout container2 = (FrameLayout) fragment.getActivity().findViewById(R.id.content_frame);
container2.addView(child);
Just use a getter. Set a tag on your fragment so you can access it later, then either call getView() on your fragment to return its root view, or use a getter to access a specific View:
public class MainActivity extends AppCompatActivity {
//In onCreate
if (getFragmentManager().findFragmentByTag(FragmentNavigationDrawer.TAG) == null) {
getFragmentManager()
.beginTransaction()
.add(android.R.id.content, new FragmentNavigationDrawer(), FragmentNavigationDrawer.TAG)
.commit();
}
//Later, when you want to add said View:
FragmentNavigationDrawer frag =
(FragmentNavigationDrawer) getFragmentManager().findFragmentByTag(FragmentNavigationDrawer.TAG)
//Return the root view:
View fragRootView = frag.getView();
//Return a specific view:
frag.getUpdatableViewGroup().addView(newViewToAdd):
}
For your Fragment:
public class FragmentNavigationDrawer extends Fragment {
public static final String TAG = FragmentNavigationDrawer.class.getSimpleName();
FrameLayout updatableViewGroup;
//Can do this inside onCreateView() whilst inflating your Fragment's Views
//That's up to you.
#Override
public void onViewCreated (View view, Bundle savedInstanceState) {
updateableViewGroup = view.findViewById(R.id.updateable_view_group);
}
public FrameLayout getUpdatableViewGroup() {
return updateableViewGroup;
}
Be conscious of the Activity and Fragment life cycles however, and be careful not to attempt to access the Fragment's Views until they have finished inflating - onStart() of your Activity and later should be ok.
Please see the javadoc for FragmentTransaction.commit(). It says it will schedule a change to the fragment back stack. It doesn't happen immediately. It looks like you're expecting the fragment and its views to be instantly available.
Also, I'm really confused why you're reaching in a decor view to make changes. Usually you call out a view by id in the host activity's layout and make changes inside it.
I have a Fragment, and I want to set that whole fragment as root view of my activity. I have everything ready, and I'm instantiating my fragment programatically. I've tried (in my activity):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FeedFragment fragment = [...];
setContentView(fragment.getView());
}
But I've got a null pointer exception. In other words, how can I make my fragment act like an activity? I only target ICS+, I don't need to support older versions, if it makes any difference.
Try this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.all_lecturer_frag, container, false);
......
return rootView;
}
A Fragment, by design, is intended to be a tool to help you reuse screen space and as such, fragments have to be present inside a container. So while a fragment cannot technically be a root view, you can have a fragment be the only view inside the Activity. For this, you should inflate the view for your fragment programmatically inside the onCreateView() method of the fragment. then you could have something like this in your activity's layout xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.package.fragment_name
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
And then, within your activity, all you have to do is:
setContentView(R.layout.main);
Since, the fragment is defined in the layout xml, it cannot be removed from the activity's layout (although the layout itself can be changed) and is tied to it.
Also, on a side note, notice that the root view is a FrameLayout and not the fragment itself. But in this manner, your fragment can be tied to the activity. But don't forget that the Fragment will still retain it's lifecycle separate from the activity's.
EDIT: If you need to create your fragment instance programmatically, you have to do:
getFragmentManager().beginTransaction().add(R.id.frame_layout, your_fragment).commit();
This is the only way to add your fragment programmatically. But also keep in mind that the Fragment's layout is not tied to the activity's layout. But you can use the Fragment's lifecycle to behave similarly as an Activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
//initializations...
if (savedInstanceState == null) {
// During initial setup, plug in the fragment.
YourFragment details = new YourFragment();
getFragmentManager().beginTransaction().add(R.id.your_root_frame_layout, details).commit();
}
}
With a regular activity, you can use
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
to replace your main content with another fragment. With the default swipable tabs activity in Eclipse, this just overlays your new fragment on top of the other fragments. Can I get the id of the entire ViewPager somehow and replace that instead of android.R.id.content? I want to replace all of the tabs with my new fragment.
Normally fragments are inflated in FrameLayout
for eg;- fragment_layout.xml
<FrameLayout android:id"#+id/framelayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
In fragment class we set this layout as contentVIew as follows:
public class TestFragmentActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_layout);
}
And we can put any fragments in this content frame Layout using replace() method in Activity:
getFragmentManager().beginTransaction()
.replace(R.id.framelayout, new NewFragment())
.commit();
I have created an xml file called editor.xml which contains a FrameLayout. In my main activity I am trying to add my custom fragment to my FrameLayout.
The error I receive when trying to add my fragment is:
The method add(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, editorFrag)
However my editorFrag extends Fragment so I am confused on why this is happening. Below is my code for the files I have mentioned. Any help is appreciated.
Editor.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
editorFrag.java
public class editorFrag extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
return inflater.inflate(R.layout.newlevel, container, false);
}
}
MainActivity.java
public class editorActivity extends FragmentActivity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.editor);
// Check that the activity is using the layout version with the fragment_container FrameLayout
if(findViewById(R.id.fragment_container) != null)
{
// if we are being restored from a previous state, then we dont need to do anything and should
// return or else we could end up with overlapping fragments.
if(savedInstanceState != null)
return;
// Create an instance of editorFrag
editorFrag firstFrag = new editorFrag();
// add fragment to the fragment container layout
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFrag);
}
}
}
Answered:
Luksprog answered this problem for me below by telling me to check my imports. Eclipse chose to import the SDK version of Fragment instead of the support version that I needed. Thank you for the help.
You forgot to commit() your transaction.
You also forgot to call the addtoBackStack() method, otherwise your app closes when you hit the back button.
add commit() like this
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFrag).commit();
1- //Add fragment container in xml file
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.fragment.app.FragmentContainerView>
2- //Implementation of BackStack
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.addToBackStack("name");