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()
Related
Maybe I don't know the first thing about Android app development and xml (but I have 3 apps at Google Play Store), but what exactly determines what gets executed when?
To try to answer that question, I inserted Log statements in each method in each class file in FragmentBasics, which is the downloaded file from the URL in the Title.
Project structure:
I was surprised to see what the order of execution was.
MainActivity: `````onCreate
HeadlinesFragment: `````onAttach
HeadlinesFragment: `````onCreate
AbsListView: checkAbsListViewlLogProperty get invalid command
ArticleFragment: `````onCreateView
MainActivity: `````fragment_container IS null--two-pane mode
HeadlinesFragment: `````onStart
ArticleFragment: `````onStart
Why/how did onAttach and onCreate in HeadlinesFragment and onCreateView in ArticleFragment get executed before MainActivty "fragment container is Null..."?
And why does HeadlinesFragment not have onCreateView like ArticleFragment, but just onCreate?
I read that setContentView does this: "Set the activity content from a layout resource. The resource will be inflated, adding all top-level views to the activity." Since I was using a "large" device (tablet) and HeadlinesFragment and ArticleFragment are part of large\news_articles.xml, did that xml cause the classes to be instantiated?
I really feel like I know next to nothing about Android development. But I have those three apps ...
Here's the essence of the 3 classes mentioned:
public class MainActivity extends FragmentActivity
implements HeadlinesFragment.OnHeadlineSelectedListener
{
public void onCreate(Bundle savedInstanceState)
{
Log.w("MainActivity","`````onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles); // either normal or large
if (findViewById(R.id.fragment_container) == null)
Log.w("MainActivity","`````fragment_container IS null--two-pane mode");
else
{
Log.w("MainActivity","`````fragment_container not null--one-pane mode");
if (savedInstanceState != null)
return;
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager() .beginTransaction()
.add(R.id.fragment_container, firstFragment)
.commit();
}
}
}
.
public class HeadlinesFragment extends ListFragment
{
public void onCreate(Bundle _savedInstanceState)
{
Log.w("HeadlinesFragment","`````onCreate");
super.onCreate(_savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
}
public void onStart()
{
Log.w("HeadlinesFragment","`````onStart");
super.onStart();
if (getFragmentManager().findFragmentById(R.id.article_fragment) != null)
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
public void onAttach(Activity _activity)
{
Log.w("HeadlinesFragment", "`````onAttach");
super.onAttach(_activity);
}
}
.
public class ArticleFragment extends Fragment
{
public View onCreateView(LayoutInflater _inflater, ViewGroup _container, Bundle _savedInstanceState)
{
Log.w("ArticleFragment", "`````onCreateView ");
View v = _inflater.inflate(R.layout.article_view, _container, false);
return v;
}
public void onStart()
{
Log.w("ArticleFragment","`````onStart");
super.onStart();
}
}
And the essence of the .xml:
article_view.xml
<TextView android:id="#+id/article" />
news_articles.xml
<FrameLayout android:id="#+id/fragment_container" />
news_articles(large).xml
<LinearLayout>
<fragment
android:name="com.example.android.fragments.HeadlinesFragment"
android:id="#+id/headlines_fragment">
<fragment
android:name="com.example.android.fragments.ArticleFragment"
android:id="#+id/article_fragment"/>
</LinearLayout> [1]: http://i.stack.imgur.com/JPCZu.png
Someone else already did the same as you, but built also a nice image with how in practice the lifecycle happens. Here is a direct reference to the image diagram.
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 have a problem with a fragment. I need to display a fragment many times and don't create new instant of it. I have a method that it use to change the content of activity.
protected void setContentFragment(Fragment contentFragment) {
this.contentFragment = contentFragment;
setContentView(R.layout.content_frame);
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, contentFragment).commit();
getSlidingMenu().showContent();
}
content_frame is a simple layout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The problem is: I create instant of the first Fragment and pass it to setContentFragment(...) (still keep instant of this). Then call setContentFragment with other Fragment. And now, I pass instant of the first Fragment to this method, it show bank screen. Please help me :(
Try This..
private void switchFragment(Fragment fragment) {
if (getActivity() == null)
return;
if (getActivity() instanceof MainActivity) {
DashboardActivity dashboard = (MainActivity) getActivity();
dashboard.switchFragment(fragment, "Main List");
}
}
do this
protected void setContentFragment(Fragment contentFragment) {
this.contentFragment = contentFragment;
Random random = new Random(100);
String rString = "myRandomString"+random.nextInt(); // Create a random string here everytime you call setContentFragment
setContentView(R.layout.content_frame);
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, contentFragment, rString).commit();
getSlidingMenu().showContent();
}
This will replace the fragment as you require
Reference: http://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html#replace%28int,%20android.support.v4.app.Fragment,%20java.lang.String%29
When swapping fragments you need to detach/reattach instead of replacing them. This leaves it in memory. Also you don't need to setContentView again, just leave the existing FrameLayout ( you could also use android.R.id.content for the main activity content view, instead of creating a FrameLayout)
Fragment content1, content2;
Fragment current=content1;
public void onCreate() {
if(savedInstanceState==null)
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, content1)
.commit();
}
public void swap() {
if(current==content1) {
getSupportFragmentManager().beginTransaction()
.detach(content1)
.attach(android.R.id.content, content2)
.commit();
current=content2;
} else {
getSupportFragmentManager().beginTransaction()
.detach(content2)
.attach(android.R.id.content, content1)
.commit();
current=content1;
}
}
I am trying to build an app that will implement Action Bar tabs. One of the tabs should contain a MapFragment.
How can I implement an action bar with tabs, under one of which is a map Fragment?
Can you help me with how to proceed with this?
Here is what I have so far :
main class
package com.nfc.demo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
public class NFCDemoActivity extends Activity {
Tab selectedTab = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setDisplayShowHomeEnabled(false);
bar.setDisplayShowTitleEnabled(false);
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setCustomView(R.layout.main);
ActionBar.Tab mapTab = bar.newTab().setText("Map");
ActionBar.Tab settingsTab = bar.newTab().setText("Settings");
ActionBar.Tab aboutTab = bar.newTab().setText("About");
MapFragment mapFragment = new MapFragment();
SettingsFragment settingsFragment = new SettingsFragment();
AboutFragment aboutFragment = new AboutFragment();
mapTab.setTabListener(new TabListener(mapFragment));
settingsTab.setTabListener(new TabListener(settingsFragment));
aboutTab.setTabListener(new TabListener(aboutFragment));
Tab selectedTab = (Tab) getLastNonConfigurationInstance();
if (selectedTab == null) {
bar.addTab(mapTab, false);
bar.addTab(settingsTab, false);
bar.addTab(aboutTab, true);
}
setContentView(R.layout.main);
}
public Object onRetainNonConfigurationInstance() {
return selectedTab;
}
protected boolean isRouteDisplayed() {
return false;
}
protected class TabListener implements ActionBar.TabListener {
private Fragment fragment;
public TabListener(Fragment fragment) {
this.fragment = fragment;
}
public void onTabSelected(Tab tab, FragmentTransaction fragmentTransaction) {
fragmentTransaction.replace(R.id.mainFragment, this.fragment, null);
selectedTab = tab;
}
public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
fragmentTransaction.remove(this.fragment);
}
public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
//do nothing
}
}
}
The Fragment classes are all just returning an inflater with an .xml layout.
XML Layouts :
main.xml ( map should be on this XML file )
<?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:id="#+id/mainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
settings.xml AND about.xml
<?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="horizontal" >
<TextView
android:id="#+id/textView123"
android:text="asdfg"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
But adding the MapFragment thows a error:
Error inflating class fragment
error caused by java.lang.IllegalArgumentException:
Binary XML file line #2: Duplicate id 0x7f040005, tag null, or parent id 0x1020002 with another fragment for com.google.android.gms.maps.MapFragment 12-28 21:14:07.991: E/AndroidRuntime(26189): at android.app.Activity.onCreateView(Activity.java:4722)
I've been trying to figure out how to proceed for a couple of days but I am really confused.
Any help/tips would be greatly appreciated.
Also, what about getLastNonConfigurationInstance()? It is deprecated.
In the following solution, it is possible to add a GoogleMap to an Action Bar tab/dropdown. The key to doing this lies in correctly setting up your fragment to destroy the MapFragment when switching to another fragment in the Action Bar.
Create an Activity that implements the Action Bar functionality:
Create a project in Eclipse that uses Tabs for the main activity. If you don't do this, proceed to steps 2-5.
Create a class that extends Activity and implements ActionBar.OnNavigationListener.
Create a layout XML file that is a container for your tab fragments when you switch between them.
Implement/override the following method in your Activity class: public boolean onNavigationItemSelected(int position, long id).
In this method, switch between the position object to determine the selected tab and set the fragment to a new instance using the FragmentManager like this: getFragmentManager().beginTransaction().replace(R.id.container, fragment).commit().
Create a fragment that holds the map:
Create a class that extends Fragment to use as your tab's fragment. Read [1] to better understand the MapFragment.
Create a layout XML file that contains a fragment element (as seen in [1]).Use the XML in that page to create a layout XML file and use it in your fragment class.
Inflate that layout XML file in your fragment class by overriding onCreateView.
Your app should now display a map in the tab that uses your fragment class, however, switching to another tab and back to the map tab will result in a duplicate view ID. To overcome this, go on to the next step.
In your fragment class, override the following method to specifically destroy the underlying GoogleMap object so that it can be recreated when the map tab loads your fragment class again:
#Override
public void onDestroyView() {
super.onDestroyView();
MapFragment f = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
}
Not sure if you already resolved it or not.
You have to add google play services as library project to make it work. First I tried to add the jar file, but that didn't work.
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");