I am trying to make an android layout where I have ListFragment on the left of the screen that contains menu entries and then an area to the right of the ListFragment where I can programmatically load in different fragments based on the entry selected on the left. So I started off by making an xml layout file that has a horizontal linear layout which contains two frame layouts. My thought was that I could load my fragments into the frame layouts.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/fragment_container_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="30"/>
<FrameLayout
android:id="#+id/fragment_container_right"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="70"/>
</LinearLayout>
Then in my MainViewActivity class in the onCreate method I add a list fragment to the left container, and a list fragment to the right container.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menu_items);
if(findViewById(R.id.fragment_container_left) != null) {
if(savedInstanceState == null){
MenuListFragment menuListFragment = new MenuListFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container_left, menuListFragment);
ft.commit();
}
}
if(findViewById(R.id.fragment_container_right) != null) {
if(savedInstanceState == null){
MoveListFragment moveListFragment = new MoveListFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container_right, moveListFragment);
ft.commit();
}
}
}
So then the onCreate method of the MenuListFragment is:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(ArrayAdapter.createFromResource(getActivity().getApplicationContext(),
R.array.menu_items, R.layout.main_menu_item));
}
And then the onCreate method of the MoveListFragment is:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] items = { "move 1", "move 2", "move 3", "move 4" };
setListAdapter(new ArrayAdapter<String>(getActivity(), R.layout.move_list_items, items));
}
and here is the main_menu_item xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:padding="6dp"
android:id="#+id/main_menu_item"/>
and move_list_items xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:padding="6dp"
android:id="#+id/move_list_item"/>
When I run this code, I do not get any errors of any kind, but nothing but a white background shows up on the screen. I do not understand what I am doing wrong.
Calling findViewById(R.id.fragment_container_left) will give you the FrameLayout, not the fragment, which obviously will never be null. You should check whether the respective fragment already exists using findFragmentById:
if(getFragmentManager().findFragmentById(R.id.fragment_container_left) == null) {
MenuListFragment menuListFragment = new MenuListFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container_left, menuListFragment);
ft.commit();
}
As another heads up, you can link the the content fragment with the list fragment using setTargetFragment on the list fragment. This will automatically reconnect the fragments should the fragments be recreated.
Related
I am trying to go to another fragment when clicking a button but i need to keep the button even when i am in the other fragment. I came out with this but it doesn't go to the other fragment when i click the button.in the activity nothing happens at all except the first fragment is shown:
public class Signupnew extends AppCompatActivity {
Button next;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signupnew);
getSupportActionBar().hide();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
next = (Button) findViewById(R.id.nextf);
final Fragment fragment = new f1();
final Fragment fragment2 = new f2();
FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.contentFragment, fragment);
transaction.commit();
next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (fragment.isAdded()){
System.out.println("added");
transaction.replace(R.id.contentFragment,fragment2);
}
if (fragment2.isAdded()){
System.out.println("added2");
transaction.replace(R.id.contentFragment, fragment);
}
}
});
}
}
XML:
<LinearLayout 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:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent"
android:background="#drawable/signupmorenew"
tools:context=".Signupnew">
<FrameLayout
android:id="#+id/contentFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="#+id/nextf"
android:layout_width="350dp"
android:layout_height="60dp"
android:layout_marginLeft="28dp"
android:layout_marginTop="540dp"
android:background="#drawable/nextsignupandsignin"
android:fontFamily="#font/varroxfont"
android:text="التالي"
android:textColor="#FDBC30"
android:textSize="20dp" />
</FrameLayout>
</LinearLayout>
You have to call FragmentTransaction#commit to apply changes. Also I think it is a good practice to call the whole stack of operations for fragment transaction.
getSupportFragmentManager().beginTransaction()
.replace()
.commit()
Now I know this is a very simple question and was asked many times, but non of the solutions provided previously has worked for me.
I have an activity with a FrameLayout as follows:
<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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/replace_me"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
And in my MainActivity class I have the following:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
replaceFragment(new ChooseLevelFragment(), false);
}
}
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction replace = getSupportFragmentManager().beginTransaction().replace(R.id.replace_me, fragment);
if (addToBackStack) {
replace.addToBackStack(null);
}
replace.commit();
Log.d(TAG, "Fragment replaced!");
}
The first fragment is being shown correctly. However, when the user clicks on an item from the menu and logs in, the app should replace the current fragment with the new one
private void showAuthenticationDialog() {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment fragmentByTag = getSupportFragmentManager().findFragmentByTag(AuthenticationDialogFragment.TAG);
if (fragmentByTag != null) {
fragmentTransaction.remove(fragmentByTag);
}
fragmentTransaction.addToBackStack(null);
AuthenticationDialogFragment authenticationDialogFragment = AuthenticationDialogFragment.newInstance(() -> {
replaceFragment(new ParentControlPanelFragment(), true);
});
authenticationDialogFragment.show(fragmentTransaction, AuthenticationDialogFragment.TAG);
}
The new fragment is not being replaced even after the commit. I can see my log message but not the new fragment. I'm not quite sure where the problem is.
The problem was that I was trying to replace the fragment before I dismiss the dialog.
The correct approach is to dismiss the dialog first then try to replace the fragment.
I am trying to show multiple fragments on the one screen by creating them programmatically. I am able to do this no problem by including each fragment into the activities layout file. However when I try and do it programmatically Im confused.This is what I have so far for two fragments on a screen.
Main Class:
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentOne one = new FragmentOne();
FragmentTwo two = new FragmentTwo();
FragmentThree three = new FragmentThree();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.imOne, one, "fragmentone");
ft.add(R.id.imTwo, two, "fragmenttwo");
ft.add(R.id.imThree, three, "fragmenthree");
ft.commit();
}
}
Fragment One:
public class FragmentOne extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.frag_one, container, false);
return view;
}
}
Fragment Two:
public class FragmentTwo extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.frag_two, container, false);
return view;
}
}
The layout files for my two fragment classes just contain a simple TextView.
And then the activity_main.xml layout file for my main class:
<?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="horizontal" >
<FrameLayout
android:id="#+id/imOne"
android:name="com.david.twofragexample.FragmentOne"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<FrameLayout
android:id="#+id/imTwo"
android:name="com.david.twofragexample.FragmentTwo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<FrameLayout
android:id="#+id/imThree"
android:name="com.david.twofragexample.FragmentThree"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
When I try and do it programatically I get confused as to what should be in my activity_main.xml file. So from the Android Developer Guide(http://developer.android.com/guide/components/fragments.html) it says to use
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentOne fragment = new FragmentOne();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
So when I add this code to my onCreate method what changes do I have to make to my activity_main.xml file to do this programmatically?I don't see where R.id.fragment_container comes from. How can I determine where on the screen my new fragment will go?Thanks
EDIT: I am developing this on a tablet and not a phone.This has now been solved and the above code will add three fragments programmatically.
<?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="horizontal" >
<FrameLayout
android:id="#+id/list"
android:name="com.david.twofragexample.FragmentOne"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<FrameLayout
android:id="#+id/viewer"
android:name="com.david.twofragexample.FragmentTwo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
In your Activity:
FragmentOne one = new FragmentOne();
FragmentTwo two = new FragmentTwo();
fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.list, one, "fragmentone").commit();
fm.beginTransaction().replace(R.id.viewer, two, "fragmenttwo").commit();
I suggest you study this:
http://developer.android.com/reference/android/app/Fragment.html
and fully understand the fragment lifecycle, etc. The way I show is quick and dirty, but not neccesarilly best.
As I don't have higher enough reps to comment yet, I will just leave the information here -- the way Rafael T suggested does work nicely, although I also had my doubt at the first place.
1) Define your xml like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
2) Add each fragment into the same slot:
View box = v.findViewById(R.id.container);
FragmentTransaction ft = getFragmentManager().beginTransaction();
for (String catalog : mCatalogs) {
Fragment fragment = HighlightedProductFragment.newInstance(catalog);
ft.add(R.id.container, fragment, catalog);
}
ft.commit();
Thanks for the solution for this tricky requirement.
Try it like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
as your activity_main.xml
You asked yourself the right question: where is the id fragment_container, and the answer was: nowhere. >ou also set your layout to horizontal, which makes it very likely, that added Views will be kind of 'out of screen'
I have a layout file containing a listview that I would like to fill with the help of a Fragment. But it continues to give me errors.
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" >
</ListView>
<TableLayout
android:id="#+id/details"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:stretchColumns="1" >
<Button
android:id="#+id/create_patient_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/create_patient_button" />
</TableLayout>
</RelativeLayout>
My fragmentActivity:
public class BasicFragmentActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_patient_view);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.list);
if (fragment == null) {
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.list, new BasicFragment());
ft.commit(); // Make sure you call commit or your Fragment will not be added.
// This is very common mistake when working with Fragments!
}
}
}
My ListFragment:
public class BasicFragment extends ListFragment {
private PatientAdapter pAdapter;
#Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
pAdapter = new PatientAdapter(getActivity(), GFRApplication.dPatients);
setListAdapter(pAdapter);
}
}
The error:
java.lang.UnsupportedOperationException: addView(View) is not supported in AdapterView
findFragmentById(...) function takes ID of fragment(!) as parameter. But you call it with R.id.list which is ID of ListView (<ListView android:id="#+id/list" ...). It's wrong because ListView is NOT a fragment. It's the first problem.
The second problem is:
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.list, new BasicFragment());
in ft.add() function first parameter is ID of container in which you want to put your fragment. But you use R.id.list which is id of your ListView. It is wrong because ListView is not a container in which you can put fragments directly.
If you want put fragments into ListView items you can:
fill ListView with custom views.
declare <fragment ...> into custom view layout (XML). Or create fragment container in custom view layout (FrameLayout for example) and put there fragment at runtime (in getView() method).
I haven't been able to find a way how to dynamically add fragment into existing dynamically added fragment. Do you know, if it is possible?
I am generating fragments this way:
FragmentManager fragMgr = getSupportFragmentManager();
FragmentTransaction xact = fragMgr.beginTransaction();
if(null == fragMgr.findFragmentByTag(FRAG1_TAG)) {
xact.add(10101010, new DateTime(), FRAG1_TAG);
}
if(null == fragMgr.findFragmentByTag(FRAG4_TAG)) {
xact.add(7777, new loginForm(), FRAG4_TAG);
}
xact.commit();
How to add into FRAG4_TAG fragment another one?
Edit2:
I hard coded it's id to be able to work with it in future (where ll is my linearLayout in XML):
FrameLayout frml4 = (FrameLayout)inflater.inflate(R.layout.frame,null);
frml4.setId(7777);
frml4.setBackgroundColor(Color.YELLOW);
ll.addView(frml4);
I assume the problem that you are running into is that there is not an inflated view to add the fragment to because the original fragment, FRAG4_TAG, has not been inflated before you are trying to add it.
You can pass enough information to FRAG4_TAG in the Arguments to let it know that it should create and add a fragment (or what all fragments you need it to have) to itself during it's onCreateView, after the view has been inflated...
The layout for the activity...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="MyActivity"/>
<LinearLayout
android:orientation="vertical"
android:id="#+id/main_frag_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The Activity...
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragOne = new MyFragment();
Bundle arguments = new Bundle();
arguments.putBoolean("shouldYouCreateAChildFragment", true);
fragOne.setArguments(arguments);
ft.add(R.id.main_frag_container, fragOne);
ft.commit();
}
}
The layout for the fragment...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="#+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Some fragment"/>
<LinearLayout
android:orientation="vertical"
android:id="#+id/frag_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The fragment...
public class MyFragment extends Fragment {
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.frag_layout, container, false);
boolean shouldCreateChild = getArguments().getBoolean("shouldYouCreateAChildFragment");
if (shouldCreateChild) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fm.beginTransaction();
Fragment fragTwo = new MyFragment();
Bundle arguments = new Bundle();
arguments.putBoolean("shouldYouCreateAChildFragment", false);
fragTwo.setArguments(arguments);
ft.add(R.id.frag_container, fragTwo);
ft.commit();
}
return layout;
}
}
This example covers the case where you need to dynamically add fragments to a fragment that HAS NOT already been inflated and added to the hierarchy. Adding a fragment to a fragment that HAS already been inflated and added to the hierarchy is as simple as just specifying the target fragments container that you want to add to by ID like you would normally.
As the documentation states "A fragment must always be embedded in an activity".
So when you add a "sub-fragment" it will always belong to the activity even if you add it within your fragment class. For example if you later decide to remove the containing fragment the sub fragments won't be automatically removed.
When I had to do the same I had to store in a vector the sub fragments and manually remove them in the onDestroy methods of my container fragment.
I think that fragments are not thought to be used like this
You cannot insert Fragments into other Fragments. (At least, not yet)
You can however replace Fragments with other Fragments with FragmentTransaction.replace(containerViewId, Fragment).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.linear1, new activity()).commit();
}