got error fragment already added on my MyPagerAdapter class which extends FragmentPagerAdapter
this is my error logs
E/AndroidRuntime: FATAL EXCEPTION: main
Process: id.cahyowhy.tmdbmovieimplementation, PID: 8228
java.lang.IllegalStateException: Fragment already added: MovieFragment{df79747} (01cf33b8-9eb3-4dce-9519-384ca7dd7570) id=0x7f08014e android:switcher:2131231054:0}
at androidx.fragment.app.FragmentStore.addFragment(FragmentStore.java:67)
at androidx.fragment.app.FragmentManager.addFragment(FragmentManager.java:1563)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:405)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
this is my pageradapter code
class MyPagerAdapter(fm: FragmentManager) :
FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val pages = listOf(
MovieFragment.newInstance(MovieListType.POPULAR),
MovieFragment.newInstance(MovieListType.TOP_RATED),
MovieFragment.newInstance(MovieListType.UPCOMING)
)
override fun getItem(position: Int): Fragment {
return pages[position]
}
override fun getCount(): Int {
return pages.size
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> "Popular"
1 -> "Top Rated"
else -> "Up Coming"
}
}
}
this is my movie fragment code
package id.cahyowhy.tmdbmovieimplementation.ui.activity.fragment
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import id.cahyowhy.tmdbmovieimplementation.databinding.MoviesFragmentResultBinding
import id.cahyowhy.tmdbmovieimplementation.ui.adapter.ItemTypeFactoryImpl
import id.cahyowhy.tmdbmovieimplementation.ui.adapter.VisitableRecyclerAdapter
import id.cahyowhy.tmdbmovieimplementation.ui.adapter.viewholder.ErrorStateItem
import id.cahyowhy.tmdbmovieimplementation.ui.adapter.viewholder.MovieItem
import id.cahyowhy.tmdbmovieimplementation.ui.base.BaseFragment
import id.cahyowhy.tmdbmovieimplementation.ui.base.BaseViewItem
import id.cahyowhy.tmdbmovieimplementation.ui.util.ext.observe
import org.koin.android.viewmodel.ext.android.viewModel
open class MovieFragment(
private val movieListType: MovieListType
) : BaseFragment() {
private val viewModel by viewModel<MovieFragmentViewModel>()
private lateinit var binding: MoviesFragmentResultBinding
private val movieAdapter by lazy {
VisitableRecyclerAdapter(
ItemTypeFactoryImpl(),
::onItemClicked
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = MoviesFragmentResultBinding.inflate(
inflater,
container,
false
)
return binding.root
}
override fun onResume() {
super.onResume()
viewModel.loadData(0, movieListType)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
with(binding) {
recyclerView.adapter = movieAdapter
recyclerView.setHasFixedSize(true)
swipeRefresh.setOnRefreshListener { viewModel.loadData(0, movieListType) }
}
super.onViewCreated(view, savedInstanceState)
}
override fun observeChange() {
observe(viewModel.loading, ::handleLoading)
observe(viewModel.movies, ::onDataLoaded)
observe(viewModel.toastMessage, ::showSnackbarMessage)
}
private fun handleLoading(status: Boolean) {
binding.swipeRefresh.isRefreshing = status
}
private fun onDataLoaded(items: List<BaseViewItem>) {
movieAdapter.submitList(items)
}
private fun onItemClicked(viewItem: BaseViewItem, view: View) {
when (viewItem) {
is MovieItem -> {
Log.d("MovieItem", "MovieItem Click: ${viewItem.title}")
}
is ErrorStateItem -> {
viewModel.loadData(0, movieListType)
}
}
}
companion object {
fun newInstance(movieListType: MovieListType): MovieFragment =
MovieFragment(movieListType)
}
}
and this is when i called from MainActivity.kt
class MainActivity : BaseActivity() {
private lateinit var binding: ActivityMainBinding;
....
fun initView() {
with(binding) {
viewPager.adapter = MyPagerAdapter(supportFragmentManager)
viewPagerTab.setupWithViewPager(viewPager)
}
}
can anyone solve this.. thanks..
The problem is MyFragment
is a class and the FragmentManager
is something that you get by calling supportFragmentManager
. This is provided to you by the system. For the fragment manager to be able to add new fragments to the back stack, the Fragments provided to it should be unique.
Now I do not know what your use case is but calling MyFragment.newInstance()
does not provide you unique classes as they are just new instances of the same MyFragment
class. The error log is stating that.
To get around this you can create three different Fragment classes like PopularMoviesFragment
, TopRatedMoviesFragment
and UpcomingMoviesFragment
and then add them to your list on the adapter class.
I know it seems like a lot of work but this way you have three different fragments that shows data for three different cases of Movies and you can have additional logic in them. And you will also not hit java.lang.IllegalStateException: Fragment already added: MovieFragment
error
Your Adapter code where you call:
private val pages = listOf(
MovieFragment.newInstance(MovieListType.POPULAR),
MovieFragment.newInstance(MovieListType.TOP_RATED),
MovieFragment.newInstance(MovieListType.UPCOMING)
)
should now look like:
private val pages = listOf(
PopularMoviesFragment.newInstance(MovieListType.POPULAR),
TopRatedMoviesFragment.newInstance(MovieListType.TOP_RATED),
UpcomingMoviesFragment.newInstance(MovieListType.UPCOMING)
)