Using FragmentManager and FragmentTransaction in a DialogFragment - java

The gist of the issue is this: I'm trying to Launch a DialogFragment from a FragmentActivity. This DialogFragment's view contains a FrameLayout which I would like to populate with a Fragment. Basically the FragmentActivity launches the DialogFragment, then the DialogFragment populates it's FrameLayout with a Fragment. I've scoured the internet for tutorials and I've pieced together something that (in my mind) should work. However, no matter what I try I continuously get errors. This is what I have so far:
This is my FragmentActivity's layout (file name is "activity_interact"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/activity" >
<Button
android:id="#+id/btnLaunchDialog"
style="#style/btn" />
This is my DialogFragment's layout (file name is "dialog_overview"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/dialog" >
<FrameLayout
android:id="#+id/frameDisplay"
style="#style/frame" />
This is my Fragment's layout (file name is "fragment_stats"):
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/table" >
<TableRow style="#style/table" >
<TextView
style="#style/display"
android:gravity="right"
android:text="#string/textStr" />
</TableRow>
Here is the java code for my FragmentActivity:
public class ActivityInteract extends FragmentActivity implements
OnClickListener {
Button btnLaunchDialog;
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_interact);
btnLaunchDialog = (Button) findViewById(R.id.btnLaunchDialog);
btnLaunchDialog.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLaunchDialog:
FragmentManager fm = getSupportFragmentManager();
DialogOverview dialogOverview = new DialogOverview();
dialogOverview.show(fm, "dialog_overview");
break;
}
}
}
Here is my DialogFragment's code:
public class DialogOverview extends DialogFragment implements OnClickListener {
public DialogOverview() {
}
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.dialog_overview, vg);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frameDisplay, new FragmentStats());
ft.commit();
return view;
}
}
Here is my Fragment's code:
public class FragmentStats extends Fragment {
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.fragment_stats, vg);
return view;
}
}
And finally, here is the logcat error:
06-11 10:07:29.382: E/AndroidRuntime(30013): java.lang.IllegalArgumentException: No view found for id 0x7f060003 for fragment FragmentStats{4169c928 #1 id=0x7f060003}
I can see that it's saying that I don't have a view for the Fragment, but I do...(or do I?) I'm lost here, any help would be appreciated. Also, am I going about this the right way? Would it be more efficient to re-use a FragmentManager? (i.e. pass it from the FragmentActivity into the DialogFragment)
Update: I removed the code to load my Fragment into the DialogFragment and the DialogFragment displays without issue now. So obviously (as the logcat error suggests) there is something wrong with my Fragment itself...however, it matches examples that I've seen on the internet. Which is making me wonder: Is there an issue with nesting fragments in this way? A FragmentActivity displaying a DialogFragment that displays a Fragment makes me want to quip that "we can't go any deeper" but I don't know. Could I nest more fragments?

Actually, you CAN add nested fragment to a DialogFragment, BUT it cannot be based on a wrapped Dialog.
Instead of overriding onCreateDialog, inflate the View that contains the ViewGroup that will use the Fragment in onCreateView.
A consequence of this is that you cannot use a DialogFragment that wraps an AlertDialog - so if you want positive and negative buttons you need to manually create them in the content view.
Also, keep in mind that you cannot set the fragment in the XML. You need to declare the container view in XML and perform the fragment transaction programmatically.
public class MyDialogFragment extends DialogFragment {
[...]
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.dialog_layout, container);
if (savedInstanceState == null) {
final ChildFragment fragment = [...];
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
return view;
}
}

Related

how to make a fragment as a app launching activity

I have "home_fragment.java" class file in my project
which i need to be launched when the app starts.
but i am only able to add 'activities' as launch default,not fragments.
please help me to add a 'fragment' as a launch activity.
i am new to android ,thank you.
this is my home_fragment.java
public class home_fragment extends Fragment {
View myView;
Button more;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View myview = inflater.inflate(R.layout.home_layout,container, false);
Button button = (Button) myview.findViewById(R.id.button5);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do something
}
});
return myview;
}
}
you have to add the fragment into the activity and launch this activity.
it is not possible for fragment to be launched independently.
in empty activity add this xml
<fragment android:name="com.company.appName.fragments.FirstFragment"
android:id="#+id/fragment_place"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and in activity onCreate method do this
Fragment fr = new FirstFragment();
fr.setArguments(args);
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr);
fragmentTransaction.commit();
hope this will help you.
Fragment must be launched from Activity.
You can do it by launching your fragment from your activity onCreate() method like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyFragment()).commit();
}

