FragmentPagerAdapter throwing IllegalArgumentException - Fragment Already Added - android-fragments

I have a ViewPager and a TabLayout containing 3 Fragments. At times when I swipe through(with the help of ViewPager) the 3 Fragments, an IllegalArgumentException is thrown with a message that Fragment is already added: ...(Fragment at index 0 to be specific). What could be the problem as the error log is only showing something to do with FragmentManager class and none of my Fragment classes? Below is the adapter(s) I am using
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.fragment.app.FragmentStatePagerAdapter
data class TitledFragment(val fragment: Fragment, val title: CharSequence?)
/**
* Should be used when displaying more than 2 [Fragment]s in a view-pager otherwise use
* [FragmentViewPagerAdapter]
*/
#Suppress("KDocUnresolvedReference")
class FragmentStateViewPagerAdapter(
private val titledFragmentList: List<TitledFragment>,
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabCount = titledFragmentList.size
/**
* Selects the middle or first [Fragment] as the default selected [Fragment] from a list
* ([titledFragmentList]) of odd-numbered [Fragment]s and even-numbered [Fragment]s respectively.
* For example, a view-pager supposed to display 3 fragments ([titledFragmentList] = 3) will have
* it's default selected fragment position equal to 1(2nd [Fragment]) from the list whereas a
* view-pager supposed to display 2 fragments ([titledFragmentList] = 2) will have it's default
* selected fragment position equal to 0(1st [Fragment])
*/
val middleFragmentPosition: Int
get() = if (tabCount <= 0 || tabCount == 1) {
0
} else {
val fl = tabCount.toFloat() / 2
when {
tabCount % 2 == 0 -> fl.toInt() - 1
else -> fl.toInt()
}
}
override fun getPageTitle(position: Int): CharSequence? =
titledFragmentList[position].title ?: super.getPageTitle(position)
override fun getItem(position: Int): Fragment = titledFragmentList[position].fragment
override fun getCount(): Int = tabCount
}
/**
* Should be used when displaying less than 3 [Fragment]s in a view-pager otherwise use
* [FragmentStateViewPagerAdapter]
*/
#Suppress("KDocUnresolvedReference")
class FragmentViewPagerAdapter(
private val titledFragmentList: List<TitledFragment>,
fragmentManager: FragmentManager
) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabCount = titledFragmentList.size
/**
* Selects the middle or first [Fragment] as the default selected [Fragment] from a list
* ([titledFragmentList]) of odd-numbered [Fragment]s and even-numbered [Fragment]s respectively.
* For example, a view-pager supposed to display 3 fragments ([titledFragmentList] = 3) will have
* it's default selected fragment position equal to 1(2nd [Fragment]) from the list whereas a
* view-pager supposed to display 2 fragments ([titledFragmentList] = 2) will have it's default
* selected fragment position equal to 0(1st [Fragment])
*/
val middleFragmentPosition: Int
get() = if (tabCount <= 0 || tabCount == 1) {
0
} else {
val fl = tabCount.toFloat() / 2
when {
tabCount % 2 == 0 -> fl.toInt() - 1
else -> fl.toInt()
}
}
override fun getPageTitle(position: Int): CharSequence? =
titledFragmentList[position].title ?: super.getPageTitle(position)
override fun getItem(position: Int): Fragment = titledFragmentList[position].fragment
override fun getCount(): Int = tabCount
}

I think, you shouldn't hold references to fragments outside of adapter. They should be created inside the adapter. Probably the adapter can be written this way:
class FragmentStateViewPagerAdapter(
private val tabTitles: List<String>,
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabs: List<Fragment> = (tabTitles.indices).map { YourFragment.newInstance(it) }
override fun getCount(): Int = tabTitles.size
override fun getItem(position: Int): Fragment = tabs[position]
override fun getPageTitle(position: Int): CharSequence {
// Generate a title depending on the position.
return tabTitles[position]
}
}
But your example also works. Maybe more code is required. Also strangely BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT is not required in my case (it shows error, but must not).
class MainActivity : AppCompatActivity() {
private lateinit var adapter: FragmentViewPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val titledFragments = (1..3).map { TitledFragment(PlusOneFragment(), it.toString()) }
adapter = FragmentViewPagerAdapter(titledFragments, supportFragmentManager)
with(view_pager) {
adapter = this#MainActivity.adapter
}
}
}

