I'm developing an Android app (I'm a newbie) that uses a Navigation Drawer. I've created multiple fragments that represent the various item in the side menu. One of them has a RecyclerView, and I want to show another fragment with various details when the user clicks one of the item of the RecyclerView.
I've created the structure successfully, implement the click listener on the RecyclerView item, but I don't know how to show the details fragment (and how to go back from it).
If showing another fragment is not the right way, please suggest me the best way to obtain the navigation that I need.
CODE
MenuActivity.xml
<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.DrawerLayout
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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimaryDark"
android:theme="#style/AppTheme.AppBarOverlay"
app:popupTheme="#style/AppTheme.PopupOverlay"
app:titleTextColor="#android:color/white"/>
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar"/>
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="#+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/drawer_view"
app:headerLayout="#layout/nav_header"/>
MenuActivity.kt
class MenuActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
val vendutoFragment = VendutoFragment()
val prezziFragment = PrezziFragment()
val giacenzeFragment = GiacenzeFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu)
setSupportActionBar(toolbar)
val toggle = ActionBarDrawerToggle(this, drawerLayout, toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
navigationView.setNavigationItemSelectedListener(this)
if(savedInstanceState == null){
addFragment(vendutoFragment)
navigationView.setCheckedItem(nav_venduto)
}
}
override fun onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
var selectedFragment = Fragment()
when (item.itemId) {
R.id.nav_venduto -> {
selectedFragment = vendutoFragment
}
R.id.nav_prezzi -> {
selectedFragment = prezziFragment
}
R.id.nav_giacenze -> {
selectedFragment = giacenzeFragment
}
}
replaceFragment(selectedFragment)
drawerLayout.closeDrawer(GravityCompat.START)
return true
}
private fun addFragment(fragment: Fragment){
supportFragmentManager.beginTransaction().add(R.id.frameLayout, fragment).commit()
}
private fun replaceFragment(fragment: Fragment){
supportFragmentManager.beginTransaction().replace(R.id.frameLayout, fragment).commit()
}
}
1st Fragment with RecyclerView and clickable item
class GiacenzeFragment: Fragment(){
var global: Global? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
//returning our layout file
//change R.layout.yourlayoutfilename for each of your fragments
return inflater.inflate(R.layout.fragment_giacenze, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
//you can set the title for your toolbar here for different fragments different titles
activity!!.title = "Giacenze"
}
override fun onActivityCreated(savedInstanceState: Bundle?)
{
super.onActivityCreated(savedInstanceState)
giacenzeTable.layoutManager = LinearLayoutManager(context) as RecyclerView.LayoutManager?
global = getActivity()?.getApplication() as Global
// Access the RecyclerView Adapter and load the data into it
giacenzeTable.adapter = GiacenzeTableAdapter(global!!.prodotti, { prodotto: Prodotto -> prodottoItemClicked(prodotto) })
}
private fun prodottoItemClicked(prodotto: Prodotto)
{
Toast.makeText(context, "Clicked: ${prodotto.name}", Toast.LENGTH_SHORT).show()
var serbatoiFrag = SerbatoiFragment()
serbatoiFrag.idProdotto = prodotto.idProdotto
serbatoiFrag.nameProdotto = prodotto.name
fragmentManager?.beginTransaction()!!.replace(R.id.frameLayout, serbatoiFrag).commit()
}
}
if you want to load fragment in a new activity, just open new activity and use replaceFragment
if you want to load in same activity, just use replaceFragment
do you know how to use replaceFragment?
Related
I just set up a projet with Android navigation component and here's the structure of my graph:
<navigation 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/nav_graph"
app:startDestination="#id/a">
<fragment
android:id="#+id/a"
android:name="com.example.tutorial.fragmentA"
android:label="a">
<action android:id="#+id/action_a_to_b"
app:destination="#id/b"
app:enterAnim="#anim/nav_default_enter_anim"
app:exitAnim="#anim/nav_default_exit_anim"
app:popEnterAnim="#anim/nav_default_pop_enter_anim"
app:popExitAnim="#anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment
android:id="#+id/b"
android:name="com.example.tutorial.fragmentB"
android:label="b">
</fragment>
</navigation>
In fragment A I navigate to B like this:
findNavController().navigate(R.id.action_a_to_b)
In fragment B, I have a custom toolbar and the idea is that a click on the top left arrow should close fragment B and resume A:
(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)
binding.toolbar.setNavigationOnClickListener {
findNavController().popBackStack()
}
Same goes if I click on the Key down press button:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner){
findNavController().popBackStack()
}
The problem I'm still stuck in fragment B: the exit animation starts and ends and I'm still in fragment B. Any ideas how to go about to fix this ?
I've created a small project with 2 fragments and it works flowless.
We have a login Screen and a screen with passwords.
LoginFragment
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_login, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.loginToolbar)
toolbar.navigationIcon = null
// Set the Toolbar as your activity's ActionBar
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
// Find this Fragment's NavController
val navController = NavHostFragment.findNavController(this);
// And set up the ActionBar
NavigationUI.setupActionBarWithNavController(
requireActivity() as AppCompatActivity,
navController
)
val b = view.findViewById<MaterialButton>(R.id.loginButton)
b.setOnClickListener {
this.findNavController().navigate(R.id.passwordsFragment)
}
}
}
PasswordFragment
class PasswordsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_passwords, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.myToolbar)
// Set the Toolbar as your activity's ActionBar
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
// Find this Fragment's NavController
val navController = NavHostFragment.findNavController(this);
// And set up the ActionBar
NavigationUI.setupActionBarWithNavController(
requireActivity() as AppCompatActivity,
navController
)
toolbar.setNavigationOnClickListener {
navController.navigateUp()
}
}
}
Toolbar in the LoginFragment XML
<androidx.appcompat.widget.Toolbar
android:id="#+id/loginToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black"
app:layout_constraintTop_toTopOf="parent" />
Toolbar in the PasswordFragment XML
<androidx.appcompat.widget.Toolbar
android:id="#+id/myToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black"
app:layout_constraintTop_toTopOf="parent" />
MainActivity XML
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/constraintLayout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="6dp"
android:fillViewport="true"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
I navigate from the loginFragment to the PasswordFragment and then I press the up button and I get to the previous fragment :)
Let me know if it works
private lateinit var appBarConfiguration: AppBarConfiguration
//onCreate
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment4) as NavHostFragment
val navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navDrawView.setupWithNavController(navController)
bottomAppBar.background = null
bottomAppBar.setupWithNavController(navController)
//automatic NavigateUp setup
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragment4)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
Can you try this setup in your main activity, it's worked for me I use bottomNav navdrawer and appbar together.
if you wanna use custom Toolbar
//in fragment or activity
private fun handleToolbarChanges() {
with(binding.toolbarDataAnalysis) {
setNavigationOnClickListener { onBackPressed() }
title = "your title here"
}
}
//in xml file
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbarDataAnalysis"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/green"
app:navigationIcon="#drawable/ic_baseline_arrow_back_24"
app:titleTextColor="#color/white" />
Now its possible we can create observer for return values from second fragment using popBackStack()
Suppose we have firstFragment and secondFragment
In firstFragment use this for observer :-
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navController = findNavController()
navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("isDelete")
?.observe(viewLifecycleOwner) {
if(it.lowercase().compareTo("true")==0)
{
//write code here
}
else
{
//write code here
}
}
}
In secondFragment use this code
val navController = findNavController()
navController.previousBackStackEntry?.savedStateHandle?.set("isDelete", "true/false")
navController.popBackStack()
I am wondering if there is a way to add another fragment to the screen without replacing the old one with the Navigation Graph.
I want to do this because the new fragment will just be open for a bit just to check some details and when the user returns to the previous fragment, I want them to be in the same spot they left off.
I have read around but I couldn't find one that does it without replacing the original. I have tried to just add it to the nav_graph_fragment which I try to do this in the newFrag() function in the CurrentFragment section but it doesn't work.
Main Activty
class MainActivity : AppCompatActivity() {
private lateinit var toolbar: Toolbar
private val navController by lazy {
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
}
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
toolbar = findViewById(R.id.mainToolBar)
setSupportActionBar(toolbar)
appBarConfiguration = AppBarConfiguration(navController.graph, null)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.main_menu_bar, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
if (item.itemId == navController.currentDestination?.id)
return true
return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
}
}
Main Activity xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.MainActivity">
<include
layout="#layout/appbar"
android:id="#+id/mainToolBar"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/mainToolBar"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
CurrentFragment
class CurrentFragment : Fragment() {
private lateinit var currentFragmentViewModel: CurrentFragmentViewModel
private lateinit var binding: CurrentFragmentBinding
private lateinit var recyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.current_fragment, container, false)
currentFragmentViewModel = ViewModelProvider(this).get(CurrentFragmentViewModel::class.java)
binding.lifecycleOwner = this
binding.viewmodel = currentFragmentViewModel
binding.accountButton.setOnClickListener {
Log.d("TEST", "button clicked")
newFrag()
}
return binding.root
}
// Here is where I am trying to open the new fragment
private fun newFrag() {
val newFrag = Fragment()
val transaction: FragmentTransaction = parentFragmentManager.beginTransaction()
transaction.hide(this)
transaction.add(R.id.nav_host_fragment, newFrag).commit()
}
}
Nav Graph
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.app.ui.main.MainFragment"
android:label="MainFragment">
<action
android:id="#+id/action_mainFragment_to_currentFragment"
app:destination="#id/currentFragment" />
</fragment>
<fragment
android:id="#+id/currentFragment"
android:name="com.example.app.ui.search.CurrentFragment"
android:label="currentFragment" >
</fragment>
I am fetching data from network and showing in my list. I am using BottomNavigationView in Main Screen. It shows 4 tabs. When i launch the app, data is loading in Home screen but when i go to some other tab in Bottom navigation view and comeback to home tab, Data is not loading. In Home Screen has view pager tabs.
MainActivity.kt
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
lateinit var tabs: TabLayout
lateinit var toolbar: Toolbar
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sharedPreferences = getSharedPreferences(AppConstants.PREF_NAME, Context.MODE_PRIVATE)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
toolbar.setTitle(R.string.app_name)
val navigationView1: BottomNavigationView = findViewById(R.id.nav_view)
navigationView1.setOnNavigationItemSelectedListener(this)
if (savedInstanceState == null) {
loadFragment(MainFragment())
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
var fragment: Fragment? = null
when (item.itemId) {
R.id.news -> {
invalidateOptionsMenu()
fragment = MainFragment()
}
R.id.source -> {
fragment = SourcesFragment()
toolbar.getMenu().clear()
toolbar.setTitle("News Sources")
}
R.id.save -> {
toolbar.setTitle("Saved Articles")
toolbar.getMenu().clear()
fragment = WatchListFragment()
}
R.id.settings -> {
toolbar.setTitle("Settings")
toolbar.getMenu().clear()
fragment = SettingsFragment()
}
}
loadFragment(fragment)
return true
}
private fun loadFragment(fragment: Fragment?) {
val transaction = supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.design_bottom_sheet_slide_in, R.anim.design_bottom_sheet_slide_out)
transaction.replace(R.id.container, fragment!!)
transaction.commit()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.action_search -> {
startActivity(Intent(this, SearchActivity::class.java))
}
else -> ""
}
return true
}
}
MainFragment.kt
class MainFragment : Fragment() {
lateinit var tabs: TabLayout
lateinit var pager: ViewPager
lateinit var adapter: PagerAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var view = inflater.inflate(R.layout.fragment_main, container, false)
pager = view.findViewById(R.id.viewpager)
setupViewPager(pager)
pager.addOnPageChangeListener(onPageChangeListener)
tabs = view.findViewById(R.id.tabs)
tabs.setupWithViewPager(pager)
Log.d("LIVE", "onCreateView")
return view
}
private fun setupViewPager(pager: ViewPager) {
adapter = PagerAdapter(fragmentManager!!)
adapter.addFragment(TopNewsFragment(), "Top News")
adapter.addFragment(TechnologyFragment(), "Technology")
adapter.addFragment(BusinessFragment(), "Business")
adapter.addFragment(SportsFragment(), "Sports")
adapter.addFragment(EntertainmentFragment(), "Entertainment")
adapter.addFragment(ScienceFragment(), "Science")
adapter.addFragment(HealthFragment(), "Health")
pager.adapter = adapter
adapter.notifyDataSetChanged()
}
private val onPageChangeListener = object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
adapter.notifyDataSetChanged()
}
override fun onPageScrollStateChanged(state: Int) {
}
}
}
fragment_main.xml
<FrameLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabs"
style="#style/Widget.MaterialComponents.TabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="10dp"
app:tabGravity="center"
app:tabIndicatorColor="#color/tab_selected_color"
app:tabMode="scrollable"
app:tabSelectedTextColor="#color/tab_selected_color"
app:tabTextColor="#color/textColorPrimary" />
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</LinearLayout>
</FrameLayout>
activity_main.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/AppTheme.AppBarOverlay"
app:elevation="10dp">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:elevation="4dp"
app:itemIconTint="#drawable/bottom_navigation_text_color"
app:itemTextColor="#drawable/bottom_navigation_text_color"
app:labelVisibilityMode="labeled"
app:menu="#menu/bottom_navigation_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Home Screen
DataNotloading
Updated Code:
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
lateinit var tabs: TabLayout
lateinit var toolbar: Toolbar
lateinit var sharedPreferences: SharedPreferences
private val mNewsFragment = MainFragment()
private val mSourceFragment: SourcesFragment = SourcesFragment()
private val mSaveFragment: WatchListFragment = WatchListFragment()
private val mSettingFragment = SettingsFragment()
var activeFragment: Fragment= MainFragment()
val fm: FragmentManager = supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sharedPreferences = getSharedPreferences(AppConstants.PREF_NAME, Context.MODE_PRIVATE)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
toolbar.setTitle(R.string.app_name)
val navigationView1: BottomNavigationView = findViewById(R.id.nav_view)
navigationView1.setOnNavigationItemSelectedListener(this)
fm.beginTransaction().add(R.id.container, activeFragment).commit();
fm.beginTransaction().add(R.id.container, mSettingFragment).hide(mSettingFragment).commit();
fm.beginTransaction().add(R.id.container, mSourceFragment).hide(mSourceFragment).commit();
fm.beginTransaction().add(R.id.container, mSaveFragment).hide(mSaveFragment).commit();
/* if (savedInstanceState == null) {
val transaction = supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.design_bottom_sheet_slide_in, R.anim.design_bottom_sheet_slide_out)
transaction.add(R.id.container, activeFragment).commit();
transaction.add(R.id.container, activeFragment).hide(activeFragment).commit();
transaction.add(R.id.container, activeFragment).hide(activeFragment).commit();
}*/
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
var fragment: Fragment? = null
when (item.itemId) {
R.id.news -> {
invalidateOptionsMenu()
fragment = MainFragment()
toolbar.getMenu().clear()
toolbar.setTitle("News Headlines")
}
R.id.source -> {
fragment = SourcesFragment()
toolbar.getMenu().clear()
toolbar.setTitle("News Sources")
}
R.id.save -> {
toolbar.setTitle("Saved Articles")
toolbar.getMenu().clear()
fragment = WatchListFragment()
}
R.id.settings -> {
toolbar.setTitle("Settings")
toolbar.getMenu().clear()
fragment = SettingsFragment()
}
}
loadFragment(fragment)
return true
}
private fun loadFragment(fragment: Fragment?) {
val transaction = supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.design_bottom_sheet_slide_in, R.anim.design_bottom_sheet_slide_out).hide(activeFragment).show(fragment!!).commit();
activeFragment = fragment;
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.action_search -> {
startActivity(Intent(this, SearchActivity::class.java))
}
else -> ""
}
return true
}
}
You need to Add
pager.setOffscreenPageLimit(2)
After
pager = view.findViewById(R.id.viewpager)
For details
https://developer.android.com/reference/android/support/v4/view/ViewPager#setoffscreenpagelimit
UPDATE
As per new findings, when you swipe between fragments, your fragment state is saved.It is saved because when you swipe, Viewpager comes into action. And when you set pager.setOffscreenPageLimit(2), 2 fragments on either side are saved.Hence you have no issues with swiping.
But when you choose a fragment using BottomNavigationView, you are using replace. replace method removes a fragment from a container so onCreate() will get executed each time when user switches the tabs.
Using the following code for BottomNavigationView, you can solve this issue.
Bottom Line : Instead of Creating/replacing new fragments with BottomNavigationView, you can use hiding.
MainActivity
Declare a fragment variable like that
Fragment activeFragment= new MainFragment();
In onCreate, after setContentView, hide all fragments and commit them to the fragment manager, but do not hide the first fragment that will serve as home fragment.
fm.beginTransaction().add(R.id.main_container,fragment1).commit();
fm.beginTransaction().add(R.id.main_container, fragment2).hide(fragment2).commit();
fm.beginTransaction().add(R.id.main_container, fragment3).hide(fragment3).commit();
Replace your loadFragment() like that.
private fun loadFragment(fragment: Fragment?) {
val transaction = supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.design_bottom_sheet_slide_in, R.anim.design_bottom_sheet_slide_out).hide(activeFragment).show(fragment).commit();
transaction.commit()
activeFragment=fragment;
}
2ND UPDATE
Replace
fm.beginTransaction().add(R.id.main_container,fragment1).commit();
With
fm.beginTransaction().add(R.id.main_container,activeFragment).commit();
I've been trying to combine a bottom navigation view, a tablayout and a viewpager. I already have the tableyout working with the viewpager, but when selecting another item (different from the one containing the tableyout) from the bottom navigation view nothing happens and it seems that they were disabled. I do not know what I'm doing wrong. I am a novice on Android.
I want something like this:
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_data_sheet -> {
replaceFragment(DataSheetFragment())
return#OnNavigationItemSelectedListener true
}
R.id.navigation_search -> {
replaceFragment(SearchFragment())
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
replaceFragment(NotificationsFragment())
return#OnNavigationItemSelectedListener true
}
R.id.navigation_profile -> {
val intent = Intent(this#MainActivity, NoLoginActivity::class.java)
startActivity(intent)
finish()
return#OnNavigationItemSelectedListener true
}
}
false
}
private fun replaceFragment(fragment: Fragment) {
val fragmentTransition = supportFragmentManager.beginTransaction()
fragmentTransition.replace(R.id.fragmentContainer, fragment)
fragmentTransition.commit()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
replaceFragment(DataSheetFragment())
val navView: BottomNavigationView = findViewById(R.id.bottom_navigation)
navView.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)
}}
PagerAdapter.kt
class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> {
SpecimensFragment()
}
else -> {
return FungiFragment()
}
}
}
override fun getCount(): Int {
return 2
}
override fun getPageTitle(position: Int): CharSequence {
return when (position) {
0 -> "Especímenes"
else -> {
return "Hongos"
}
}
}}
DataSheetFragment.kt: Fragment que contiene el tablayout
class DataSheetFragment : Fragment() {
private lateinit var viewPager: ViewPager
private lateinit var tabs: TabLayout
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater.inflate(R.layout.fragment_data_sheet, container, false)
viewPager = view.findViewById(R.id.viewPager)
tabs = view.findViewById(R.id.data_sheet_tabs)
val fragmentAdapter = PagerAdapter(childFragmentManager)
viewPager.adapter = fragmentAdapter
tabs.setupWithViewPager(viewPager)
return view
}}
fragment_data_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".Fragments.DataSheetFragment"
>
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:title="#string/data_sheet"
app:titleTextColor="#color/white"
android:background="#color/colorPrimary"
>
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="#+id/data_sheet_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/toolbar"
android:background="#color/background_color"
app:tabTextAppearance="#style/TabLayoutTextAppearance"
>
<com.google.android.material.tabs.TabItem
android:id="#+id/specimens_tab"
android:text="#string/specimens"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<com.google.android.material.tabs.TabItem
android:id="#+id/fungi_tab"
android:text="#string/fungi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/data_sheet_tabs"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
I might be missing something very simple here, but I could definitely use some help.
I have an activity inside which I want to load different fragments with different content and different toolbar style (i.e. Toolbars with custom View and CollapsingToolbarLayout(that too having custom layout inside in it)). At the same time, I want to have one instance of the drawer, accessible through the single activity(i.e. MainActivity).
I have gone through the examples to find something similar, but all of them use new activities in order to populate their content or they just hide/show toolbar content that is not appropriate in my case as I've custom Toolbar views for each Fragment, therefore I cannot find out what should be the order of the layout views. Some of the solution that I've gone through are like :
Collapsing Toolbar only for one Fragment in Navigation View
Coordinator Layout with Toolbar in Fragments or Activity
Collapsing Toolbar and DrawerLayout
TL;DR
For one Fragment I want Toolbar as below
And for other Fragment
EDIT:
This is not a clean code but it is a working one. I just want to show you. You can use something like BaseFragment and structure a better code. In MainFragment you can use navigation drawer and in DetailFragment you can use back arrow on toolbar. If you want you can use navigation view in DetailFragment too. Also you can use collapsing toolbar in any fragment which you prefer.
MainActivity
class MainActivity : AppCompatActivity() {
private var actionBarToggle : ActionBarDrawerToggle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpNavigationList()
if (savedInstanceState == null){
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_container, MainFragment())
.addToBackStack(null)
.commit()
}
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item!!.itemId){
android.R.id.home -> onBackPressed()
}
return true
}
fun setActionBarDrawerToggle(toolbar: Toolbar){
actionBarToggle = ActionBarDrawerToggle(this, drawer_layout, toolbar, R.string.drawer_open, R.string.drawer_close)
actionBarToggle?.syncState()
drawer_layout.closeDrawer(Gravity.START)
}
fun lockDrawer(lock: Boolean){
val lockMode = if (lock) DrawerLayout.LOCK_MODE_LOCKED_CLOSED else DrawerLayout.LOCK_MODE_UNLOCKED
drawer_layout.setDrawerLockMode(lockMode)
actionBarToggle?.isDrawerIndicatorEnabled = lock
}
private fun setUpNavigationList(){
val items = arrayListOf("Detail Fragment")
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
list_navigation.adapter = adapter
list_navigation.setOnItemClickListener { parent, view, position, id ->
when (position){
0 -> {
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_container, DetailFragment())
.addToBackStack(null)
.commit()
}
}
drawer_layout.closeDrawer(Gravity.START)
}
}
}
MainFragment
class MainFragment() : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.fragment_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as AppCompatActivity).setSupportActionBar(toolbar_detail)
(activity as MainActivity).lockDrawer(false)
(activity as MainActivity).setActionBarDrawerToggle(toolbar_main)
}
}
DetailFragment
class DetailFragment() : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.fragment_detail, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as MainActivity).setSupportActionBar(toolbar_detail)
(activity as MainActivity).lockDrawer(true)
(activity as MainActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(true)
}
}
Main Activity Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.coskun.drawer.MainActivity">
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/list_navigation"
android:layout_width="150dp"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>
MainFragment Layout
<LinearLayout 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:orientation="vertical"
tools:context="com.coskun.drawer.DetailFragment">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#color/colorPrimary"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/main_fragment"/>
</LinearLayout>
DetailFragment Layout
<LinearLayout 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:orientation="vertical"
tools:context="com.coskun.drawer.DetailFragment">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_detail"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#color/colorAccent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/detail_fragment"/>
</LinearLayout>
PREVIOUS ANSWER:
You can define navigation drawer logic in activity and you can use separate toolbars for each fragment. You just need to sync state navigation drawer state with fragment toolbars.
fun setActionBarDrawerToggle(toolbar: Toolbar){
actionBarToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close)
actionBarToggle?.syncState()
drawerLayout.closeDrawer(Gravity.START)
}
fun lockDrawer(lock: Boolean){
val lockMode = if (lock) DrawerLayout.LOCK_MODE_LOCKED_CLOSED else DrawerLayout.LOCK_MODE_UNLOCKED
drawerLayout.setDrawerLockMode(lockMode)
actionBarToggle?.isDrawerIndicatorEnabled = lock
}
You can take a look at this code. I set toolbar as action bar in every fragment and let them to inflate their own menu and sync with activities drawer state. Let me know if this helps.