How to add fragment to existing fragment dynamically? - java

I want to add a fragment to an existing fragment on a button press (button sits on parent fragment) but I get an error: java.lang.ClassCastException: must implement OnFragmentInteractionListener.
What does that mean and why doesn't any of the example have it?
Parent fragment button press code:
Button interestedButton = (Button) myFragmentView.findViewById(R.id.interestedButton);
interestedButton.setOnClickListener(new View.OnClickListener() {
InterestedFormFragment interestedFormFragment = new InterestedFormFragment();
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction().add(R.id.interestedFrag_container, interestedFormFragment, "INTERESTED_FORM");
fragmentTransaction.commit();
}
});
Parent fragment 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"
android:background="#FFFFFF"
android:layout_gravity="bottom"
android:id="#+id/buildingPageId">
<LinearLayout
android:orientation="vertical"
android:id="#+id/interestedFrag_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
The "interestedFrag_container" should hold the newly added child fragment.
My parent fragment isinflated and hold the button that should add the child.
What am I missing here?
Thank you!

Nested fragment are only added since API 17.
You can use getChildFragmentManager() to manage your nested fragments.

Related

Fragment transaction from main activity doesn't work

I'm writing my first android app using fragments but I can't figure out why it doesn't change view from main activity to fragment.
This is my mainActivity.java:
public class MainActivity extends AppCompatActivity {
Button fragment1_BTN, fragment2_BTN;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1_BTN = findViewById(R.id.frag1_btn);
fragment2_BTN = findViewById(R.id.frag2_btn);
fragment1_BTN.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
replaceFragment(new Frag1());
}
});
fragment2_BTN.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
replaceFragment(new Frag2());
}
});
}
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frameLayout, fragment);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.commit();
}}
This is my activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="#+id/therapists_btn"
android:layout_width="150dp"
android:layout_height="60dp"
android:backgroundTint="#color/teal_700"
android:text="button 1"
android:layout_marginRight="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/patients_btn" />
<Button
android:id="#+id/patients_btn"
android:layout_width="150dp"
android:layout_height="60dp"
android:layout_marginLeft="10dp"
android:backgroundTint="#color/teal_700"
android:text="button 2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/therapists_btn"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout>
The problem is that when I click on button 1 I see the view of first fragment and also of main activity.
How can I see only the fragment view?
your frameLayout container for Fragments is placed before LinearLayout, Fragments View is loaded into it and is visible, but also rest of Views is drawn after that (as order in XML) on top of it. just setVisibility(View.GONE) for this LinearLayout when you are loading Fragment (set for it some id and use findViewById as for buttons). another approach would be to put your LinearLayout inside container - you are using replace method so Fragment will remove your buttons, but in that case these are gone forever then and you don't have an easy option for bringing them back (with your current code - only whole Activity restart)
it would be way better (by design) if your Activity wouldn't have these buttons at all, only container, and on start would check if container has anything loaded (e.g. getChildCount()==0), if not then load some initial Fragment for preventing displaying empty Activity

Android - Add navigation drawer to multiple activities

