I have an app that has 1 Main Activity and 5 main fragments. When the MainActivity is created I create a List containing each of the 5 fragments. The user is presented with a tab bar on the bottom of the screen which he/she can use to navigate between fragments. How do I set this up so as when the user selects a tab, the corresponding fragment is shown without creating a new instance of it? Just want to change the view on the screen to the already created fragment.
I am using a BottomBar from https://github.com/roughike/BottomBar which calls a "onTabSelected" interface method when a tab is pressed.
You can use 5 Fragments with the library you specified like this. The layout file should look like this
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- This could be your fragment container, or something -->
<FrameLayout
android:id="#+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/bottomBar" />
<com.roughike.bottombar.BottomBar
android:id="#+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
app:bb_tabXmlResource="#xml/bottombar_tabs" />
</RelativeLayout>
The containing Activity class will replace the Framelayout with Fragment depending on the Fragment selected from the BottomBar View. A simple example
public class Main3Activity extends AppCompatActivity {
private Fragment fragment;
private FragmentManager fragmentManager;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three_tabs);
BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
#Override
public void onTabSelected(#IdRes int tabId) {
if(tabId == R.id.tab_home){
fragment = new HomeFragment();
}
if(tabId == R.id.tab_favorite){
fragment = new FavoriteFragment();
}
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.contentContainer, fragment).commit();
});
bottomBar.setOnTabReselectListener(new OnTabReselectListener() {
#Override
public void onTabReSelected(#IdRes int tabId) {
Toast.makeText(getApplicationContext(), TabMessage.get(tabId, true), Toast.LENGTH_LONG).show();
}
});
}
}
Then you can create individual Fragment with their content like below
public class FavoriteFragment extends Fragment {
public FavoriteFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_favorite, container, false
);
}
}
Related
I am building a quiz app with three questions and therefore I have 6 Fragments but I would like to disable the default transition from one fragment to another, how can I achieve this? I already disabled that you can swipe between the fragments, you have to click a button to get to the next fragment, but there is still somehow a swiping transition after clicking the button. I searched for this but there was never an answer that would fit my problem. Here is one fragment example:
public class FragmentQuestion1 extends Fragment {
private Button btnNavFrag1;
private EditText editText;
private ProgressBar m_bar;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_question_1, container, false);
btnNavFrag1 = view.findViewById(R.id.btn_question1);
editText = view.findViewById(R.id.edit_text_question_1);
editText.addTextChangedListener(new NumberTextWatcher(editText));
m_bar = view.findViewById(R.id.progress_bar_question_1);
btnNavFrag1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((GameActivity)getActivity()).setViewPager(2);
}
});
return view;
}
// Method that is used so the countdown starts when the user gets to this fragment
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
startCountdownTimer();
}
}
// Countdown 17 seconds
int i = 0;
private void startCountdownTimer() {
m_bar.setProgress(i);
final int totalMsecs = 17 * 1000; // 17 seconds in milli seconds
int callInterval = 100;
/** CountDownTimer */
new CountDownTimer(totalMsecs, callInterval) {
public void onTick(long millisUntilFinished) {
int secondsRemaining = (int) millisUntilFinished / 1000;
float fraction = millisUntilFinished / (float) totalMsecs;
// progress bar is based on scale of 1 to 100;
m_bar.setProgress((int) (fraction * 100));
}
public void onFinish() {
}
}.start();
}
Because you are using a viewPager, you could use a library like this which lets you add a transformer (some sort of effect) to the viewPager when going to the next fragment. I'd recommend the ZoomOutTranformer for your use-case. If the transition is not what you expect you can always extend from that class and override the transition so it's more to your liking.
ViewPagerTransformers are native, so you don't need to use a library. Just create a class, implement the PageTransformer interface and override the method transformPage.
In your layout xml define a tablayout and a FrameLayout like this:
..................your xml code...............
.............................................................
<com.google.android.material.tabs.TabLayout
android:id="#+id/simpleTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabBackground="#color/colorPrimary"
app:tabIndicatorColor="#0080FF"
app:tabSelectedTextColor="#050505"
app:tabTextColor="#color/colorAccent">
<com.google.android.material.tabs.TabItem
android:id="#+id/abcd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Item 1" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item 2" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="item 3" />
</com.google.android.material.tabs.TabLayout>
<FrameLayout
android:id="#+id/simpleFrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
This layout is defined for three tabs. You can make it for more tabs by adding more tabs if you like.
Then define the fragments for each tab. In this case:
item_one_fragment:
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="your.activity.context">
......................................
............................................
fragments xml
.............................
,.....................................
</RelativeLayout>
Similarly item 2 and item 3 fragment layouts.
Then define the fragments in java:
public class FirstItemFragment extends Fragment {
public ListView CallListView;
public FirstFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.item_one_fragment, container, false);
// Inflate the layout for this fragment
return view;
}
}
Simiarly second and third fragment.
Then in your Main Activity:
FrameLayout simpleFrameLayout = (FrameLayout) findViewById(R.id.simpleFrameLayout);
TabLayout tabLayout = (TabLayout) findViewById(R.id.simpleTabLayout);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.simpleFrameLayout, new FirstItemFragment());
ft.commit();
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
// get the current selected tab's position and replace the fragment accordingly
Fragment fragment = null;
switch (tab.getPosition()) {
case 0:
fragment = new FirstItemFragment();
break;
case 1:
fragment = new SecondItemFragment();
break;
case 2:
fragment = new ThirdItemFragment();
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.simpleFrameLayout, fragment)
.addToBackStack(null)
//Commit the transaction.
.commit();
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Now when you run your activity you will have three tabs and when you click on any one you will go to the respective fragment without any swipe
I'm doing an app so when you click on a login button, it will then display a screen with a bottom menu. But I'm having problems with setting the listener to the items in the menu. The problem is when I try to use findViewById to find my BottomNavigationView it will look for it in my activity_main instead of the fragment (welcome_fragment) where I have put it, because I only want it to show when someone has logged in. And therefore I will get null as the BottomNavigationView.
I have tried setting setContentView(R.layout.welcome_fragment) in my method "welcomePage", which does make it so that so that it will find it and it won't be null. However this results in a lot of other errors such as
" No view found for id 0x7f080032 (com.example.MyAPP:id/container) for fragment WelcomePage{9547e9f #1 id=0x7f080032}", so I don't believe it to be the right way to go about it.
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
Login loggingIn= new Login();
fm.beginTransaction().replace(R.id.container, loggingIn).commit();
}
//When button is pressed we get into this function
public void finishProcess(){
WelcomePage wp = new WelcomePage();
FragmentManager fm = getFragmentManager();
fm.beginTransaction().addToBackStack(null);
fm.beginTransaction().replace(R.id.container, wp).commit();
try {
//Here is where I get null, because we are looking in activity_main
BottomNavigationView bottomNav=findViewById(R.id.bottomnavigate);
bottomNavigation.setOnNavigationItemSelectedListener(menuListener);}
catch (Exception e) {
System.out.println(e);
}
}}
//Have tried this one and it works fine
private BottomNavigationView.OnNavigationItemSelectedListener menuListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {...}
}
Here is my WelcomeFragment:
public class WelcomePage extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.welcome_fragment, container, false)}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Here is my layout to the welcome_fragment:
<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"
tools:context=".WelcomePage"
android:background="#drawable/welcomegradient">
<FrameLayout
android:id="#+id/showing_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottomnavigate">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottomnavigate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:menu="#menu/mymenu"
android:background="?android:attr/windowBackground" />
</RelativeLayout>
As I mentioned earlier the problem is that findViewById can't find the BottomNavigationView because it is looking in the activity_main instead of welcome_fragment and I don't want to put my menu in activity_main because I only want to display it when a user has logged in. How do I fix this problem?
Try this, it should work.
WelcomePage
public class WelcomePage extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.welcome_fragment, container, false);
//Have tried this one and it works fine
BottomNavigationView.OnNavigationItemSelectedListener menuListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {...}
BottomNavigationView bottomNav = view.findViewById(R.id.bottomnavigate);
bottomNav.setOnNavigationItemSelectedListener(menuListener);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private FragmentManager fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fm = getSupportFragmentManager();
Login loggingIn= new Login();
fm.beginTransaction().replace(R.id.container, loggingIn).commit();
}
//When button is pressed we get into this function
public void finishProcess() {
WelcomePage wp = new WelcomePage();
fm.beginTransaction().addToBackStack(null);
fm.beginTransaction().replace(R.id.container, wp).commit();
}
}
I'm trying to start a fragment from my main activity, but somehow the fragment is not showing up. Can someone help me with this?
Here is my code:
public class Main extends Activity implements OnClickListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.action_menu);
b.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.action_menu){
Log.d("--", "menu clicked");
MenuFragment newFragment = new MenuFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(android.R.id.content, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
main activity layout:
<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/main_rl" >
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="#+id/action_menu" android:text="Menu"/>
</RelativeLayout>
and my fragment Class:
public class MenuFragment extends Fragment{
final String TAG=this.getClass().getSimpleName();
private GridView grid;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View mView;
Log.d(TAG, "Hello from Fragment");
mView=inflater.inflate(R.layout.menu_fullscreen, null);
initWidgets(mView);
return mView;
}
private void initWidgets(View mView) {
grid=(GridView) mView.findViewById(R.id.menu_fullscreen_grid);
}
}
you cannot replace what you put in setContentView(R.layout.main); if main.xml is hardCoded...(xml file). with
transaction.replace...
You may use fragmentActivity without setContentView(R.layout.main)
I'm trying do understand the lifecycle of Fragment.
I have a MainActivity, with a layout including two FrameLayout to load Fragments, one for a top bar with a vertical menu, another one for the content.
When I start the app, an instance of HomeFragment is loaded in the content FrameLayout container. Then when I click on a button in the menu, an instance of SomeFragment replaces it.
When on the SomeFragment content, I click on the device's Home button, and all the visible Fragment (i.e. the TopBarFragment and the SomeFragment) are destroyed and detached from the MainActivity, i.e. onDestroyView, onDestroy and onDetach are called for both.
Now when I restart the app, the TopBarFragment and the SomeFragment are created and destroyed (full lifecycle from onAttach to onDetach) and then the TopBarFragment and the HomeFragment are created, as expected from the code in the MainActivity's onCreate.
Why are the TopBarFragment and the SomeFragment, i.e. the latest visible Fragment before clicking on the device's Home button, recreated and destroyed before executing what's in the MainActivity's onCreate?
Note: in order to test my app I checked the option Don't keep activities in my device's developer options.
MainActivity.java
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment homeFragment = new HomeFragment();
fragmentTransaction.replace(R.id.content_container, homeFragment, "");
Fragment topBarFragment = new TopBarFragment();
fragmentTransaction.replace(R.id.top_bar_container, topBarFragment, "top_bar_fragment");
fragmentTransaction.commit();
}
}
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" >
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="44dp" />
<FrameLayout
android:id="#+id/top_bar_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false" />
</RelativeLayout>
TopBarFragment.java
public class TopBarFragment extends Fragment{
private int mSelectedMenuOption = 0;
private LinearLayout mVerticalMenu;
private Boolean mMenuIsOpen = true;
private ImageButton btn_01, btn_02; // there are more
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.top_bar, container, false);
}
#Override
public void onActivityCreated (Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
btn_01 = (ImageButton) getView().findViewById(R.id.btn_01);
btn_02 = (ImageButton) getView().findViewById(R.id.btn_02);
btn_01.setOnClickListener(mButtonClickListener);
btn_02.setOnClickListener(mButtonClickListener);
mVerticalMenu = (LinearLayout) getView().findViewById(R.id.vertical_menu);
toggleMenu(0);
Button btn_menu = (Button) getView().findViewById(R.id.btn_menu);
btn_menu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// toggle vertical menu
}
});
}
private OnClickListener mButtonClickListener = new OnClickListener()
{
#Override
public void onClick(View v) {
/* ... */
if(!v.isSelected()){
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
switch(v.getId()){
case R.id.btn_01:
Fragment homeFragment = new HomeFragment();
fragmentTransaction.replace(R.id.content_container,homeFragment, "");
fragmentTransaction.commit();
break;
case R.id.btn_02:
Fragment someFragment = new SomeFragment();
fragmentTransaction.replace(R.id.content_container, someFragment, "");
fragmentTransaction.commit();
break;
}
}
}
};
}
SomeFragment
public class SomeFragment extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
((TopBarFragment)getActivity().getSupportFragmentManager().findFragmentByTag("top_bar_fragment")).setSelectedButton(1);
return inflater.inflate(R.layout.some_fragment, container, false);
}
}
I want to add a Fragment to an Activity that implements its layout programmatically. I looked over the Fragment documentation but there aren't many examples describing what I need. Here is the type of code I tried to write:
public class DebugExampleTwo extends Activity {
private ExampleTwoFragment mFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frame = new FrameLayout(this);
if (savedInstanceState == null) {
mFragment = new ExampleTwoFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(frame.getId(), mFragment).commit();
}
setContentView(frame);
}
}
...
public class ExampleTwoFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
Button button = new Button(getActivity());
button.setText("Hello There");
return button;
}
}
This code compiles but crashes at start, probably because my FragmentTransaction.add() is incorrect. What is the correct way to do this?
It turns out there's more than one problem with that code. A fragment cannot be declared that way, inside the same java file as the activity but not as a public inner class. The framework expects the fragment's constructor (with no parameters) to be public and visible. Moving the fragment into the Activity as an inner class, or creating a new java file for the fragment fixes that.
The second issue is that when you're adding a fragment this way, you must pass a reference to the fragment's containing view, and that view must have a custom id. Using the default id will crash the app. Here's the updated code:
public class DebugExampleTwo extends Activity {
private static final int CONTENT_VIEW_ID = 10101010;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frame = new FrameLayout(this);
frame.setId(CONTENT_VIEW_ID);
setContentView(frame, new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
if (savedInstanceState == null) {
Fragment newFragment = new DebugExampleTwoFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(CONTENT_VIEW_ID, newFragment).commit();
}
}
public static class DebugExampleTwoFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
EditText v = new EditText(getActivity());
v.setText("Hello Fragment!");
return v;
}
}
}
Here is what I came up with after reading Tony Wong's comment:
public class DebugExampleTwo extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addFragment(android.R.id.content,
new DebugExampleTwoFragment(),
DebugExampleTwoFragment.FRAGMENT_TAG);
}
}
...
public abstract class BaseActivity extends Activity {
protected void addFragment(#IdRes int containerViewId,
#NonNull Fragment fragment,
#NonNull String fragmentTag) {
getSupportFragmentManager()
.beginTransaction()
.add(containerViewId, fragment, fragmentTag)
.disallowAddToBackStack()
.commit();
}
protected void replaceFragment(#IdRes int containerViewId,
#NonNull Fragment fragment,
#NonNull String fragmentTag,
#Nullable String backStackStateName) {
getSupportFragmentManager()
.beginTransaction()
.replace(containerViewId, fragment, fragmentTag)
.addToBackStack(backStackStateName)
.commit();
}
}
...
public class DebugExampleTwoFragment extends Fragment {
public static final String FRAGMENT_TAG =
BuildConfig.APPLICATION_ID + ".DEBUG_EXAMPLE_TWO_FRAGMENT_TAG";
// ...
}
Kotlin
If you are using Kotlin make sure to take a look at what the Kotlin extensions by Google provide or just write your own.
public class Example1 extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DemoFragment fragmentDemo = (DemoFragment)
getSupportFragmentManager().findFragmentById(R.id.frame_container);
//above part is to determine which fragment is in your frame_container
setFragment(fragmentDemo);
(OR)
setFragment(new TestFragment1());
}
// This could be moved into an abstract BaseActivity
// class for being re-used by several instances
protected void setFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(android.R.id.content, fragment);
fragmentTransaction.commit();
}
}
To add a fragment into a Activity or FramentActivity it requires a
Container. That container should be a "Framelayout", which can be
included in xml or else you can use the default container for that
like "android.R.id.content" to remove or replace a fragment in
Activity.
main.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Framelayout to display Fragments -->
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/imagenext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="16dp"
android:src="#drawable/next" />
</RelativeLayout>
After read all Answers I came up with elegant way:
public class MyActivity extends ActionBarActivity {
Fragment fragment ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
fragment = fm.findFragmentByTag("myFragmentTag");
if (fragment == null) {
FragmentTransaction ft = fm.beginTransaction();
fragment =new MyFragment();
ft.add(android.R.id.content,fragment,"myFragmentTag");
ft.commit();
}
}
basically you don't need to add a frameLayout as container of your fragment instead you can add straight the fragment into the android root View container
IMPORTANT: don't use replace fragment as most of the approach shown here, unless you don't mind to lose fragment variable instance state during onrecreation process.
For attaching fragment to an activity programmatically in Kotlin, you can look at the following code:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// create fragment instance
val fragment : FragmentName = FragmentName.newInstance()
// for passing data to fragment
val bundle = Bundle()
bundle.putString("data_to_be_passed", DATA)
fragment.arguments = bundle
// check is important to prevent activity from attaching the fragment if already its attached
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.add(R.id.fragment_container, fragment, "fragment_name")
.commit()
}
}
}
}
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=".ui.MainActivity">
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
FragmentName.kt
class FragmentName : Fragment() {
companion object {
fun newInstance() = FragmentName()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// receiving the data passed from activity here
val data = arguments!!.getString("data_to_be_passed")
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
If you are familiar with Extensions in Kotlin then you can even better this code by following this article.
public abstract class SingleFragmentActivity extends Activity {
public static final String FRAGMENT_TAG = "single";
private Fragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
fragment = onCreateFragment();
getFragmentManager().beginTransaction()
.add(android.R.id.content, fragment, FRAGMENT_TAG)
.commit();
} else {
fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
}
}
public abstract Fragment onCreateFragment();
public Fragment getFragment() {
return fragment;
}
}
use
public class ViewCatalogItemActivity extends SingleFragmentActivity {
#Override
public Fragment onCreateFragment() {
return new FragmentWorkShops();
}
}
For API level 17 or higher, View.generateViewId() will solve this problem. The utility method provides a unique id that is not used in build time.
This may help you
Defining a Fragment
create xml file for fragment view fragment_abc.xml
<?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="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
create fragment ABCFragment.java
import androidx.fragment.app.Fragment;
public class FooFragment extends Fragment {
// The onCreateView method is called when Fragment should create its View object hierarchy,
// either dynamically or via XML layout inflation.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle
savedInstanceState) {
// Defines the xml file for the fragment
return inflater.inflate(R.layout.fragment_abc, parent, false);
}
// This event is triggered soon after onCreateView().
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Setup any handles to view objects here
// EditText etFoo = (EditText) view.findViewById(R.id.etFoo);
}
}
Add frameLayout in your activity
<FrameLayout
android:id="#+id/your_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent">
now in activity, add following method
protected void setFragment() {
// Begin the transaction
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Replace the contents of the container with the new fragment
ft.replace(R.id.fragment_container, new ABCFragment());
// or ft.add(R.id.your_placeholder, new ABCFragment());
// Complete the changes added above
ft.commit();
}
reference : https://guides.codepath.com/android/creating-and-using-fragments#defining-a-fragment