Related

Horizontal Scrolling is not working in RowsSupportFragment Android Leanback?

I am trying to implement a rowsSupportFragment using Android Leanback for android tv.
But the vertical Scrolling is not working is Keeps glitching.
class DetailsRowListFragment: RowsSupportFragment() {
private var rootAdapter : ArrayObjectAdapter = ArrayObjectAdapter(
ListRowPresenter(
FocusHighlight.ZOOM_FACTOR_MEDIUM,true)
)
private var episodeItemSelectedListener: ((Episode)->Unit)?= null
private var relatedItemSelectedListener: ((Relation)->Unit)?= null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//setting up adapter from within the class
adapter = rootAdapter
//Setting up listener
onItemViewSelectedListener = ItemViewSelectedListener()
}
fun bindData(animeData : AnimeDetails ){
val episodeObjectAdapter = ArrayObjectAdapter(EpisodePresenter())
animeData.episodes.forEach { it->
episodeObjectAdapter.add(it)
}
val episodeHeaderItem= HeaderItem("Episodes")
val listRow = ListRow(episodeHeaderItem,episodeObjectAdapter)
rootAdapter.add(listRow)
}
fun setOnContentSelectedListener(listener: (Episode)->Unit){
this.episodeItemSelectedListener=listener
}
private fun setRelatedRow(itemList: List<Relation>){
val relatedObjectAdapter = ArrayObjectAdapter(RelatedPresenter())
itemList.forEach { it->
relatedObjectAdapter.add(it)
}
val headerItem = HeaderItem("Related Media")
val listRow = ListRow(headerItem,relatedObjectAdapter)
rootAdapter.add(listRow)
}
inner class ItemViewSelectedListener : OnItemViewSelectedListener {
override fun onItemSelected(
itemViewHolder: Presenter.ViewHolder?,
item: Any?,
rowViewHolder: RowPresenter.ViewHolder?,
row: Row?
) {
if( row !=null && row!!.headerItem!!.name=="Episodes" && item is Episode){
episodeItemSelectedListener?.invoke(item as Episode)
}else{
Log.d("Selection Listener","Listener Activated")
}
}
}
The Implemetation of My RowsSupportFragment Class is given above
enter image description here
Whenever I try to scroll side to side using the emulator's remote the row jsut glitches and comes back to its initial position.
But I am able to move the list by dragging with my mouse cursor.

Problems updating recycler views with sqlite database in kotlin