Android - where to put onClick on linearlayout of a fragment

Obviously, I am new to Android - XML programming... So I have a navigation drawer, and once item is selected from the drawer, a corresponding fragment on the right side will display. Inside that fragment, I have linear layouts. I'd like to get redirected to another activity once that linear layout has been tapped. I was able to make it work on activities, by using android:onClick on XML file, but can't make it work on fragment. Somebody help me please.
App interface, refer to this image:
http://i.stack.imgur.com/u6dHi.jpg
Code:
fragment_smart.xml - the display once item's selected. I am trying to use the onClick on xml.
<LinearLayout
android:id="#+id/linearLayoutsmart1"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_below="#+id/smart_title"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:orientation="horizontal"
android:weightSum="1"
android:onClick="smart_recommended_link">
Here's my Java code:
public class FragmentSmart extends Fragment {
public static final String TAG = "stats";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
return myfragment;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(this, SmartRecommended.class);
startActivity(smartRecommendedIntent);
}
}
The app is crashing when I clicked on the linearlayout using this code. What's the best thing to do here? Thank you!
For fragments you need to add the listener programmatically:
public class MyFragment extends Fragment implements View.OnClickListener {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.my_layout).setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Handle click based on v.getId()
}
}
First of all, linear layouts cant trigger onClick events by default.
Check this answer for more information: LinearLayout onClick.
You can get the LinearLayout from the view in your onCreateView() like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
LinearLayout layout = (LinearLayout)myFragment.findViewById(R.id.linearLayoutsmart1);
// here you can set a listener of any type you want to the layout
return myfragment;
}
In fragments, you must use getActivity() method instead of this to reference the activity that the fragment is attached to.
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(getActivity(), SmartRecommended.class);
startActivity(smartRecommendedIntent);
}

Calling Fragment's method from Activity doesn't work?

