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
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()
When I press the back button, it opens the previous fragment. But in the bottom navigation, another tab remains highlighted.
How do I make the tab on the bottom navigation change when the fragment changes too?
private lateinit var mainBottomBar: BottomNavigationView
private val navigationBottomViewListener = NavigationBarView.OnItemSelectedListener {
val fragment = when (it.itemId) {
R.id.homeMenu -> HomeFragment()
R.id.pomodoroMenu -> PomodoroFragment()
R.id.statisticsMenu -> StatisticsFragment()
else -> return#OnItemSelectedListener false
}
replaceFragmentWithAddBackStack(fragment)
return#OnItemSelectedListener true
}
private fun replaceFragmentWithAddBackStack(fragment: Fragment) {
supportFragmentManager.beginTransaction().addToBackStack(null).replace(R.id.mainContainer, fragment).commit()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainBottomBar = findViewById(R.id.mainBottomBar)
mainBottomBar.setOnItemSelectedListener(navigationBottomViewListener)
}
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 would like to create an recyclerView in a fragment, but it shows an error " java.lang.IllegalStateException: recylerView_Main must not be null
at com.gph.bottomnavigation.FragmentMe.onCreateView(FragmentMe.kt:28)"
Question 1) Please kindly help to solve this issue.
Question 2) I created an recyclerView only in a empty project without any fragment, it is working properly.
But the same code is no working in Fragment, it shows error so I change "recylerView_Main.layoutManager = LinearLayoutManager(this)" to "recylerView_Main.layoutManager = LinearLayoutManager(context)"
It shows no error and I can run in simlulator, but when I click the navigation button of the Fragment, the app stops and show this error. Please kindly help to solve it.
Here with the code for FragmentMe.kt:
class FragmentMe : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
recylerView_Main.layoutManager = LinearLayoutManager(context)
recylerView_Main.adapter = Mainadapter()
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_me, container, false)
}
}
Here with the code of MainActivity.kt:
class MainActivity : AppCompatActivity() {
val manager = supportFragmentManager
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
//message.setText(R.string.title_home)
createFragmentQpon()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
//message.setText(R.string.title_dashboard)
createFragmentMe()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
//message.setText(R.string.title_notifications)
createFragmentTools()
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Action Bar
val actionBar = supportActionBar
actionBar!!.setDisplayShowHomeEnabled(true)
actionBar.setBackgroundDrawable(ColorDrawable(Color.parseColor("#00FFFFFF")))
actionBar.setIcon(R.drawable.ic_home_black_24dp)
actionBar.setDisplayShowTitleEnabled(false)
createFragmentQpon()
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
fun createFragmentQpon() {
val transaction = manager.beginTransaction()
val fragment = FragmentQpon()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
fun createFragmentMe() {
val transaction = manager.beginTransaction()
val fragment = FragmentMe()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
fun createFragmentTools() {
val transaction = manager.beginTransaction()
val fragment = FragmentTools()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
Here with the code of Mainadapter.kt:
class Mainadapter: RecyclerView.Adapter<CustomViewHolder>() {
val videolist = listOf("aaa","bbbb","cccc")
override fun getItemCount(): Int {
return 3
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.tutorial_layout, parent, false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
var videoName = videolist.get(position)
holder.itemView.title.text = videoName
}
}
class CustomViewHolder(v: View): RecyclerView.ViewHolder(v) {
}
Move this code
recylerView_Main.layoutManager = LinearLayoutManager(context)
recylerView_Main.adapter = Mainadapter()
from onCreateView to onActivityCreated
override onActivityCreated and place the above code.
There are two things incorrect in your code :
You are trying to access recyclerView even before inflating the View.
The context of a Fragment is null in onCreateView and is usable in between onAttach and onDetach
recylerView_Main.layoutManager = LinearLayoutManager(this.context)
Try this out, worked fine for me.
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")
}