I am writing a code that deletes the item from the database when the button of the popup menu is pressed , and deletes it according to the recycler viewer.
selected item is deleted from the database, but not from the recycler view.
error code: No error code
PrintActivity.kt
class PrintActivity : AppCompatActivity() {
val helper = SqliteHelper(this, "myDB.sql", 1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vBinding = ActivityPrintBinding.inflate(layoutInflater)
setContentView(vBinding.root)
var recyclerViewAdapter = CustomAdapter()
recyclerViewAdapter.listData = helper.select()
vBinding.myRecyclerView.adapter = recyclerViewAdapter
vBinding.myRecyclerView.layoutManager = LinearLayoutManager(this)
vBinding.myRecyclerView.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
)
}
inner class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>(){
var listData = ArrayList<questionType>()
inner class Holder(val vBinding: QuestionLayoutRecyclerBinding) :
RecyclerView.ViewHolder(vBinding.root){
fun setData(id:Int?, question: String, answer: String, exp: String) {
vBinding.printId.text=id.toString()
vBinding.myLinear.setOnClickListener {
var myPopupMenu = PopupMenu(this#PrintActivity, it)
menuInflater?.inflate(R.menu.menu, myPopupMenu.menu)
var listener = PopupMenuListener()
myPopupMenu.setOnMenuItemClickListener(listener)
myPopupMenu.show()
}
}
inner class PopupMenuListener:PopupMenu.OnMenuItemClickListener{
override fun onMenuItemClick(p0: MenuItem?): Boolean {
listData[adapterPosition].id?.let { helper.delete(it) }
//here
this#CustomAdapter.notifyDataSetChanged()
return false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.Holder {
val vBinding = QuestionLayoutRecyclerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return Holder(vBinding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val question = listData[position]
holder.setData(question.id, question.question, question.answer, question.exp)
}
override fun getItemCount(): Int {
return listData.size
}
}
}
You can try this approach using higher order function as callback mechanism.
One recommendation I would give is you can try to avoid these nested inner classes and make into separate files. Hope it works
inner class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>(){
var listData = ArrayList<questionType>()
fun deleteItem(position: Int) { //Function to remove recyclerview item
listData.remove(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, getItemCount())
}
inner class Holder(val vBinding: QuestionLayoutRecyclerBinding) :
RecyclerView.ViewHolder(vBinding.root){
fun setData(id:Int?, question: String, answer: String, exp: String,itemRemovedCallback:(a:Int)->Unit) { //higher order callback function
vBinding.printId.text=id.toString()
vBinding.myLinear.setOnClickListener {
var myPopupMenu = PopupMenu(this#PrintActivity, it)
menuInflater?.inflate(R.menu.menu, myPopupMenu.menu)
var listener = PopupMenuListener(itemRemovedCallback) //Passed to popupmenu listener
myPopupMenu.setOnMenuItemClickListener(listener)
myPopupMenu.show()
}
}
inner class PopupMenuListener(val itemRemovedCallback:(a:Int)->Unit):PopupMenu.OnMenuItemClickListener{
override fun onMenuItemClick(p0: MenuItem?): Boolean {
listData[adapterPosition].id?.let { helper.delete(it) }
itemRemovedCallback.invoke(adapterPosition) //Calling function. It will pass position to function in adapter
return false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.Holder {
val vBinding = QuestionLayoutRecyclerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return Holder(vBinding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val question = listData[position]
holder.setData(question.id, question.question, question.answer, question.exp,::deleteItem) //Passing function as higher order function to viewholder
}
override fun getItemCount(): Int {
return listData.size
}
}

JavaFX ListChangeListener: getPermutation() not working

I use ListChangeListener to listen to changes in Tab Pane.
private final TabPane tabBar = new TabPane();
...
tabBar.getTabs().addListener(this::tabsChanged);
I'm trying to listen to tab move events with the following code:
private void tabsChanged(ListChangeListener.Change<? extends Tab> change) {
while (change.next()) {
if (change.wasPermutated()) {
for (int i = change.getFrom(); i < change.getTo(); i++) {
System.out.println(i + " -> " + change.getPermutation(i));
}
}
}
}
As JavaFX documentation says:
In order to get the new position of an element, you must call:
change.getPermutation(oldIndex). Returns: the new index of the same
element.
But in my case change.getPermutation(i) always returns just i.
For example, I have 4 tabs.
Their indexes are: 0, 1, 2, 3.
Then I move the 4th tab to the first position.
I expect the following output:
0 -> 1
1 -> 2
2 -> 3
3 -> 0
But I get:
0 -> 0
1 -> 1
2 -> 2
3 -> 3
How can I make it work as I need?
As already noted in the comments: the behavior you observe is a bug just reported as JDK-8278062 - the doc and your expectation based on the doc is correct, the notification (implemented in the internal class TabObservableList) is wrong.
Normally, if we want to find the newIndex, a listChangeListener would do something like:
for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); ++oldIndex) {
int newIndex = c.getPermutation(oldIndex);
...
}
To work around the issue, we could manually keep a copy of the tabs, lookup the tab at the old index and find its new index in the re-ordered tabs:
for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); ++oldIndex) {
Tab tab = copy.get(oldIndex);
int newIndex = c.getList().indexOf(tab);
...
}
// update the copy
Or we could have some fun and implement a TransformationList around the original tabs that does the work for us :) It jumps in when it detects a permutation and fires the correct notification. Note that the only internal class used below is SourceChangeAdapter, we either need to relax encapsulation or c&p its content (it is doing nothing but pass on notifications on behalf of the wrapper)
public class TabObservableListWrapper extends TransformationList<Tab, Tab> {
// copy of source used to build the correct permutation
private ObservableList<Tab> copy = FXCollections.observableArrayList();
public TabObservableListWrapper(ObservableList<Tab> source) {
super(source);
updateCopy();
}
#Override
protected void sourceChanged(Change<? extends Tab> c) {
// TBD: cope with a change that has
// - a mixture of permutation and other subchanges
// - multiple subchanges of type permutation
boolean isPermutation = false;
// check if the change is a permutation
while (c.next()) {
if (c.wasPermutated()) {
isPermutation = true;
break;
}
}
c.reset();
if (isPermutation) {
beginChange();
updatePermutation(c);
endChange();
} else {
// assuming other change type notifications are correct, just delegate
fireChange(new SourceAdapterChange<>(this, c));
}
// keep copy sync'ed to source
updateCopy();
}
/**
* Converts the incorrect permutation notification from source
* into a correct one and let super fire the appropriate change.
*
* Note: this method must be called inside a begin/endChange block.
* #param c a change with a single subChange of type wasPermutated
*/
private void updatePermutation(Change<? extends Tab> c) {
c.next();
int from = c.getFrom();
int to = c.getTo();
int permSize = to - from;
int[] perm = new int[permSize];
// fill the perm
for(int i = 0; i < permSize; i++) {
int oldIndex = from + i;
Tab tab = copy.get(oldIndex);
perm[i] = c.getList().indexOf(tab);
}
nextPermutation(from, to, perm);
}
// keep copy sync'ed
private void updateCopy() {
copy.setAll(getSource());
}
// implement public methods by delegating 1:1 to source
#Override
public int getSourceIndex(int index) {
return index;
}
#Override
public int getViewIndex(int index) {
return index;
}
#Override
public Tab get(int index) {
return getSource().get(index);
}
#Override
public int size() {
return getSource().size();
}
}
To use, wrap it around a tabPane's tab list and listen to the wrapper instead of directly to original list, something like:
TabObservableListWrapper wrapper = new TabObservableListWrapper(tabPane.getTabs());
wrapper.addListener((ListChangeListener<Tab>)change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("from wrapper:");
for (int oldIndex = change.getFrom(); oldIndex < change.getTo(); oldIndex++) {
System.out.println(oldIndex + " -> " + change.getPermutation(oldIndex));
}
}
}
});

Cannot fill a MutableLiveData of type ArrayList, outcome is always null

Im working on a quizgame and i want to store some ids in a MutableLiveData-arraylist. Therfore i made a function to loop all my documents in de database and add each ID to the arraylist. BUT the outcome is always null. I don't see where i go wrong?
I'm working with a MVVM-structure
GameViewModel:
class GameViewModel : ViewModel() {
// database instance
val db = FirebaseFirestore.getInstance()
// the current category
private val _category = MutableLiveData<String>()
val category: LiveData<String>
get() = _category
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
// the current question
private val _question = MutableLiveData<String>()
val question: LiveData<String>
get() = _question
/**
* Set Current Category
*/
fun SetCategory (categoryName: String){
_category.value = categoryName
}
/**
* Get the list of QuestionIds
*/
fun GetListQuestionIds() {
db.collection("questions")
.whereEqualTo("category", "$_category")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
_questionIdsArray.value?.add(document.data["questionid"] as Long)
Log.d("GetSize","${_questionIdsArray.value?.size}")
}
Log.d("GetSize2","${_questionIdsArray.value?.size}")
}
.addOnFailureListener { exception ->
Log.w("errorforloop", "Error getting documents: ", exception)
}
}
/**
* Get a Question
*/
fun GetQuizQuestion() {
Log.d("retro","${_questionIdsArray.value?.size}")
db.collection("questions")
.whereEqualTo("category", "$_category")
.whereEqualTo("questionid", "${_questionIdsArray.value?.get(0)}")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
_question.value = document.data["question"].toString()
}
}
.addOnFailureListener { exception ->
Log.w("err", "Error getting documents: ", exception)
}
}
GAMEFRAGMENT:
class GameFragment : Fragment() {
private lateinit var viewModel: GameViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentGameBinding.inflate(inflater)
// Get the viewModel
viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
binding.lifecycleOwner = this
// Set the viewModel for DataBinding - this allows the bound layout access to all of the data in the VieWModel
binding.gameviewModel = viewModel
//arguments passed
val selectedCategory = arguments?.getString("selectedCategory")!!
//set current category so that the viewModel can use it
viewModel.SetCategory(selectedCategory)
viewModel.GetListQuestionIds()
viewModel.GetQuizQuestion()
return binding.root
}
If someone can enlighten me ...
Your Problem
You're not initializing the array. This is your code:
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
This declares a MutableLiveData of type ArrayList<Long>, but does not initialize it so its value defaults to null.
In your for loop you conditionally add items:
_questionIdsArray.value?.add(document.data["questionid"] as Long)
But of course value was never initialized so it's null so add is no-op (does nothing).
The Solution
Just ensure you initialize the live data object at some point.
You could do this inline in the declaration:
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>(arrayListOf())
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
Or during your attempt to populate the list:
.addOnSuccessListener { documents ->
val idsArray = arrayListOf<Long>() // Non-null list to add to
for (document in documents) {
idsArray.add(document.data["questionid"] as Long)
Log.d("GetSize","${idsArray.size}")
}
_questionIdsArray.value = idsArray // Now set live data with a valid list
Log.d("GetSize2","${_questionIdsArray.value?.size}")
}

