I have a BottomNavigationView which hides when the view is getting scrolled to the bottom and returns when it's scrolled to the top.
My issue is when the BottomNavigationView is hidden and I change
the fragment I am unable to show the BottomNavigationView in the
screen any help is appreciable.
Below is my code of DashboardActivity.kt
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fitsSystemWindows="true"
app:elevation="2dp">
<include layout="#layout/toolbar"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:layout_gravity="bottom"
app:labelVisibilityMode="selected"
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
app:itemIconTint="#android:color/white"
app:itemTextColor="#android:color/white"
android:background="#color/colorAccent"
app:itemBackground="#color/colorAccent"
app:menu="#menu/navigation"/>
Any help is appreciable
Thanks in advance
Edit #1
DashboardActivity.kt
class DashboardActivity : BaseActivity(), EditProfileFragment.RefreshView, AddPatientFragment.NavigateView {
override fun navigateView() {
toDashboard()
}
override fun refreshView() {
val f: Fragment? = supportFragmentManager.findFragmentById(R.id.frame_container)
if (f != null && f is ProfileFragment) {
f.refreshView()
supportActionBar?.title = PROFILE
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
initializeToolBar()
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
toDashboard()
}
/**
* Initialization of ToolBar
*/
private fun initializeToolBar() {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
if (supportActionBar != null) {
supportActionBar!!.setDisplayShowHomeEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
}
}
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
toDashboard()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_medicine -> {
toMedicines()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_add_patient -> {
toAddPatient()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_profile -> {
toProfile()
return#OnNavigationItemSelectedListener true
}
}
false
}
private fun toProfile() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_profile
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(ProfileFragment::class.java, PROFILE, false, null, R.id.frame_container)
}
private fun toAddPatient() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_add_patient
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(AddPatientFragment::class.java, ADD_PATIENT, false, null, R.id.frame_container)
}
private fun toMedicines() {
toggleToolBar(false)
// navigation.selectedItemId = R.id.navigation_medicine
navigation.labelVisibilityMode = View.VISIBLE
replaceFragment(MedicineFragment::class.java, MEDICINE, false, null, R.id.frame_container)
}
private fun toDashboard() {
toggleToolBar(false)
navigation.labelVisibilityMode = View.VISIBLE
// navigation.selectedItemId = R.id.navigation_home
replaceFragment(HomeFragment::class.java, HOME, false, null, R.id.frame_container)
}
private fun toggleToolBar(value: Boolean) {
supportActionBar?.setDisplayHomeAsUpEnabled(value)
supportActionBar?.setDisplayShowHomeEnabled(value)
}
}
Update to latest Support Library Version 28.0.0-alpha1 and just add one attribute to BottomNavigationView.
<BottomNavigationView
....
....
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"/>
Note :- see here Hide/Show bottomNavigationView on Scroll
Related
this is MainActivity
class MainActivity : AppCompatActivity(), FragmentDrawerListener,
BottomNavigationView.OnNavigationItemSelectedListener {
private lateinit var drawerFragment: DrawerFragment
#Inject
lateinit var mainActivityViewModel: MainActivityViewModel
#Inject
lateinit var appPreference: AppPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
navigation.setOnNavigationItemSelectedListener(this)
displayView()
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onDrawerItemSelected(view: View, position: Int) {
displayView(position)
}
private fun displayView() {
drawerFragment =
supportFragmentManager.findFragmentById(R.id.fragment_navigation_drawer) as DrawerFragment
drawerFragment.init(R.id.fragment_navigation_drawer, drawer_layout, toolbar)
displayView(0)
}
private fun displayView(position: Int) {
when (position) {
0 -> {
HomeFragment()
}
5 -> {
val intent = Intent(this, MerchantOnBoardingActivity::class.java)
startActivity(intent)
}
9 -> {
val intent = Intent(this, AppSecurityActivity::class.java)
startActivity(intent)
}
else -> {
val bundle = Bundle()
bundle.putInt("menuItemPosition", position - 1)
val intent = Intent(this, MenuActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)
}
}
}
private fun showDefaultPage() {
val fragment = HomeFragment()
title = "Home"
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.main_content, fragment)
fragmentTransaction.commit()
supportActionBar?.title = title
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
var fragment: Fragment? = null
when (item.itemId) {
R.id.navigation_home -> fragment = HomeFragment()
R.id.navigation_monitor -> Toast.makeText(this, "Monitor", Toast.LENGTH_LONG).show()
R.id.navigation_profile -> Toast.makeText(this, "Profile", Toast.LENGTH_LONG).show()
}
return loadFragment(fragment)
}
private fun loadFragment(fragment: Fragment?): Boolean {
//switching fragment
if (fragment != null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
return true
}
return false
}
}
This is my xml :
<?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:local="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:background="#color/white"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
local:popupTheme="#style/ThemeOverlay.AppCompat.Light"
local:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<FrameLayout
android:id="#+id/main_content"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemIconTint="#color/design_default_color_primary"
app:itemTextColor="#color/design_default_color_primary"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/navigation" />
</LinearLayout>
<fragment
android:id="#+id/fragment_navigation_drawer"
android:name="com.egpaid.employeeapp.home.homedashboard.DrawerFragment"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="start"
app:layout="#layout/fragment_drawer"
tools:layout="#layout/fragment_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
</RelativeLayout>
using this code i am able to display data but when i try to click on Home button on either from bottom navigation view or from drawber then i am getting
No view found for id 0x7f0900a3 (com.egpaid.employeeapp:id/fragment_container) for fragment HomeFragment{9d6bf5} (7b531975-d1d4-4ad4-b3c7-6f43ae436c6d) id=0x7f0900a3}
I don't know what i am missing i am trying to create custom navigation drawber with bottom navigation view please help me Thanks
I created a class called BaseActivity that all of the activities inherit from so that I can add the drawer layout in all of my activities.
The Drawer Toggle button is shown in the AppBar, but when I click on it nothing happens!
Here is the code for the BaseActivity.kt:
open class BaseActivity : AppCompatActivity() {
private var dl: DrawerLayout? = null
private var t: ActionBarDrawerToggle? = null
private var nv: NavigationView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.base_activity)
dl = findViewById(R.id.drawer_layout)
t = ActionBarDrawerToggle(this, dl, R.string.drawer_open, R.string.drawer_close)
supportActionBar?.setDisplayShowTitleEnabled(true);
supportActionBar?.setHomeButtonEnabled(true);
supportActionBar?.setDisplayHomeAsUpEnabled(true);
dl?.addDrawerListener(t!!)
t?.syncState()
nv = findViewById(R.id.navigation_view)
nv?.setNavigationItemSelectedListener(NavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_note -> Toast.makeText(this#BaseActivity, "My Account", Toast.LENGTH_SHORT).show()
R.id.nav_calendar -> Toast.makeText(this#BaseActivity, "Settings", Toast.LENGTH_SHORT).show()
R.id.nav_trash -> Toast.makeText(this#BaseActivity, "Trash", Toast.LENGTH_SHORT).show()
else -> return#OnNavigationItemSelectedListener true
}
true
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (t?.onOptionsItemSelected(item) == true) {
true
} else super.onOptionsItemSelected(item!!)
}
}
and here the base_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BaseActivity">
<!--include layout="#layout/toolbar"/-->
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/drawer_menu"
app:headerLayout="#layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>
For the style I left it at DarkActionBar. Here is how it looks like Don't mind what's written there
I still cannot figure out what's wrong and why it doesn't work. I appreciate any suggestions from the community. Thank you.
So I kind of changed my code to this and it is working Here is the new code for BaseActivty class
open class BaseActivity : AppCompatActivity() {
//var toolbar: Toolbar? = null
var drawerLayout: DrawerLayout? = null
var drawerToggle: ActionBarDrawerToggle? = null
var navigationView: NavigationView? = null
var mContext: Context? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mContext = this#BaseActivity
setContentView(R.layout.base_activity)
}
override fun setContentView(layoutResID: Int) {
val fullView = layoutInflater.inflate(R.layout.base_activity, null) as DrawerLayout
val activityContainer = fullView.findViewById<View>(R.id.activity_content) as FrameLayout
layoutInflater.inflate(layoutResID, activityContainer, true)
super.setContentView(fullView)
}
private fun setUpNav() {
drawerLayout = findViewById<View>(R.id.drawer_layout) as DrawerLayout
drawerToggle = ActionBarDrawerToggle(
this#BaseActivity,
drawerLayout,
R.string.app_name,
R.string.app_name
)
drawerLayout!!.setDrawerListener(drawerToggle)
supportActionBar!!.setHomeButtonEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
navigationView = findViewById<View>(R.id.navigation_view) as NavigationView
// Setting Navigation View Item Selected Listener to handle the item
// click of the navigation menu
navigationView!!.setNavigationItemSelectedListener(NavigationView.OnNavigationItemSelectedListener { menuItem -> // Checking if the item is in checked state or not, if not make
// it in checked state
if (menuItem.isChecked) menuItem.isChecked = false else menuItem.isChecked = true
// Closing drawer on item click
drawerLayout!!.closeDrawers()
// Check to see which item was being clicked and perform
// appropriate action
val intent_calendar = Intent(this, CalendarActivity::class.java)
val intent_add_note = Intent(this, AddActivity::class.java)
val intent_note = Intent(this, MainActivity::class.java)
when (menuItem.itemId) {
R.id.nav_note -> this.startActivity(intent_note)
R.id.nav_calendar -> this.startActivity(intent_calendar)
R.id.nav_trash -> Toast.makeText(this, "Trash", Toast.LENGTH_SHORT).show()
R.id.nav_add_note -> this.startActivity(intent_add_note)
else -> return#OnNavigationItemSelectedListener true
}
false
})
// calling sync state is necessay or else your hamburger icon wont show
// up
drawerToggle!!.syncState()
}
public override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
setUpNav()
drawerToggle!!.syncState()
}
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig != null) {
super.onConfigurationChanged(newConfig)
}
drawerToggle!!.onConfigurationChanged(newConfig)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (drawerToggle?.onOptionsItemSelected(item) == true) {
true
} else super.onOptionsItemSelected(item!!)
}
}
and here is the code for its xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BaseActivity"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/activity_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="#menu/drawer_menu"
app:headerLayout="#layout/nav_header"/>
</androidx.drawerlayout.widget.DrawerLayout>
Here is the answer that helped me, (I didn't use a toolbar and i left the style to "Theme.AppCompat.Light.DarkActionBar") https://stackoverflow.com/a/42533759/15018682
I have a single activity with many fragments (Using jetpack navigation). On my first fragment, i have a recyclerview. If i scroll on the first fragment and then navigate to the other fragment, the fragment retains the scroll position and i don't want that. An example is as follows:
i.e. Suppose i have two fragments A and B, When my app starts it starts on A. Suppose i start scrolling on A and then navigate to B. My app retains the scroll position on B which is not what i want. I want fragment B to start on top. And then when it returns to fragment A, i want it to retain the scroll position it previously scrolled.
Fragment A.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import type="android.view.View" />
<variable
name="ViewModel"
type="....AccountViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Fragment_Account"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
Recyclerview
-->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView_Account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.accountListVisibility? View.VISIBLE : View.GONE}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<!--
Empty Views and group
-->
<androidx.constraintlayout.widget.Group
android:id="#+id/Empty_View"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.accountEmptyViewVisibility?
View.VISIBLE : View.GONE}"
app:constraint_referenced_ids="Empty_View_Illustration,Empty_View_Title,Empty_View_Subtitle" />
<ImageView
android:id="#+id/Empty_View_Illustration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#+id/Empty_View_Title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="#drawable/il_account" />
<TextView
android:id="#+id/Empty_View_Title"
style="#style/Locky.Text.Title6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="#string/text_title_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="#id/Empty_View_Subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Empty_View_Illustration"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<TextView
android:id="#+id/Empty_View_Subtitle"
style="#style/Locky.Text.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="?attr/actionBarSize"
android:text="#string/text_subtitle_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Empty_View_Title"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<!--
Progress Bar
-->
<include
android:id="#+id/Progress_Bar"
layout="#layout/custom_view_list_loading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.loadingStatus? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment A.kt:
class AccountFragment : Fragment() {
private var _binding: FragmentAccountBinding? = null
private var _viewModel: AccountViewModel? = null
private var _lastClickTime: Long = 0
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
companion object {
const val TAG = "ACCOUNT_FRAGMENT_DEBUG"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentAccountBinding.inflate(inflater, container, false)
// Fetch view model
_viewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
//Bind view model to layout
binding.viewModel = _viewModel
// Bind lifecycle owner
binding.lifecycleOwner = this
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
/* Observe snack bar events */
observeSnackBarEvent()
/* Observe the account list changes */
observeAccounts()
/* Observe back stack entry result after navigating from sort sheet */
observeBackStackEntryForSortSheet()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_toolbar_filter, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.Toolbar_Filter -> {
navigateToSort()
true
}
else -> false
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
/*
* My Functions
*/
private fun observeBackStackEntryForSortSheet() {
val navController = findNavController()
// After a configuration change or process death, the currentBackStackEntry
// points to the dialog destination, so you must use getBackStackEntry()
// with the specific ID of your destination to ensure we always
// get the right NavBackStackEntry
val navBackStackEntry = navController.getBackStackEntry(R.id.Fragment_Account)
// Create our observer and add it to the NavBackStackEntry's lifecycle
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME
&& navBackStackEntry.savedStateHandle.contains(KEY_ACCOUNTS_SORT)
) {
viewModel.sortChange(
navBackStackEntry.savedStateHandle.get<AccountSort>(
KEY_ACCOUNTS_SORT
)!!
)
navBackStackEntry.savedStateHandle.remove<AccountSort>(KEY_ACCOUNTS_SORT)
}
}
navBackStackEntry.lifecycle.addObserver(observer)
// As addObserver() does not automatically remove the observer, we
// call removeObserver() manually when the view lifecycle is destroyed
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
navBackStackEntry.lifecycle.removeObserver(observer)
}
})
}
private fun observeSnackBarEvent() {
viewModel.showSnackBarEvent.observe(viewLifecycleOwner, Observer {
if (it != null) {
snackBarAction(it)
}
})
}
private fun observeAccounts() {
with(viewModel) {
accounts.observe(viewLifecycleOwner, Observer {
if (it != null) {
//set loading flag to hide loading animation
doneLoading()
//Alternate visibility for account list and empty view
alternateAccountListVisibility(it.size)
//Submit the cards
initiateAccounts().submitList(it)
}
})
}
}
private fun initiateAccounts(): AccountAdapter {
val adapter = AccountAdapter(
AccountClickListener {
navigateToSelectedAccount(it)
},
AccountOptionsClickListener { view, card ->
view.apply {
isEnabled = false
}
createPopupMenu(view, card)
})
binding.RecyclerViewAccount.apply {
this.adapter = adapter
setHasFixedSize(true)
}
return adapter
}
private fun createPopupMenu(view: View, account: Account) {
requireContext().createPopUpMenu(
view,
R.menu.menu_moreoptions_account,
PopupMenu.OnMenuItemClickListener {
when (it.itemId) {
R.id.Menu_CopyUsername -> copyToClipboardAndToast(account.username)
R.id.Menu_CopyPass -> copyToClipboardAndToast(account.password)
R.id.Menu_ShowPass -> triggerSnackBarEvent(account.password)
else -> false
}
}, PopupMenu.OnDismissListener {
view.apply {
isEnabled = true
}
})
}
private fun navigateToSort() {
if (SystemClock.elapsedRealtime() - _lastClickTime >= 800) {
_lastClickTime = SystemClock.elapsedRealtime()
navigateTo(AccountFragmentDirections.actionFragmentAccountToBottomSheetFragmentAccountFilter())
}
}
private fun navigateToSelectedAccount(account: Account) {
navigateTo(
AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(
account
)
)
}
private fun snackBarAction(message: String) {
binding.LayoutFragmentAccount.snackbar(message) {
action(getString(R.string.button_snack_action_close)) { dismiss() }
}
viewModel.doneShowingSnackBar()
}
private fun triggerSnackBarEvent(message: String): Boolean {
viewModel.setSnackBarMessage(message)
return true
}
private fun copyToClipboardAndToast(message: String): Boolean {
copyToClipboard(message)
toast(getString(R.string.message_copy_successful))
return true
}
Fragment B.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="Account"
type="....Account" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Credential_View"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/Account_Logo"
imageUrl="#{Account.logoUrl}"
loadingResource="#{#drawable/ic_image_loading}"
errorResource="#{#drawable/ic_account_placeholder}"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="#drawable/ic_account_placeholder" />
<TextView
android:id="#+id/Account_Name"
style="#style/Locky.Text.Title5.Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:textAlignment="center"
android:text="#{Account.accountName}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Account_Logo"
tools:text="This can be a very very very long title toooooo" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView_Credentials_Field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="8dp"
android:nestedScrollingEnabled="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Account_Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment B.kt
class ViewAccountFragment : Fragment() {
private var _binding: FragmentViewAccountBinding? = null
private var _viewModel: ViewAccountViewModel? = null
private lateinit var _account: Account
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//Fetch the layout and do the binding
_binding = FragmentViewAccountBinding.inflate(inflater, container, false)
//Instantiate view model
_viewModel = ViewModelProvider(this).get(ViewAccountViewModel::class.java)
binding.lifecycleOwner = this
//Fetch the account clicked on the previous screen
_account = ViewAccountFragmentArgs.fromBundle(requireArguments()).parcelcredaccount
with(_account) {
//Bind the account to the layout for displaying
binding.account = this
//Submit the account details to the recyclerview
initiateCredentialsFieldList().submitList(viewModel.fieldList(this))
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_credentials_actions, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.Action_Duplicate -> {
/*
* We set the account id to empty here
* When the add screen receives it, it wil perceive it as a new account that needs to be
* added to the database
*/
navigateToEditScreen(_account.copy(accountID = generateUniqueID()))
true
}
R.id.Action_Edit -> {
navigateToEditScreen(_account)
true
}
R.id.Action_Delete -> {
deleteConfirmationDialog(_account.accountName)
true
}
else -> false
}
}
private fun initiateCredentialsFieldList(): CredentialsViewAdapter {
val credentialsAdapter =
CredentialsViewAdapter(
CopyClickListener { data ->
copyToClipboardAndToast(data)
},
ViewClickListener { data ->
snackBarAction(data)
})
binding.RecyclerViewCredentialsField.apply {
adapter = credentialsAdapter
setHasFixedSize(true)
}
return credentialsAdapter
}
private fun deleteAndNavigateBackToAccountList() {
with(_account) {
viewModel.delete(accountID)
toast(getString(R.string.message_credentials_deleted, accountName))
findNavController().popBackStack()
}
}
navigation.xml
<fragment
android:id="#+id/Fragment_Account"
android:name="....AccountFragment"
android:label="Accounts"
tools:layout="#layout/fragment_account">
<action
android:id="#+id/action_Fragment_Account_to_Fragment_View_Account"
app:destination="#id/Fragment_View_Account" />
<action
android:id="#+id/action_Fragment_Account_to_BottomSheet_Fragment_Account_Filter"
app:destination="#id/BottomSheet_Fragment_Account_Filter" />
</fragment>
MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/Drawer_Main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.main.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/Layout_Coordinator_Main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/Toolbar_Main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorOnSurface"
android:outlineAmbientShadowColor="#color/colorShadowColor"
android:outlineSpotShadowColor="#color/colorShadowColor">
<TextView
android:id="#+id/Toolbar_Main_Title"
style="#style/Locky.Text.Title6.Toolbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/app_name" />
</com.google.android.material.appbar.MaterialToolbar>
<androidx.core.widget.NestedScrollView
android:id="#+id/Nested_Scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true">
<fragment
android:id="#+id/Navigation_Host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/navigation_drawer_main" />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/FAB_Search"
style="#style/Locky.FloatingActionButton.Mini"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="85dp"
app:layout_anchor="#id/FAB_Add"
app:layout_anchorGravity="top|center_horizontal"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="#drawable/ic_search" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/FAB_Add"
style="#style/Locky.FloatingActionButton.Normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="#drawable/ic_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/Navigation_View"
style="#style/Locky.Widget.Custom.NavigationView"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:clipToPadding="false"
android:paddingStart="0dp"
android:paddingEnd="16dp"
app:headerLayout="#layout/drawer_header"
app:itemTextAppearance="#style/Locky.Text.Body.Drawer"
app:menu="#menu/menu_drawer_main" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var _binding: ActivityMainBinding
private lateinit var _viewModel: MainActivityViewModel
private lateinit var _appBarConfiguration: AppBarConfiguration
//Fragments that can navigate with the drawer
private val _navigationFragments = setOf(
R.id.Fragment_Card,
R.id.Fragment_Account,
R.id.Fragment_Device
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
_viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
_binding.lifecycleOwner = this
//Set the support action bar to the toolbar
setSupportActionBar(_binding.ToolbarMain)
//Remove the default actionbar title
supportActionBar?.setDisplayShowTitleEnabled(false)
/* Updates the app settings*/
updateAppSettings()
//Setup the navigation components
navigationUISetup()
//Load FABs
listenerForAddFab()
listenerForSearchFab()
//Scroll changes to adjust toolbar elevation accordingly
setUpNestedScrollChangeListener()
}
override fun onOptionsItemSelected(item: MenuItem) =
item.onNavDestinationSelected(findNavController(R.id.Navigation_Host)) || super.onOptionsItemSelected(
item
)
override fun onSupportNavigateUp() =
findNavController(R.id.Navigation_Host).navigateUp(_appBarConfiguration)
override fun finish() {
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
}
private fun navigationUISetup() {
//Fetch the Nav Controller
val navController = findNavController(R.id.Navigation_Host)
//Setup the App Bar Configuration
_appBarConfiguration = AppBarConfiguration(_navigationFragments, _binding.DrawerMain)
//Use Navigation UI to setup the app bar config and navigation view
NavigationUI.setupActionBarWithNavController(this, navController, _appBarConfiguration)
NavigationUI.setupWithNavController(_binding.NavigationView, navController)
//Set the mini FABs with navigation to navigate to fragments accordingly.
Navigation.setViewNavController(_binding.FABAdd, navController)
Navigation.setViewNavController(_binding.FABSearch, navController)
//Add on change destination listener to navigation controller to handle fab visibility
navigationDestinationChangeListener_FAB(navController)
//Add on change destination listener to navigation controller to handle screen title visibility
navigationDestinationChangeListener_ToolbarTitle(navController)
}
private fun setUpNestedScrollChangeListener() =
_binding.NestedScroll.setOnScrollChangeListener { _, _, scrollY, _, _ ->
if (scrollY > 0) {
_binding.ToolbarMain.elevation = 12F
} else {
_binding.ToolbarMain.elevation = 0F
}
}
private fun navigationDestinationChangeListener_ToolbarTitle(navController: NavController) {
navController.addOnDestinationChangedListener { _, nd, _ ->
when (nd.id) {
R.id.Fragment_Account -> updateToolbar(getString(R.string.text_title_screen_accounts))
R.id.Fragment_Card -> updateToolbar(getString(R.string.text_title_screen_cards))
R.id.Fragment_Device -> updateToolbar(getString(R.string.text_title_screen_devices))
R.id.Fragment_Settings -> updateToolbar(getString(R.string.text_title_screen_settings))
R.id.Fragment_Profile -> updateToolbar(getString(R.string.text_title_screen_profile))
R.id.Fragment_About -> updateToolbar(getString(R.string.text_title_screen_about))
R.id.Fragment_Donate -> updateToolbar(getString(R.string.text_title_screen_donate))
else -> {
//Show the toolbar
updateToolbar(null)
}
}
}
}
private fun navigationDestinationChangeListener_FAB(navController: NavController) {
navController.addOnDestinationChangedListener { nc, nd, _ ->
when (nd.id) {
nc.graph.startDestination,
R.id.Fragment_Card,
R.id.Fragment_Device -> {
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
//Show all the FABs
showFABs()
}
else -> {
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
//Hide all the FABs
hideFABs()
}
}
}
}
private fun getFadeNavOptions(): NavOptions? {
return NavOptions.Builder()
.setEnterAnim(R.anim.anim_fade_in)
.setExitAnim(R.anim.anim_fade_out)
.build()
}
private fun hideFABs() {
_binding.FABSearch.hide()
_binding.FABAdd.hide()
}
private fun showFABs() {
_binding.FABSearch.show()
_binding.FABAdd.show()
showFABFromSlidingBehavior(_binding.FABSearch, _binding.FABSearch.isVisible)
showFABFromSlidingBehavior(_binding.FABAdd, _binding.FABAdd.isVisible)
}
private fun showFABFromSlidingBehavior(fab: FloatingActionButton, isVisible: Boolean) {
val layoutParams: ViewGroup.LayoutParams = fab.layoutParams
if (layoutParams is CoordinatorLayout.LayoutParams) {
val behavior = layoutParams.behavior
if (behavior is HideBottomViewOnScrollBehavior) {
if (isVisible) {
behavior.slideUp(fab)
} else {
behavior.slideDown(fab)
}
}
}
}
I have attached a gif to demontstrate the issue here:
In the GIF i navigate from 3 fragments (Fragment A > Fragment B > Fragment C)
Is there anything i am doing wrong here ?
you have the same layoutmanager for both fragments, when you populate your different fragments; the same layoutmanager is called. Which then tries to restore the same position thinking its the same recyclerview, which is kind of a feature when you think about it.
from the docs:
Called when the RecyclerView is ready to restore the state based on a
previous RecyclerView. Notice that this might happen after an actual
layout, based on how Adapter prefers to restore State. See
RecyclerView.Adapter.getStateRestorationPolicy()
which means what we need is not to restore the state which can be done by passing
PREVENT to RecyclerView.Adapter.StateRestorationPolicy
solution1: in your fragment B adapter just call adapter.stateRestorationPolicy = PREVENT
solution2: create a different layoutmanager for fragment B in case you want to restore position for something else.
EDIT :: QA :: how can i set the view to be on top (Near Status Bar) :
Well, since you are populating your fragments inside a NestedScrollView you should call NestedScrollView.scrollTo(0, 0); when you navigate to the required fragment probably by waiting on a callback from addOnDestinationChangedListener inside your MainActivity.kt
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>