I have a simple application written in Kotlin that has a BottomNavigationView. The idea is to have a single activity (MainActivity) with a fragment to be loaded for each tab on the BottomNavigationView.
I have already created a fragment I would like to load when a tab is selected (HomeFragment) and am already changing the text under the icons on the BottomNavigationView when the active tab changes.
Now I would like to inflate / load the fragment when I change tabs. How would I go about doing this?
MainActivity.kt:
class MainActivity : AppCompatActivity() {
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_enrollments -> {
message.setText(R.string.title_enrollments)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_timeline -> {
message.setText(R.string.title_timeline)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_home -> {
message.setText(R.string.title_home)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_alerts -> {
message.setText(R.string.title_alerts)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_profile -> {
message.setText(R.string.title_profile)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
override fun onResume() {
super.onResume()
setContentView(R.layout.activity_main)
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.navigation)
bottomNavigationView.selectedItemId = R.id.navigation_home
}
}
Instead of message.setText(R.string.title_enrollments) do
supportFragmentManager.beginTransaction()
.replace(containerViewId, fragmentInstance, "TAG")
.commitAllowingStateLoss()
Or you can use an extension function I use in my code to make it cleaner. Just add this in some .kt file
inline fun FragmentManager.transactStateless(func: FragmentTransaction.() -> Unit) {
val transaction = beginTransaction()
transaction.func()
transaction.commitAllowingStateLoss()
}
and now you can add remove fragment this way:
supportFragmentManager.transactStateless {
replace(containerViewId, fragmentInstance, "TAG")
}
Related
My application consists of 3 main screens (fragments that switch between themselves bottom navigation).
Launching the app opens the Home screen (shown in code)
The question is the following. I want to add a welcome snippet that will run the very first one, where I'll make an animation and write something like "Hi, I'm an application!".
How can I make this fragment run first, and most importantly, how can I set it a time, for example 3 seconds, after which the Home fragment will start (that is, it will go to the fragment that I have right now when I start the application)?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
replaceFragment(HomeFragment())
binding.bottomNavigationView.setOnItemSelectedListener { item ->
when(item.itemId) {
R.id.home -> {
replaceFragment(HomeFragment())
true
}
R.id.profile -> {
replaceFragment(ProfileFragment())
true
}
R.id.settings -> {
replaceFragment(SettingsFragment())
true
}
else -> false
}
}
}
private fun replaceFragment(fragment: Fragment){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frame_layout, fragment)
transaction.commit()
}
}
Use this code change time (3000, 1000) according to your requeirment
var countDownTimer = object : CountDownTimer(30000, 1000) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
//Replace fragment method here
}
}.start()
I am new to android and Kotlin, developing a bottom navigation bar using onitemselectedListener, since setOnNavigationItemSelectedListener is deprecated and I couldn't find any youtube tutorial that explains how to used onitemselectedlistener for navigationbar. navigation shows up on the emulator, but fragments are not showing up when i click on navigation Icons. here are my codes.
adding image of activity_main and emulator error image
fragmentWord image, that is connected to the first icon of "A" but doesn't show up
MainActivity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.aryanvedh.vocabapp2.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val wordFragment = WordFragment()
val memorisedFragment = MemorisedFragment()
setCurrentFragment(wordFragment)
binding.bottomNavigationView.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.words -> setCurrentFragment(wordFragment)
R.id.memorised -> setCurrentFragment(memorisedFragment)
}
true
}
}
private fun setCurrentFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFragment, fragment)
commit()
}
}```
any help? thanks
Use the function like this:
val navBar = findViewById<NavigationBarView>(R.id.navBar)
navBar.setOnItemSelectedListener {
when(it.itemId) {
R.id.item01 -> {
setCurrentFragment(Fragment01())
true
}
R.id.item02 -> {
setCurrentFragment(Fragment02())
true
}
R.id.item03 -> {
setCurrentFragment(Fragment03())
true
}
else -> {
false
}
}
}
It now should raise the fragments when each navigation bar item is selected.
You need to use the OnNavigationItemSelectedListener Method to capture once the item is selected in Bottom Navigation.
Here is the sample code attached from Documentation.
BottomNavigationView.OnNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.item1 -> {
// Respond to navigation item 1 click
true
}
R.id.item2 -> {
// Respond to navigation item 2 click
true
}
else -> false
}
}
Please refer to the Official Documentation for more information about BottomNavigationView.
I try to put a BottomNavigationView like this in my main Activity, and I have a recycler view too :
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadFragment(HomeFragment(this))
//import the bottomNavigationView
val navigationView = findViewById<BottomNavigationView>(R.id.barre_nav)
navigationView.setOnNavigationItemSelectedListener {
when(it.itemId) {
R.id.nav_home -> {
loadFragment(HomeFragment(this))
return#setOnNavigationItemSelectedListener true
}
R.id.nav_choose -> {
loadFragment(ChooserFragment(ChooserActivity()))
return#setOnNavigationItemSelectedListener true
}
else -> false
}
}
}
private fun loadFragment(fragment: Fragment) {
val transactionMuscle = supportFragmentManager.beginTransaction()
transactionMuscle.replace(R.id.fragment_container, fragment)
transactionMuscle.addToBackStack(null)
transactionMuscle.commit()
}
But my problem with this code which is fine if I would to put the two view in the same container, but I wouldn't, is : I have a ChooserActivity and I would the second part of my bottomNavigationView to redirect to this page, and not the MainActivity Fragment with the Chooser composant. I would do the same as it do for the main Activity but with the ChooserActivity.
I doesn't know if I am clear, but I Thank you in advance.
EDIT to clarify :
My aim is to with mybottomNavigationView, when we click on the first button, it redirect to the MainActivity, and when we click on the second button, it redirect to the ChooserActivity. Th two must have there own containers. Hopefully it is better...
Solution
try this code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bottomNavigation: BottomNavigationView = findViewById(R.id.barre_nav)
bottomNavigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
} //onCreate() end here
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.nav_home -> {
val homeFragment = HomeFragment.newInstance()
loadFragment(homeFragment)
return#OnNavigationItemSelectedListener true
}
R.id.nav_choose -> {
val chooserFragment = ChooserFragment.newInstance()
loadFragment(chooserFragment)
return#OnNavigationItemSelectedListener true
}
}
false
}
private fun loadFragment(fragment: Fragment) {
val transactionMuscle = supportFragmentManager.beginTransaction()
transactionMuscle.replace(R.id.fragment_container, fragment)
transactionMuscle.addToBackStack(null)
transactionMuscle.commit()
}
In You Fragments OnCreateView() Method add these lines too :
1.HomeFragment
companion object {
fun newInstance(): HomeFragment = HomeFragment()
}
2.ChooserFragment
companion object {
fun newInstance(): ChooserFragment= ChooserFragment()
}
Hope so it works :)
If you still face any issue , please add it into comments
I use 2 fragments inside my MainActivity, one is a preferences fragment and the other (default one) is a home fragment. I wanted to make sure that fragment doesn't get recreated if same item is selected in navigation bar. however now I have 2 problems:
1. If I try to change theme from my preferences the bottom navigation bar will stop working
2. Switching to preferences fragment doesn't change title in action bar but it should (I tested and it worked before implementing this the fragment replacement prevention .
Here are some codes:
MainActivity.kt
class MainActivity : ThemeActivity() {
private val homeFragment: HomeFragment = HomeFragment()
private val settingsFragment: SettingsFragment = SettingsFragment()
private var currentFragment: Fragment? = null
private var activeFragment: Int = R.id.navigation_home
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
savedInstanceState?.let {
activeFragment = it.getInt(CURRENT_FRAGMENT, R.id.navigation_home)
}
val toolbar: MaterialToolbar = findViewById(R.id.home_toolbar)
setSupportActionBar(toolbar)
val prefs = getSharedPreferences("prefs", MODE_PRIVATE)
val firstStart = prefs.getBoolean("firstStart", true)
if (firstStart) {
showSecurityDialog()
}
when (activeFragment) {
R.id.navigation_home -> currentFragment = homeFragment
R.id.navigation_settings -> currentFragment = settingsFragment
}
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.add(R.id.frame_layout, settingsFragment).hide(settingsFragment)
.add(R.id.frame_layout, homeFragment).hide(homeFragment)
.show(currentFragment!!)
.commit()
}
val navView: BottomNavigationView = findViewById(R.id.bottom_nav)
navView.setOnNavigationItemSelectedListener{
setFragments(it.itemId)
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar_menu, menu)
return super .onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.about -> {
val intent = Intent(this, AboutActivity::class.java)
startActivity(intent)
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
private fun showSecurityDialog() {
AlertDialog.Builder(this)
.setTitle("Welcome!")
.setMessage("Before we implement a proper security system to check whether app was modified or not, please be sure that you downloaded manager from vanced.app/github")
.setPositiveButton("close"
) { dialog, _ -> dialog.dismiss() }
.create().show()
val prefs = getSharedPreferences("prefs", MODE_PRIVATE)
val editor = prefs.edit()
editor.putBoolean("firstStart", false)
editor.apply()
}
private fun setFragments(itemId: Int): Boolean {
activeFragment = itemId
when (itemId) {
R.id.navigation_home -> {
if (currentFragment is HomeFragment) {
return false
}
supportFragmentManager
.beginTransaction()
.hide(currentFragment!!)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(homeFragment)
.commit()
currentFragment = homeFragment
}
R.id.navigation_settings -> {
if (currentFragment is SettingsFragment) {
return false
}
supportFragmentManager
.beginTransaction()
.hide(currentFragment!!)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(settingsFragment)
.commit()
currentFragment = settingsFragment
}
}
return true
}
companion object{
const val CURRENT_FRAGMENT = "current_fragment"
}
}
PreferenceFragment.kt
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
(activity as MainActivity).supportActionBar?.title = getString(R.string.title_settings)
val updateCheck: Preference? = findPreference("update_check")
val themeSwitch: ListPreference? = findPreference("theme_mode")
themeSwitch?.setOnPreferenceChangeListener { _, _ ->
when (themeSwitch.value){
"LIGHT" -> {
activity?.setTheme(R.style.LightTheme_Blue)
activity?.recreate()
}
"DARK" -> {
activity?.setTheme(R.style.DarkTheme_Blue)
activity?.recreate()
}
"FOLLOW" -> {
when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES ->{
activity?.setTheme(R.style.DarkTheme_Blue)
activity?.recreate()
}
Configuration.UI_MODE_NIGHT_NO -> {
activity?.setTheme(R.style.LightTheme_Blue)
activity?.recreate()
}
}
}
else -> {
activity?.setTheme(R.style.LightTheme_Blue)
activity?.recreate()
}
}
true
}
}
}
I tried to lookup on the web but couldn't find anything useful, I'm pretty sure the problem is with the activity recreation but I don't really know how to fix the issue.
Switched to Navigation Components. It's way better than manually making fragment transactions
I use bottom navigation bar (xx_activity bottom by default) but I have problems.
When I click on the item it's OK, the activity is good but the item of the activity not change...
import android.content.Intent
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_grammaire.*
class GrammaireActivity : AppCompatActivity() {
private val mOnNavigationItemSelectedListener=BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_grammaire -> {
val intent = Intent(this,GrammaireActivity::class.java)
startActivity(intent)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_situations -> {
val intent = Intent(this,SituationsActivity::class.java)
startActivity(intent)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_lexiquefrsa -> {
val intent = Intent(this,LexiqueFrSaActivity::class.java)
startActivity(intent)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_lexiquesafr -> {
val intent = Intent(this,LexiqueSaFrActivity::class.java)
startActivity(intent)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_infos -> {
val intent = Intent(this,InfosActivity::class.java)
startActivity(intent)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_grammaire)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
}
I believe it's recommended to use Fragments with bottom navigation, rather than Activities.
With the onClick you would swap out the current Fragment with the one identified by the click.
I am not sure about "item of the activity not change" but i think you mean to say that the content of your current Activity doesn't change.
So for that you should replace Fragments in an Activity on the click of the BottomNavigation item. This is how it should be done
when (item.itemId) {
R.id.navigation_grammaire -> {
supportFragmentManager.beginTransaction.replace(R.id.container, FragmnetGrammaire().commit())
}
where "container" is the id of the view above your BottomNavigation bar. It can be FrameLayout.
FragmnetGrammaire(), is the instance of your Fragment.