Fragment already added issue, when using navigation with bottom navigation bar android

I have a bottom navigation view which I have implemented in androidx navigation, but the problem is it is showing me the error of
java.lang.IllegalStateException: Fragment already added: BaseFragment{27d5b00 (2156a830-7756-4fc9-bc63-7c6f3d6705f0) id=0x7f08008c android:switcher:2131230860:0}
I have a base fragment which provides views to different fragments
class MainActivity : AppCompatActivity(),
ViewPager.OnPageChangeListener,
BottomNavigationView.OnNavigationItemReselectedListener,
BottomNavigationView.OnNavigationItemSelectedListener {
// overall back stack of containers
private val backStack = Stack<Int>()
// list of base destination containers
private val fragments = listOf(
BaseFragment.newInstance(R.layout.content_home_base, R.id.toolbar_home, R.id.nav_host_home),
BaseFragment.newInstance(R.layout.content_library_base, R.id.toolbar_library, R.id.nav_host_library),
BaseFragment.newInstance(R.layout.content_settings_base, R.id.toolbar_settings, R.id.nav_host_settings))
// map of navigation_id to container index
private val indexToPage = mapOf(0 to R.id.home, 1 to R.id.library, 2 to R.id.settings)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// setup main view pager
main_pager.addOnPageChangeListener(this)
main_pager.adapter = ViewPagerAdapter()
main_pager.post(this::checkDeepLink)
main_pager.offscreenPageLimit = fragments.size
// set bottom nav
bottom_nav.setOnNavigationItemSelectedListener(this)
bottom_nav.setOnNavigationItemReselectedListener(this)
// initialize backStack with elements
if (backStack.empty()) backStack.push(0)
}
/// BottomNavigationView ItemSelected Implementation
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val position = indexToPage.values.indexOf(item.itemId)
if (main_pager.currentItem != position) setItem(position)
return true
}
override fun onNavigationItemReselected(item: MenuItem) {
val position = indexToPage.values.indexOf(item.itemId)
val fragment = fragments[position]
fragment.popToRoot()
}
override fun onBackPressed() {
val fragment = fragments[main_pager.currentItem]
val hadNestedFragments = fragment.onBackPressed()
// if no fragments were popped
if (!hadNestedFragments) {
if (backStack.size > 1) {
// remove current position from stack
backStack.pop()
// set the next item in stack as current
main_pager.currentItem = backStack.peek()
} else super.onBackPressed()
}
}
/// OnPageSelected Listener Implementation
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {}
override fun onPageSelected(page: Int) {
val itemId = indexToPage[page] ?: R.id.home
if (bottom_nav.selectedItemId != itemId) bottom_nav.selectedItemId = itemId
}
private fun setItem(position: Int) {
main_pager.currentItem = position
backStack.push(position)
}
private fun checkDeepLink() {
fragments.forEachIndexed { index, fragment ->
val hasDeepLink = fragment.handleDeepLink(intent)
if (hasDeepLink) setItem(index)
}
}
inner class ViewPagerAdapter : FragmentPagerAdapter(supportFragmentManager) {
override fun getItem(position: Int): Fragment = fragments[position]
override fun getCount(): Int = fragments.size
}
}
Base Fragment Class :
class BaseFragment: Fragment() {
private val defaultInt = -1
private var layoutRes: Int = -1
private var toolbarId: Int = -1
private var navHostId: Int = -1
private val appBarConfig = AppBarConfiguration(rootDestinations)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
layoutRes = it.getInt(KEY_LAYOUT)
toolbarId = it.getInt(KEY_TOOLBAR)
navHostId = it.getInt(KEY_NAV_HOST)
} ?: return
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return if (layoutRes == defaultInt) null
else inflater.inflate(layoutRes, container, false)
}
override fun onStart() {
super.onStart()
// return early if no arguments were parsed
if (toolbarId == defaultInt || navHostId == defaultInt) return
// setup navigation with toolbar
val toolbar = requireActivity().findViewById<Toolbar>(toolbarId)
val navController = requireActivity().findNavController(navHostId)
NavigationUI.setupWithNavController(toolbar, navController, appBarConfig)
// NavigationUI.setupWithNavController(toolbar,navController)
}
fun onBackPressed(): Boolean {
return requireActivity()
.findNavController(navHostId)
.navigateUp(appBarConfig)
}
fun popToRoot() {
val navController = requireActivity().findNavController(navHostId)
navController.popBackStack(navController.graph.startDestination, false)
}
fun handleDeepLink(intent: Intent) = requireActivity().findNavController(navHostId).handleDeepLink(intent)
companion object {
private const val KEY_LAYOUT = "layout_key"
private const val KEY_TOOLBAR = "toolbar_key"
private const val KEY_NAV_HOST = "nav_host_key"
fun newInstance(layoutRes: Int, toolbarId: Int, navHostId: Int) = BaseFragment().apply {
arguments = Bundle().apply {
putInt(KEY_LAYOUT, layoutRes)
putInt(KEY_TOOLBAR, toolbarId)
putInt(KEY_NAV_HOST, navHostId)
}
}
}
}
I tried empty the stack first but that did not work, i have three navgraphs for my three viewpager elements/ fragments.
We reverted back back to 2.1.0 (nav library) and it worked again
Error is not quite connected with Navigation library. It's most probably fragment's error itself. Try testing that fragment in empty activity without using Navigation.
Navigation library is somehow treating error wrongly.
p.s. in my case it was just and view's id missing in layout file.
Thanks for the support, I found the solution I created different navigation graphs for each page.

Resources