I followed this question and tried my hand at calling a method which is in my Fragment. I'm trying to call the method from an activity. However it's not recognizing the fragment's method. Here's my code:
XML:
<RelativeLayout
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:id="#+id/peoplefragment">
<ListView
android:id="#+id/searchpeople_list"
android:layout_height="fill_parent"
android:layout_width="match_parent"
android:scrollbars="none"
android:background="#fff">
</ListView>
</RelativeLayout>
Fragment Code:
public class SearchPeopleTab extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.activity_search_people_tab, container, false);
View rootView = inflater.inflate(R.layout.activity_search_people_tab, container, false);
return rootView;
}
public static void UpdateResults(String requestSearch)
{
new GetSearchResults(requestSearch).execute();
}
class GetSearchResults extends AsyncTask<Void, Void, Void> {
String requestSearch;
GetSearchResults(String searchtext)
{
this.requestSearch = searchtext;
}
#Override
protected Void doInBackground(Void... params) {
}
Activity Code: (Calling the Fragment's method)
private void PopulateResults() {
FragmentManager manager = (FragmentManager) getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.peoplefragment);
fragment.UpdateResults(requestSearch); //thats the method in the fragment.
}
The 'UpdateResults()' part is underlined and the following message is given as an error:
Cannot resolve method UpdateResults()
Looks like it can't find the method. What am I doing wrong?
Remove the keyword static from the method.
And also, store the fragment in the SearchPeopleTab reference variable, that you created.
You don't really need the line to store the FragmentManager, you can directly use getSupportFragmentManager();
//FragmentManager fm = (FragmentManager) getSupportFragmentManager();
SearchPeopleTab fragment = (SearchPeopleTab) getSupportFragmentManager().findFragmentById(R.id.peoplefragment);
fragment.UpdateResults();
When static methods are used, they are called using the class name. When you want a method to be called on specific objects, the method should not be static.
You need to cast the Fragment to your defined class
private void PopulateResults() {
FragmentManager manager = (FragmentManager) getSupportFragmentManager();
SearchPeopleTab fragment = (SearchPeopleTab)manager.findFragmentById(R.id.peoplefragment);
fragment.UpdateResults(); //thats the method in the fragment.
}

Null pointer when setting text/background on android app [duplicate]

This is a canonical question for a problem frequently posted on StackOverflow.
I'm following a tutorial. I've created a new activity using a wizard. I get NullPointerException when attempting to call a method on Views obtained with findViewById() in my activity onCreate().
Activity onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Layout XML (fragment_main.xml):
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="packagename.MainActivity$PlaceholderFragment" >
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:id="#+id/something" />
</RelativeLayout>
The tutorial is probably outdated, attempting to create an activity-based UI instead of the fragment-based UI preferred by wizard-generated code.
The view is in the fragment layout (fragment_main.xml) and not in the activity layout (activity_main.xml). onCreate() is too early in the lifecycle to find it in the activity view hierarchy, and a null is returned. Invoking a method on null causes the NPE.
The preferred solution is to move the code to the fragment onCreateView(), calling findViewById() on the inflated fragment layout rootView:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
View something = rootView.findViewById(R.id.something); // not activity findViewById()
something.setOnClickListener(new View.OnClickListener() { ... });
return rootView;
}
As a side note, the fragment layout will eventually be a part of the activity view hierarchy and discoverable with activity findViewById() but only after the fragment transaction has been run. Pending fragment transactions get executed in super.onStart() after onCreate().
Try OnStart() method and just use
View view = getView().findViewById(R.id.something);
or Declare any View using getView().findViewById method in onStart()
Declare click listener on view by anyView.setOnClickListener(this);
Try to shift your accessing views to the onViewCreated method of fragment because sometimes when you try to access the views in onCreate method they are not rendered at the time resulting null pointer exception.
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Agreed, this is a typical error because people often don't really understand how Fragments work when they begin working on Android development. To alleviate confusion, I created a simple example code that I originally posted on Application is stopped in android emulator , but I posted it here as well.
An example is the following:
public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container_container, new ExampleFragment())
.addToBackStack(null)
.commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
#Override
public void exampleFragmentCallback()
{
Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
}
}
activity_container.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/activity_container_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
ExampleFragment:
public class ExampleFragment extends Fragment implements View.OnClickListener
{
public static interface Callback
{
void exampleFragmentCallback();
}
private Button btnOne;
private Button btnTwo;
private Button btnThree;
private Callback callback;
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
this.callback = (Callback) activity;
}
catch (ClassCastException e)
{
Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
throw e;
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_example, container, false);
btnOne = (Button) rootView.findViewById(R.id.example_button_one);
btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
btnThree = (Button) rootView.findViewById(R.id.example_button_three);
btnOne.setOnClickListener(this);
btnTwo.setOnClickListener(this);
btnThree.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v)
{
if (btnOne == v)
{
Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
}
else if (btnTwo == v)
{
Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
}
else if (btnThree == v)
{
callback.exampleFragmentCallback();
}
}
}
fragment_example.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="#+id/example_button_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="#string/hello"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/>
<Button
android:id="#+id/example_button_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_one"
android:layout_alignRight="#+id/example_button_one"
android:layout_below="#+id/example_button_one"
android:layout_marginTop="30dp"
android:text="#string/hello" />
<Button
android:id="#+id/example_button_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_two"
android:layout_alignRight="#+id/example_button_two"
android:layout_below="#+id/example_button_two"
android:layout_marginTop="30dp"
android:text="#string/hello" />
</RelativeLayout>
And that should be a valid example, it shows how you can use an Activity to display a Fragment, and handle events in that Fragment. And also how to communicate with the containing Activity.
The view "something" is in fragment and not in activity, so instead of accessing it in activity you must access it in the fragment class like
In PlaceholderFragment.class
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
false);
View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });
return root;
}
You are trying to access UI elements in the onCreate() but , it is too early to access them , since in fragment views can be created in onCreateView() method.
And onActivityCreated() method is reliable to handle any actions on them, since activity is fully loaded in this state.
Add the following in your activity_main.xml
<fragment
android:id="#+id/myFragment"
android:name="packagename.MainActivity$PlaceholderFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</fragment>
Since you have declared your View in the fragment_main.xml,move that piece of code where you get the NPE in the onCreateView() method of the fragment.
This should solve the issue.
in the posted code above in the question there is a problem :
you are using R.layout.activity_main in oncreate method, but the xml files name is "fragment_main.xml" , means you are trying to get the view of fragment_main.xml file which is not being shown so it gives null pointer exception. change the code like :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);// your xml layout ,where the views are
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
You have to remember important thing is :
NullPointerException occurs when you have declared your variable and trying to retreive its value before assigning value to it.
Use onViewCreated() Method whenever using or calling views from fragments.
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
View v = view.findViewById(R.id.whatever)
}
I've got the same NullPointerException initializing a listener after calling findViewById() onCreate() and onCreateView() methods.
But when I've used the onActivityCreated(Bundle savedInstanceState) {...} it works. So, I could access the GroupView and set my listener.
I hope it be helpful.
Most popular library for finding views which is used by almost every developer.
ButterKnife
As I can their are enough answers explaining finding views with proper methodology. But if you are android developer and code frequently on daily basis then you can use butter-knife which saves a lot time in finding views and you don't have write code for it, With in 2-3 steps you can find views in milliseconds.
Add dependency in app level gradle:
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
Add plugin for butter knife:
File -> Settings -> plugins->
Then search for Android ButterKnife Zelezny and install plugin and restart your studio and you are done with it.
Now just go to Oncreate method of your activity and right click on your layout_name and tap on generate button and select butterknife injection option and your views references will be automatically created like mention below:
#BindView(R.id.rv_featured_artist)
ViewPager rvFeaturedArtist;
#BindView(R.id.indicator)
PageIndicator indicator;
#BindView(R.id.rv_artist)
RecyclerView rvArtist;
#BindView(R.id.nsv)
NestedScrollingView nsv;
#BindView(R.id.btn_filter)
Button btnFilter;

