even though i googled extensively, i cant find a solution to my problem. I am new to programming, so please consider that in your answer.
I have a Main Activity and a Menu bar at the bottom. Since the menu bar is scrollable and i want to have it for all the different screens, i figured out that i could - instead of making an intent for a new activity - just put a fragment on top of the existing screen (and spare out the menu bar).
But i fail at programatically opening that fragment. I tried the following, but it doesnt even recognice the ID of the FrameLayout.
I was trying to replace a FrameLayout in my Main Activities' xml file with the fragment:
FragmentManager fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction ft =
fragmentManager.beginTransaction();
ft.replace(R.id.idOfFrameLayout, new nameOfFragmentClass());
ft.commit();
EDIT:
It works after i implemented OnFragmentInteractionListener into the Main Activity. Thanks to everyone!
My original answer suggested using add instead of replace for the Fragment transaction. I have since learned that either can be used in this case. For more info on the differences, see this, this and this
Since my add/replace idea was off base, I've changed my answer to a simple Fragment tutorial. One Fragment inflated from one Activity. I've included the code for Fragment to Activity communication (the reason for the OPs problem) but left comments on what could be deleted if that's not needed. The only thing done in the Activity is launching the Fragment then receiving a message back from that Fragment. The Fragment has a button that will cause a message to be sent back to the Activity. The message will then be Logged.
In both classes, if no communications is needed from the Fragment to the Activity, delete anything that is marked TODO:
// this is what controls whether you use a Fragment from the support library
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class SimpleActivity extends AppCompatActivity
// TODO: delete "implements SimpleFragment.OnFragmentInteractionListener" from this
// line (leave the '{')
implements SimpleFragment.OnFragmentInteractionListener {
private static final String TAG = SimpleActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
SimpleFragment fragment = new SimpleFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
// TODO: this entire method can be deleted
#Override
public void onFragmentInteraction(String message) {
// This is where you can act on the message from the Fragment. You would do
// things that are done from an Activity or you may pass the message on
// to another Fragment.
Log.d(TAG, message);
}
}
Most import statements aren't shown, I left this one in to indicate using the support library
import android.support.v4.app.Fragment;
public class SimpleFragment extends Fragment {
private OnFragmentInteractionListener mListener; // TODO: delete this line
public SimpleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_simple, container, false);
Button button = (Button)rootView.findViewById(R.id.msg_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// TODO: delete these 3 lines, do something else with the button click
if (mListener != null) {
mListener.onFragmentInteraction("Message from Fragment");
}
}
});
return rootView;
}
// TODO: this entire method can be deleted
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
// TODO: this entire method can be deleted
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
// TODO: this entire method can be deleted
public interface OnFragmentInteractionListener {
void onFragmentInteraction(String message);
}
}
Now the layout files. 1st activity_simple.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_container"
>
</FrameLayout>
</android.support.constraint.ConstraintLayout>
Then fragment_simple.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Info to Activity"
android:id="#+id/msg_button"
/>
</RelativeLayout>
This is a very basic sample. Here is a more extensive guide for Fragment usage.
When you use the 'replace' method, you need 3 things:
1- the container id, which is the view that is going to hold your fragment's view.
2- the fragment instance you want to use and
3- the fragment instance's tag, but this is optional.
Given that, let's say you have the following layout for your activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
...
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:menu="#menu/navigation" />
</android.support.design.widget.CoordinatorLayout>
The FrameLayout which id is "container" is the holding view, so that's the id you've gotta use:
ft.replace(R.id.container, new nameOfFragmentClass());
I suggest use this library to manage fragments:
FragmentManagerAndroid
Related
In example navigation action defined in navigation graph:
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2"
app:enterAnim="#anim/right_slide_in"
app:popExitAnim="#anim/left_slide_out"/>
When Fragment2 opens and starts sliding into view from the right, Fragment1 disappears instantly (sadly). When Fragment2 is closed and starts sliding to the right, Fragment1 is nicely visible under it, giving a nice stack pop effect (comparable to iOS).
How can I keep Fragment1 visible while Fragment2 slides into view?
EDIT:
This is not the most elegant solution, it is actually a trick but it seems to be the best way to solve this situation until the NavigationComponent will include a better approach.
So, we can increase translationZ (starting with API 21) in Fragement2's onViewCreated method to make it appear above Fragment1.
Example:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewCompat.setTranslationZ(getView(), 100f);
}
As very nice #xinaiz suggested, instead of 100f or any other random value, we can use getBackstackSize() to assign to the fragment a higher elevation than the previous one.
The solution was proposed by #JFrite at this thread
FragmentTransaction animation to slide in over top
More details can be found there.
In order to prevent the old fragment from disappearing during the sliding animation of the new fragment, first make an empty animation consisting of only the sliding animation's duration. I'll call it #anim/stationary:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#slidingAnimationDuration" />
Then in the navigation graph, set the exit animation of the action to the newly created empty animation:
<fragment android:id="#+id/oldFragment"
android:name="OldFragment">
<action android:id="#+id/action_oldFragment_to_newFragment"
app:destination="#id/newFragment"
app:enterAnim="#anim/sliding"
app:exitAnim="#anim/stationary"
</fragment>
The exit animation is applied to the old fragment and so the old fragment will be visible for the entire duration of the animation.
My guess as to why the old fragment disappears is if you don't specify an exit animation, the old fragment will be removed immediately by default as the enter animation begins.
It seems that you mistakenly used popExitAnim instead of exitAnim.
General rule is:
when you open (push) new screen, enterAnim and exitAnim take place
when you pop screen, popEnterAnim and popExitAnim take place
So, you should specify all 4 animations for each of your transitions.
For example, I use these:
<action
android:id="#+id/mainToSearch"
app:destination="#id/searchFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_left"
app:popEnterAnim="#anim/slide_in_left"
app:popExitAnim="#anim/slide_out_right" />
Suppose your back stack currently contains:
A -> B -> C
and now from Fragment C, you want to navigate to Fragment D.
So your animation:
enterAnim -> Applied for D Fragment,
exitAnim -> Applied for C Fragment
Updated stack would be:
A -> B -> C -> D
Now you press the back or up button
popEnterAnim -> Applied for C Fragment,
popExitAnim -> Applied for D Fragment
now your back stack would be again:
A -> B -> C
TL;DR: enterAnim, exitAnim are for push, and popEnterAnim, popExitAnim are for pop operation.
I think using the R.anim.hold animation will create the effect you want:
int holdingAnimation = R.anim.hold;
int inAnimation = R.anim.right_slide_in;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(inAnimation, holdingAnimation, inAnimation, holdingAnimation);
/*
... Add in your fragments and other navigation calls
*/
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
Or just label it as you have within the action.
Here is the R.anim.hold animation mentioned above:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0.0%p"
android:toYDelta="0.0%p"/>
</set>
In my own case the simplest solution was to use DialogFragment with proper animation and style.
Style:
<style name="MyDialogAnimation" parent="Animation.AppCompat.Dialog">
<item name="android:windowEnterAnimation">#anim/slide_in</item>
<item name="android:windowExitAnimation">#anim/slide_out</item>
</style>
<style name="MyDialog" parent="ThemeOverlay.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#color/transparent</item>
<item name="android:windowAnimationStyle">#style/MyDialogAnimation</item>
</style>
Layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="#color/colorWhite"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:layout_gravity="bottom"
android:orientation="vertical"
android:scrollbars="none"
android:transitionGroup="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout 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/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
// Your Ui here
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
Java:
public class MyFragmentDialog extends DialogFragment {
#Nullable
#Override
public View onCreateView(
#NonNull LayoutInflater inflater,
#Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_dialog, container, false);
}
#Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
int width = ViewGroup.LayoutParams.MATCH_PARENT;
int height = ViewGroup.LayoutParams.MATCH_PARENT;
Objects.requireNonNull(dialog.getWindow())
.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Objects.requireNonNull(dialog.getWindow()).setLayout(width, height);
dialog.getWindow().setWindowAnimations(R.style.MyDialogAnimation);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.MyDialog);
}
}
Adding a slide animation is very easy using the new material motion library. Make sure to use the material theme version 1.2.0 or later.
For example, if you want to navigate from FragmentA to FragmentB with a slide animation, follow the steps mentioned below.
In the onCreate() of FragmentA, add an exitTransition as shown below.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialFadeThrough().apply {
secondaryAnimatorProvider = null
}
}
In the onCreate() of FragmentB, add an enterTransition as shown below.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough().apply {
secondaryAnimatorProvider = SlideDistanceProvider(Gravity.END)
}
}
The above code will create an animation fading out FragmentA and sliding in FragmentB.
Why not use ViewPager?
It will take care of the animations and maintain the correct lifecycle of your fragments. You will be able to update fragments as they change from within onResume().
Once you have your ViewPager set up, you can change fragments by swiping, or automatically jump to a desired fragment without worrying about hand-coding transformations, translations, etc.: viewPager.setCurrentItem(1);
Examples and more in-depth description:
https://developer.android.com/training/animation/screen-slide
In your activity layout XML:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:fillViewport="true">
<include
layout="#layout/toolbar"
android:id="#+id/main_toolbar"
android:layout_width="fill_parent"
android:layout_height="?android:attr/actionBarSize">
</include>
<com.google.android.material.tabs.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:minHeight="?android:attr/actionBarSize"/>
<androidx.viewpager.widget.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
In onCreate() of your Activity class:
ViewPager viewPager = null;
TabLayout tabLayout = null;
#Override
public void onCreate() {
...
tabLayout = findViewById(R.id.tab_layout);
viewPager = findViewById(R.id.pager);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
String[] tabs = new String[]{"Tab 1", "Tab 2"};
for (String tab : tabs) {
tabLayout.addTab(tabLayout.newTab().setText(tab));
}
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), tabLayout);
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
...
}
Your PagerAdapter class, which can reside within your Activity class:
public class PagerAdapter extends FragmentStatePagerAdapter {
TabLayout tabLayout;
PagerAdapter(FragmentManager fm, TabLayout tabLayout) {
super(fm);
this.tabLayout = tabLayout;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new your_fragment1();
case 1:
return new your_fragment2();
default:
return null;
}
return null;
}
#Override
public int getCount() {
return tabLayout.getTabCount();
}
}
Make sure to use the appropriate imports:
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
I have MainActivity and added 2 fragments in R.id.container.
The MainActivity looks like the following.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, Fragment1.newInstance())
.commit();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, Fragment2.newInstance())
.commit();
}
}
Fragmnet1 and Fragment2 have the same code with different layouts.
public class Fragment1 extends Fragment {
public static Fragment1 newInstance() {
return new Fragment1();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_1, container, false);
}
}
And the associated layout fragment1.xml is
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".ui.main.Fragment1">
<TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Layout for Fragment2 is fragmnet2.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
tools:context=".ui.main.Fragment1">
<TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Test2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Here is the Debug GPU overdraw MainActivity after adding Fragment1 and Fragment2
See this image below. This is the one more drawing of GPU because the layout in Fragment1 remains in the MainActivity.
How can I add fragments without loading/showing the fragment below?
You are adding two fragments in the same container. This is the exact behaviour as per your code. If you need to show the fragment added later only, you need to replace the fragment instead of add.
I do not know the exact use case of yours. However, I guess you are trying to show fragment1 and fragment2 based on some checking. In that case, you might consider having two functions like the following in your MainActivity.
public boolean fragment1Loaded = false;
public void switchToFrag1() {
fragment1Loaded = true;
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, Fragment1.newInstance())
.commit();
}
public void switchToFrag2() {
fragment1Loaded = false;
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, Fragment2.newInstance())
.commit();
}
Now in the onCreate function, you need to call a function based on a condition which is necessary.
if (showFirstFragment) switchToFrag1();
else switchToFrag2();
You can do the switching whenever necessary from MainActivity.
Hope that helps!
Update
You can handle the back button click in your MainActivity and handle the logic by yourself. Override the onBackPressed function and then switch the fragments based on your necessity. For example, see the modified functions above. I am keeping a reference of which fragment is loaded in the screen right now. Then override the onBackPressed function like the following.
#Override
public void onBackPressed() {
if (fragment1Loaded) super.onBackPressed();
else switchToFrag1();
}
Hope you get the idea.
I am trying to get a fragment to show that contains an EditText and a button. I am new to using fragments, so I am not sure exactly what the error message I get when trying to create the fragment means.
I have a class that extends Fragment, this is where the EditText and button are created.
public class EditNameFragment extends android.support.v4.app.Fragment {
EditText editText;
ImageButton button;
public EditNameFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.edit_name_dialog, container, false);
editText = (EditText) view.findViewById(R.id.editTextDialog);
button = (ImageButton) view.findViewById(R.id.submitNewItemButtonDialog);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//stuff
}
});
return view;
}
Here is edit_name_dialog.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:id="#+id/edit_name_dialog"
>
<EditText
android:id="#+id/editTextDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="#+id/submitNewItemButtonDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>
And here in my main activity (which must extend FragmentActivity because of another part) is where I try to set up my Fragment. I think it has something to do with what id I am referencing. I have seen some people using container classes when using fragments, but I do not understand why this is done.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
EditNameFragment fragment = new EditNameFragment();
fragmentTransaction.add(R.id.edit_name_dialog, fragment, "tag");
fragmentTransaction.commit();
I get the error message when trying to run the code above
No view found for id 0x7f09002a (com.myapp:id/edit_name_dialog) for fragment EditNameFragment
If anyone could explain what I am missing here/ why people use container classes, that would be great. I know some people add fragments using XML, but I would like to do this only using java.
EDIT
I have added a class that extends FragmentActivity, following the model for a container class
public class EditNameFragmentActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_name_fragment_container);
}
}
Is the parameter for setContentView supposed to be the layout, or an id?
Here is the xml file that defines where the fragment should be
edit_name_fragment_container.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<fragment android:name="com.returnjump.spoilfoil.EditNameFragment"
android:id="#+id/edit_name_fragment_container"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/edit_name_fragment" />
</LinearLayout>
So for the parameter in
fragmentTransaction.add(R.id.edit_name_dialog, fragment, "tag");
this is supposed to reference the id of the fragment, correct?
It still gives me the same error, what am I missing?
There are basically two ways to add a fragment to an activity like the documentation say:
"statically": by declaring the fragment inside the activity's layout file.
"dynamically": adding the fragment programmatically. Like you tried to do.
Here is the documentation: http://developer.android.com/guide/components/fragments.html
If you wish to add it dynamically, here is the documentation part that you want to read:
At any time while your activity is running, you can add fragments to your activity layout. You simply need to specify a ViewGroup in which to place the fragment.
To make fragment transactions in your activity (such as add, remove, or replace a fragment), you must use APIs from FragmentTransaction. You can get an instance of FragmentTransaction from your Activity like this:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
You can then add a fragment using the add() method, specifying the fragment to add and the view in which to insert it. For example:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
The first argument passed to add() is the ViewGroup in which the fragment should be placed, specified by resource ID, and the second parameter is the fragment to add.
Once you've made your changes with FragmentTransaction, you must call commit() for the changes to take effect.
And about why to use dynamic fragments instead of static fragments, it has been made for interactive UI allowing you to simply handle different fragments into one activity as you please.
I'm quite new to Android dev and I followed the official Android's "Get started".
The fact is, my fragment is not displayed on the main activity ( it worked well few days ago but I changed some lines, I don't remember which ones ). I think it's a very basic problem as I don't use sophisticated fragments : it's basically one fragment inside an activity.
This is my activity :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container1"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mysecond.MainActivity"
tools:ignore="MergeRootFrame" />
My fragment :
<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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</RelativeLayout>
And the java code for this activity (I have some other activities in the app, based on the same pattern "one fragment inside one activity" and they work well...)
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container1, new PlaceholderFragment()).commit();
}
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
return rootView;
}
}
}
Any ideas ?
Thank you :)
[edit]
so this is my new onCreate method :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(
R.id.container1, new PlaceholderFragment()).commit();
}
Still not working for this activity (If I add a button in activity_main.xml I'll be able to see it but the I'm not able to see the TextView in the fragment...)
No errors in logcat and yes the activity is launched (I added some Log.e in onCreate and onCreateView and I cas see them)
In your onCreate methode you don't have to check if the savedInstanceState is null but if the the content of the FrameLayout you use is null
Or you simply always replace the fragment with a new one and ommit any checking.
Instead of add, you can use replace.
You can do it like below shown code:
getSupportFragmentManager().beginTransaction().replace(
R.id.container1, new PlaceholderFragment());
For me this work. Implementa a interface FragmentActions with the init() method and use this
private void showFragment(String fragmentTag){
FragmentTransaction trasaction = getSupportFragmentManager().beginTransaction();
FragmentActions fragment = (FragmentActions) getSupportFragmentManager().findFragmentByTag(fragmentTag);
if(fragment==null ){
if(lastFragmentviewed!=null)
trasaction.hide(lastFragmentviewed);
fragment = (FragmentActions) newInstance(fragmentTag);
trasaction.add(R.id.content_frame,(Fragment) fragment,fragmentTag);
}else{
if(lastFragmentviewed!=null && !lastFragmentviewed.equals(fragment))
trasaction.hide(lastFragmentviewed);
if(getSupportFragmentManager().findFragmentByTag(fragmentTag)!=null){
fragment.init();
trasaction.show((Fragment) fragment);
}else
trasaction.add(R.id.content_frame,(Fragment) fragment,fragmentTag);
}
lastFragmentviewed=(Fragment) fragment;
trasaction.commit();
}
It's taken me some time to wrap my head around fragments, but this should be my last question on fragments, since I think I just about have them down. I know this is a huge mess of code to go through. But I'd appreciate the help, to make sure I'm not breaking any fundamental rules with fragments.
I am going to post all of my code just to see if someone can "look over it" to see if I'm making any major mistakes or if I should go a simpler route. Lastly, as stated in the title, my fragment is NOT being replaced... it'd being added on top.
File Tree:
MainActivity.java:
package com.example.learn.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
public class MainActivity extends FragmentActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
/* Add a class to handle fragment */
public static class SSFFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.choose_pill_frag, container,
false);
return v;
}
}
public void red(View view) {
// Create new fragment and transaction
ExampleFragments newFragment = new ExampleFragments();
android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.frag, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
public void blue(View view) {
//Figure out code for "red" first
}
}
ExampleFragments.java:
package com.example.learn.fragments;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ExampleFragments extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.red_pill_frag, container, false);
}
}
ActivityMain.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" >
<fragment
android:id="#+id/frag"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.example.learn.fragments.MainActivity$SSFFragment" />
</RelativeLayout>
choose_pill_frag.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" >
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:onClick="blue"
android:src="#drawable/blue" />
<ImageButton
android:id="#+id/imageButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:onClick="red"
android:src="#drawable/red" />
</RelativeLayout>
red_pill_frag.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" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="You stay in Wonderland and I show you how deep the rabbit-hole goes."
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
The application should show two buttons. The two buttons exist in a single fragment, and then if you hit a button, the fragment gets replaced with a new fragment that shows the proper text. As of right now, it should replace, but it only seems to add it on top.
Instead of <fragment> use <FrameLayout> in layout xml for activity.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then in FragmentActivity in onCreate add initial fragment (in your case SSFFragment):
FragmentA fragmentA = new FragmentA();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.container_id, fragmentA);
transaction.commit();
From inside fragment you can replace fragment in container.
class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button button = new Button(getActivity());
button.setText("Replace");
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
FragmentB fragmentB = new FragmentB();
transaction.replace(R.id.container_id, fragmentB);
transaction.commit();
}
});
return button;
}
}
Here is the answer to your real question...since this was your second question resulting from your original post, I've modified the solution to get at that frag in another way:
Fragment details = (Fragment)getSupportFragmentManager().findFragmentById(R.id.details);
details = new ExamplesFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
Also, the android.support.v4.app part is just not necessary, and frankly leads to possible hours of "going down the wrong road" type efforts by adding and removing it all over your code (as long as you're using:)
import android.support.v4.app.FragmentTransaction;
In this my example, you don't need to import the support for FragmentManager. However, if you're getting errors, make sure you've imported the library itself into your "libs" folder.
This solution will fix the overlapping fragment problem, and hopefully save people hours of tinkering around with replacing frags.
well i was facing the same problem and i just replace the fragment from main layout with linear lay out and guess what its working.. its strange dont know how but its working. i am using actionbar to switch between fragments
for replacing my code is :
protected class MyTabsListener1 implements ActionBar.TabListener{
private Fragment frag ;
public MyTabsListener1 ( Fragment frag){
this.frag = frag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
switch (tab.getPosition()){
case 0:
ft.replace(R.id.replace, homeFrag);
break;
case 1:
ft.replace(R.id.replace, createFrag);
break;
case 2:
ft.replace(R.id.replace, ioListFrag);
break;
case 3:
ft.replace(R.id.replace, settingFrag);
break;
default:
break;
}
}
and my main layout is this :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/replace"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
</LinearLayout>
</LinearLayout>
In short, you CAN NOT replace fragment if u defined it in XML with fragment tag. Instead, as #pawelzieba adviced add Frame tag in your layout, find it and add,remove, replace fragments there.. Hope it helped. Cheers
The main benefit of using fragments is to be able to make them take up portions of the screen rather than the whole screen, which is what Activities do.
If you're just making an app for a small screen that will function like an Activity, but is coded in Fragments, just make a separate FragmentActivity for each of your Fragments.
Make this the onCreate of your FragmentActivity:
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.emptylayout);
FragmentManager fragmentManager = getSupportFragmentManager(); //Or getFragmentManager() if you're not using the support library
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
YourFragment fragment = new YourFragment();
fragmentTransaction.add(R.id.emptyview, fragment);
fragmentTransaction.commit();
}
Where layout/emptylayout is an XML layout file with a FrameLayout. id/emptyview is that FrameLayout.
If you want to use XML for your fragment's actual layout, make a separate XML layout for the actual fragment, and inflate it in the fragment's `onCreateView':
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.files, container, false);
// Do stuff here
return view;
}
Then just use startActivity(new Intent(getActivity(), YourFragmentActivity.class)) to launch a FragmentActivity from a Fragment.
It seems redundant, yeah, but if you're going to be targeting larger screens later (if not, why are you bothering with fragments?), it'll make it easier in the long run.