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");
Related
I have main activity and I want to call method inside fragment I use getSupportFragmentManager for get fragment but always return null to me can you help me please
mainActivity:
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
public class MainActivity extends ActionBarActivity implements OnItemPressListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void A()
{
FragmentManager fragManager = this.getSupportFragmentManager();
MainFragment fragment= (MainFragment)fragManager.findFragmentById(R.id.mainfragment);///always Null
fragment.B();
}.
}
main_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:id="#+id/mainfragment"
tools:context=".mainFragment">
<GridView
android:id="#+id/gridview_movie"
android:layout_width="fill_parent"
android:layout_height="fill_parent"></GridView>
</FrameLayout>
MainFragmet:
import android.support.v4.app.Fragment;;
public class MainFragment extends Fragment {
public void B() {
///do some thing
}
}
you just need to get an instance from the fragment inside the main activity
MainFragment fragment=new MainFragment();
I don't see any code in your question that suggests you ever instantiate a Fragment and load it into R.id.mainfragment. In addition, your main_fragment.xml file contains an GridView with match_parent parameters. This is pointless if you intend to load a fragment into R.id.mainfragment, as your GridView will only obscure it.
With that in mind, I would make the following corrections to your code.
Remove your GridView from your layout file:
<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:id="#+id/mainfragment"
tools:context=".mainFragment" />
Add something like the following to your onCreate() in MainActivity:
if(getSupportFragmentManager.findFragmentById(R.id.mainfragment) == null) {
getSupportFragmentManager()
.beginTransaction
.add(R.id.mainfragment, new MainFragment())
.commit();
}
//...This can be in onCreate(), or inside a method, to access MainFragment's methods
MainFragment frag = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.mainfragment);
if (frag != null) frag.callYourMethodHere();
If you want use findFragmentById... You must write like this:
<fragment
android:id="#+id/mainfragment"
class="com.oldfeel.base.BaseFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
First you haven't instantiate your fragment in the main activity try the code below:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check whether the activity is using the layout version with
// the fragment_container FrameLayout. If so, we must add the first fragment
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment, "myAwesonFragment").commit();
}
}
then if your method A do the following
public void A()
{
BrowseFragment fragment = (BrowseFragment) getSupportFragmentManager().findFragmentByTag("myAwesonFragment");
//Check if the Bottom Sheet is Expanded
fragment.B();
}
And that's it this time it won't be null. I personally always use findFragmentByTag when I want to locate a fragment.
PD: we used getSupportFragmentManager() because you are using android.support.v4.app.Fragment if your using android.app.Fragment use getFragmentManager()
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();
}
}
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 trying to use Android fragments in a very simple way, similar to the tutorial on the Android developer website.
I have an Activity (MediaInfoActivity) with the following code:
public class MediaInfoActivity extends FragmentActivity {
private final String TAG = "MediaInfoActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
setContentView(R.layout.media_info_activity_layout);
}
}
Here is the code for the media_info_activity_layout.xml file:
<?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">
<fragment class="com.hawkforce.test.MediaInfoFragment"
android:id="#+id/mediaInfoFragment"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
<FrameLayout android:id="#+id/mediaPlayerBarPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<fragment class="com.hawkforce.test.MediaPlayerBarFragment"
android:id="#+id/mediaPlayerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
And finally here is the code for MediaInfoFragment:
public class MediaInfoFragment extends Fragment {
private final static String TAG = "MediaInfoFragment";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate()");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i(TAG, "onCreateView()");
if (container == null) {
Log.i(TAG, "onCreateView(): container = null");
}
return inflater.inflate(R.layout.media_info_fragment_layout, container, false);
}
}
Here is my problem : the container passed in the onCreateView() method of the MediaInfoFragment is null. As I understood, this should only be the case for non-UI Fragments. However, my Fragment has a UI, which is displayed OK on the screen when I launch MediaInfoActivity. It causes problems because no style declared in the xml layout file of the fragment is applied.
Here is my Log:
I/MediaInfoActivity: onCreate()
I/MediaInfoFragment: onCreate()
I/MediaInfoFragment: onCreateView()
I/MediaInfoFragment: onCreateView(): container = null
Am I missing anything obvious here ?
You just have to create a inflater like bellow in your fragment.
View rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.activity_my_cart, null);
} else {
((ViewGroup) container.getParent()).removeView(rootView);
}
return rootView;
}
I hope it will work as per your question.
I am not sure since I don't have the code of the SDK in front of me but I think that the life-cycle of calling Fragment "onCreateView" function is different between the two cases:
1. Static settings of fragment in layout
2. Loading pragmatically with FragmentManager.
In the first case the debugger get into Fragment.onCreateView() method immediately upon adding the content view to the parent activity as part of onCreate() code:
When calling: setContentView(R.layout.some_layoue);
You will see the debugger get into Fragment.onCreateView() before going to next line
In the second case the Fragment.onCreateView() is being invoked only after the onCreate() of the activity is finished.
This looks like design bug for me but possibly as design feature.
Anyway the container is null when adding fragment statically because the related object was not yet created.
In fact the difference between the two situations is much deeper. In the case of static fragments toggling between fragments will not create the view hierarchy correctly.
For example if you will add button-A to fragment A and button-B to Fragment-B and toggle the fragments with a code looks like this (highlighting only the relevant code):
public void turnOnFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.attach(this)
.commit();
}
}
public void turnOffFragment() {
FragmentManager manager = getFragmentManager();
if (manager != null) {
manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.detach(this)
.commit();
}
}
You will see that in the case of static fragments the buttons from both fragments are presented although turning on and off. If however fragments are added programatically the toggle works fine and view hierarchy is cleaned and show only button from relevant fragment.
This is based of my experience with version 4.4.2
I have a tab + ViewPager layout and in one of these tabs I have a list view. When I replace that list fragment upon the onclick I can still see the old fragment under the new fragment. See:
Code:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
HallsInStateFragment hallsForState = new HallsInStateFragment();
transaction.replace(R.id.container, hallsForState);
transaction.addToBackStack(null);
transaction.commit();
where the R.id.container is the FrameLayout in the view.
when need to remove all views from the parent view you need to call removeAllViews() at container in your onCreateView() method of your fragment.
Here is the code:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
container.removeAllViews(); // Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false);
}
Instead of R.id.container put id of fragment like this: ((ViewGroup)getView().getParent()).getId(). Actually it is not replacing the fragment but the previous layout i.e FrameLayout. It works for me and i hope it will work in your case also.
Add this to both fragment parent layout
android:background="#android:color/white".
The one which you are replacing and one which you will replace.
The fragment's UI is a part of the activity view hierarchy. So if you created your views in onCreateView() method then you inflate your layout using the ViewGroup container. This container keeps references to your fragment views. Try to override onDestroyView() method of your fragment and remove all the views from the parent:
#Override
public void onDestroyView() {
//mContainer.removeAllViews();
ViewGroup mContainer = (ViewGroup) getActivity().findViewById(R.id.container);
mContainer.removeAllViews();
super.onDestroyView();
}
Your fragments should be loaded in FrameLayout like this
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground" />
And your fragments should be added/loaded in this frameLayout by this function
private fun switchFragment(
fragment: Fragment,
addToBackstack: Boolean
) {
//check new fragment is alredy loaded currently, then return
val myFragment =
supportFragmentManager.fragments.lastOrNull()//return current visible fragment or null
if (myFragment != null && fragment::class == myFragment::class) {
return
}
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
//transaction.add(R.id.frameLayout, fragment, fragment.javaClass.name)
transaction.replace(
R.id.frameLayout,
fragment,
fragment.javaClass.name
)//using replace will make sure that the previous fragment won't be visible from new fragment
if (addToBackstack) {
transaction.addToBackStack(fragment.javaClass.name)
}
transaction.commit()
}
So initially, your first fragment should be loaded like this,
switchFragment(HomeFragment(), false)
and then other fragments when you select from bottom navigation view or navigation drawer, call this function like this
switchFragment(MyProfileFragment(), true)
In My Case , This was happening because I was using the static fragment as
<?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" >
<fragment
android:name="com.example.android.FooFragment"
android:id="#+id/fooFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
where foofragment is the initial fragment , Then Trying to replace the fragment using with other fragment , so that both fragment overlaps.
Instead , Problem is solved , when I used dynamic Linkage , in place of fragment in the xml , we need to use framelayout as
<?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/your_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
Then dynamically adding the fragment-1 and replacing with the fragment-2 , is working fine for me.
I did not find answer earlier, hence posting this solution.
In the root view add clickable = true and add a background color(both in the xml) of the fragment replacing the current fragment .
Its just a fix for workaround
Best code for it. clearly and not used another ram of device.
on you Activity onCreate or before adding fragments add this code:
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment instanceof NavigationDrawerFragment) {
continue;
}
else if (fragment != null) {
getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
}
Happy coding :)