Android - How do I manage multiple instances of a single fragment with different content?

I want to be able to setText and getText of Views of individual Fragments. As it is now, when I setText of a Framgent's TextView it changes the text of that View in all Fragments.
I've been experimenting by moving things around, but here is my code as of this moment:
Fragment class
public class TestFragment extends Fragment{
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.test_fragment, container, false);
TextView tv = (TextView) view.findViewById(R.id.huh);
//tv.setText("AAAAAAAAAAAAAAAAAAA");
return view;
}
public void setText(String asdf) {
TextView test = (TextView) view.findViewById(R.id.huh);
test.setText(asdf);
}
}
Activity Class
public class Manage extends BaseActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.manage);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
TestFragment fragment = new TestFragment();
//fragment.setText("ASDF");
fragmentTransaction.add(R.id.test_fragment, fragment, "testtag");
fragmentTransaction.commit();
}
}
The framgent.xml is pretty plain; just a single TextView.
Fragments are added to stack with a parameter named tag. In your case you've added your fragment with "testtag".
fragmentTransaction.add(R.id.test_fragment, fragment, "testtag");
If you create multiple instances of same fragment and add them with unique tags, then you are able to get them with that unique tags. When you get a fragment then you can reach its content.
FragmentManager fm = this.getSupportFragmentManager();
Fragment testtagFragment = fm.findFragmentByTag("testtag");
View targetView = testtagFragment.getView().findViewById(R.id.anyViewInsideContentOfYourFragment);
Edit:
I want to be able to setText and getText of Views of individual
Fragments.
This question has 2 parts.
To setText while initializing you have to pass your initial
parameters to your fragment while creating its instance. I suggest
you to use a static newInstance method for this. See sample
here
To getText read my answer above. Note that, you can get the content of a fragment after its onCreateView method is executed. So If you try to call getView method of a fragment at your activities onCreate method (after you add the fragment), that will return null. You can get its content successfully under a click event to test that, and use get or set operations of any view on that fragment's content.

Categories