I'm new to Android / Kotlin and I have an application with multiple activities.
So far I have created all the activities needed and the app works fine except for the fact now I need to add a Navigation Drawer.
Coming from iOS, I designed all my layouts thinking I can add the Navigation Drawer after:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/backgroundColor"
android:backgroundTint="#color/backgroundColor">
<RelativeLayout
android:id="#+id/mainRelativeLayout"
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="#drawable/navbar_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/ivNavBarLogo"
android:layout_width="102dp"
android:layout_height="24dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:contentDescription="#string/empty_description"
app:srcCompat="#mipmap/navbar_logo"/>
<ImageButton
android:id="#+id/btnSideMenu"
android:layout_width="25dp"
android:layout_height="15dp"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:background="#mipmap/sidemenu_button"
android:contentDescription="#string/empty_description"
app:srcCompat="#mipmap/sidemenu_button"/>
</RelativeLayout>
<!-- STUFF TO BE ADDED THERE -->
</android.support.constraint.ConstraintLayout>
My intention was to create a NavigationDrawer activity and call it when tapping the btnSideMenu on each activity.
However, I've been trying to get around this for a few days now without any luck. I never thought such a basic feature could be so hard to implement. I tried almost every suggestion and example found here, and also this: https://developer.android.com/training/implementing-navigation/nav-drawer.html
Am I thinking this issue the wrong way? Is there any way of doing this without having to recreate all my activities based on the template provided by Android Studio or using fragments?
LE: Forgot to mention that I removed the ActionBar from all activities.
To add a navigation drawer to multiple activities without going through and re-writing every activity's layout, you can leverage a base activity class and a ViewStub tag.
Create a layout like this (I'll refer to it as navdrawer_activity.xml later):
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<ViewStub
android:id="#+id/navdrawer_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!--
your drawer here
you can use android.support.design.widget.NavigationView
or some other view if you want
-->
</android.support.v4.widget.DrawerLayout>
Then create a class for all of your activities to extend:
public class NavDrawerActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.navdrawer_activity);
}
#Override
public final void setContentView(#LayoutRes int layoutResID) {
ViewStub stub = findViewById(R.id.navdrawer_stub);
stub.setLayoutResource(layoutResID);
stub.inflate();
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// set up nav drawer as normal, using whatever
// callback methods or helper methods you want
}
}
With these in place, the only thing you have to do is change any activity where you want the nav drawer to extend NavDrawerActivity instead of AppCompatActivity.
Well as the guide you included says:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout ...>
<!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
<content goes here
... />
<!-- Container for contents of drawer -->
<left pane of drawer goes here
...
android:layout_gravity="start" />
</android.support.v4.widget.DrawerLayout>
You could define the left pane of the drawer as a compound view, and the content as a compound view. Kinda like this:
your_view.xml
<YourView 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">
<Button android:id="#+id/..."
android:layout_width="..."
android:layout_height="..."
android:layout_centerInParent="true"/>
</YourView>
And
public class YourView extends RelativeLayout {
public YourView(Context context) {
super(context);
}
public YourView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// ... two more constructors are possible here
private Button button;
#Override
public void onFinishInflate() {
super.onFinishInflate();
button = findViewById(R.id.button);
button.setOnClickListener((view) -> {
...
}
}
}
And you can do the same thing for the view of the left pane. Make sure you include the android:layout_gravity="start" as an attribute of <YourLeftPane in its xml.
And now you can do in your activity_whatever.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout ...>
<include layout="#layout/your_view"/>
<include layout="#layout/your_left_pane" />
</android.support.v4.widget.DrawerLayout>

How to start a fragment from an activity

I am trying to run a class extending a fragment from a class extending AppCompactActivity, I have tried everything I have saw in Stackover flow and I cant get any to fix my problem. LineDetails is extending the Fragment
Progress1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Fragment fr = new LineDetails();
android.app.FragmentManager fm = getFragmentManager();
android.app.FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr);
fragmentTransaction.commit();
}
});
XML
<fragment android:name="com.almac.tracker.LineDetails"
android:id="#+id/fragment_place"
android:layout_width="match_parent"
android:layout_height="match_parent" />
PART OF XML FOR ACTIVITY CLASS
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:overScrollMode="never"
android:scrollbars="none"
tools:context=".Dashboard">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/fragment_place">
</FrameLayout>
</RelativeLayout >
If your activity extends AppCompatActivity, then you cannot use getFragmentManager().
In fact you should get rid of classes in the package android.app such as android.app.FragmentManager. You should use the support classes from the package android.support.v4.app such as android.support.v4.app.FragmentManager
Use getSupportFragmentManager() instead of getFragmentManager()
The stack trace reports that you don't have any Layout with id R.id.fragment_place inside your activity. Check the xml of your activity and correct the id of fragment holder.
You're replacing instead of adding without a refresh, just do an add. I also simplified one of your useless variables and just went straight to ft instead of having an intermediate fm.
Progress1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Fragment fr = new LineDetails();
android.app.FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.fragment_place, fr);
ft.commit();
}
});
getSupportFragmentManager() should be used if activity extends AppcompatActivity
EDIT :
As per the Errors ! There is no View named fragment_place in the activity's layout that is #+id/fragment_place must exist in activity_main (If this is the layout attached to activity)
Double check your layout xml, It should have a FrameLayout with attribute #+id/fragment_place act as a placeholder for your LineDetails fragment.
If you already place <fragment /> in your layout, you don't need to replace() it programmatically.

Which Fragment will be replaced when use fragmentTransaction.replace?

I have a activity_main.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="[URL]http://schemas.android.com/apk/res/android[/URL]"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment
android:name="fragments"
android:id="#+id/lm_fragment"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_width="0dp"/>
<fragment
android:name="fragments"
android:id="#+id/pm_fragment"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="2"/>
</LinearLayout>
So I have 2 Fragment in my Activity
And when I add a Fragment in android.R.id.content, which Fragment will be added?
My Mainactivity.java:
if(config.orientation == Configuration.ORIENTATION_LANDSCAPE){
LM_Fragment lm_Fragment = new LM_Fragment();
fragmentTransaction.replace(android.R.id.content, lm_Fragment);
}else{
PM_Fragment pm_Fragment = new PM_Fragment();
fragmentTransaction.replace(android.R.id.content, pm_Fragment);
}
fragmentTransaction.commit();
}
LM_Fragment and PM_Fragment is used for Landscape mode and Portrait mode. And when I use fragmentTransaction.replace(android.R.id.content, lm_Fragment);
It will be added in 1 in 2 fragment which I specified in xml, but where lm_fragment or pm_fragment? Thank you!
Here is replace method signature
public abstract FragmentTransaction replace (int containerViewId, Fragment fragment)
You don't have such id android.R.id.content in your layout.
Thing is that you're using static fragments. But you need dynamic. Replace both fragment tag with single FrameLayout with id=android.R.id.content. Or better use your own id. E.g. "fragment_container", and then call
fragmentTransaction.replace(R.id.fragment_container, lm_Fragment);
and
fragmentTransaction.replace(R.id.fragment_container, pm_Fragment);

How can i access fragment layout in parent activity?

I have got a tabpager and fragments. And a frament has got a "GridView". I am using a basedapter innerclass in the fragment parent activity.
But when i want to set adapter to my gridview i m getting nullpointerexception. Because GridView variable is always null.how can solve this issue?
Fragment Parent MainActivity OnCreate Method;
GridView gv = (GridView)findViewById(R.id.gridview); // debugging and always null
gv.setAdapter(new AdapterB(this,WallPaperList));
Fragment xml
<!-- TODO: Update blank fragment layout -->
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:columnWidth="90dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
MyGrid.xml (For populate with adapter)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/imagepart"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SeƧ"
android:id="#+id/checkBox" />
</LinearLayout>
You cant acces fragment views like that, becouse they could be not yet in the activity view hierarchy.
You have to add some method to your fragment, like
myCustomFragment.setGridAdapter(MyCustomGridViewAdapter adapter)
and in this method in your custom fragment:
public void setGridAdapter(){
this.myCustomGridAdapter = adapter;
GridView gv = (GridView)findViewById(R.id.gridview);
if(gv != null)
gv.setAdapter(this.myCustomGridAdapter);
}
This code:
GridView gv = (GridView)findViewById(R.id.gridview); // debugging and always null
gv.setAdapter(new AdapterB(this,WallPaperList));
should be inside of your fragment's onCreateView. You should create fragments that are independent from the activity they're attached to.
If you absolutely need to access the fragment's layout from your activity you need to delay the findViewById because the fragment's layout is not yet inflated when you call it:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
GridView gv = (GridView)findViewById(R.id.gridview);
gv.setAdapter(new AdapterB(this, WallPaperList));
}
}, 1000);
Although this code would work, I highly discourage you from using it!

Categories

Resources