I have implemented a tableyout within a fragment, the logic is simple, when I click on an item in a list it derives to a fragment (tableyout), the problem happens when I go back and select another item it shows me the tableyout but with blank views, reviewing a behavior the fragments of the tablayout are never destroyed. Here is the adapter code and the main fragment. Thanks in advance.
class TabAdapter(fm: FragmentManager): FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when(position){
0->FormPartOne()
1->FormPartTwo()
2->FormPartThree()
else->FormPartOne()
}
}
override fun getCount(): Int {
return 3
}
override fun getPageTitle(position: Int): CharSequence? {
return when(position){
0->"1"
1->"2"
2->"3"
else->""
}
}
}
Main Fragment:
class ReceptionFormFragment : Fragment() {
private lateinit var receptionOrderViewModel: ReceptionOrderViewModel
private lateinit var tabs: TabLayout
private lateinit var viewPager: ViewPager
private lateinit var sectionsPagerAdapter: TabAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_reception_form, container, false)
sectionsPagerAdapter = fragmentManager?.let { TabAdapter(it) }!!
viewPager = root.findViewById(R.id.view_pager)
viewPager.adapter = sectionsPagerAdapter
tabs = root.findViewById(R.id.tabs)
tabs.setupWithViewPager(viewPager)
return root
}
}
Replace
sectionsPagerAdapter = fragmentManager?.let { TabAdapter(it) }!!
With
sectionsPagerAdapter = TabAdapter(childFragmentManager)
And it will both work correctly and remove the fragments automatically on back (when this Fragment is removed).
Related
I have a main activity that contains a ViewPager2 and a TabLayout to house a few fragments. Each fragment will have buttons that I want to trigger actions in the main activity. However, I'm not seeing how to set up the buttons/event handlers so that the main activity is notified whenever a button is pressed inside one of the fragments. Thanks!
MenuActivity.kt - ViewPager2/TabLayout Setup
private fun configureMenu() {
// Create adapter
val fragmentAdapter = PageAdapter(supportFragmentManager, lifecycle)
// Add fragments
fragmentAdapter.addFragment(CameraFragment(), getString(R.string.camera_tab))
fragmentAdapter.addFragment(DevicesFragment(), getString(R.string.devices_tab))
fragmentAdapter.addFragment(ConnectionFragment(), getString(R.string.connection_tab))
// Set up pageviewer
viewpager_menu.adapter = fragmentAdapter
TabLayoutMediator(tablayout_main, viewpager_menu) { tab, position ->
tab.text = fragmentAdapter.getPageTitle(position)
viewpager_menu.setCurrentItem(tab.position, true)
}.attach()
}
PageAdapter.kt
class PageAdapter(fm: FragmentManager, lifecycle: Lifecycle): FragmentStateAdapter(fm, lifecycle) {
private var fragmentList = ArrayList<Fragment>()
private var fragmentTitleList = ArrayList<String>()
fun addFragment(fragment: Fragment, title: String) {
fragmentList.add(fragment)
fragmentTitleList.add(title)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
}
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
fun getPageTitle(position: Int): String {
return fragmentTitleList[position]
}
}
CameraFragment.kt
class CameraFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_camera, container, false)
}
}
Thanks!
Inside actions of the fragment
val main = this.activity!! as MainActivity
main.callSomeFunc()
You can pass the this object of activity in specific frament constructor and use from fragment to call the activity class method
for example:
class MenuActivity : AppCompatActivity(){
fragmentAdapter.addFragment(CameraFragment(this), getString(R.string.camera_tab))
}
class CameraFragment : Fragment {
var mActivity: MenuActivity? = null
constructor(mActivity: MenuActivity?) {
this.mActivity = mActivity
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_camera, container, false)
onlick{
mActivity.callMethodOfMenuActivity()
}
}
}
I have a Fragment that contains a ViewPager. When I navigate to this fragment I see the first page as expected. However, if I hit back, and then navigate to that fragment again, I am then presented with a blank white screen...
What I'm noticing in the Layout Inspector is that when I navigate to the ViewPager Fragment the second time, the ViewPager is present, but none of the page fragments are in the hierarchy.
Here is how I set up my viewPager in my ViewPagerFragment:
/**
* Variables
*/
var pageOneFragment = ForgotPasswordInitiateFragment()
var pageTwoFragment = ForgotPasswordSubmitCodeFragment()
var pageThreeFragment = ForgotPasswordSubmitPasswordFragment()
lateinit var mViewPager: ForgotPasswordViewPager
lateinit var mPagerAdapter: ForgotPasswordPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.forgot_password_pager_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
forgotPasswordViewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ForgotPasswordViewModel::class.java)
pageOneFragment.viewPager = this
pageTwoFragment.viewPager = this
pageThreeFragment.viewPager = this
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.let {
mPagerAdapter = ForgotPasswordPagerAdapter(it.supportFragmentManager)
}
mViewPager = viewPager
mViewPager.adapter = mPagerAdapter
mViewPager.setListeners()
}
override fun onResume() {
super.onResume()
}
inner class ForgotPasswordPagerAdapter(fm: FragmentManager): FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
when (position) {
0 -> return pageOneFragment
1 -> return pageTwoFragment
2 -> return pageThreeFragment
else -> throw IllegalStateException("Pager position $position is out of bounds.")
}
}
override fun getCount(): Int {
return 3
}
}
I attempted to set all my mViewPager configurations into the onResume function but the same thing happened.
When does the viewPager actually populate its pages? Because what it seems like to me is they are populated the first time, but not the second....
EDIT:
I debugged the Activity's fragments on the back pressed and noticed that the pages were in that fragment stack, but not the actual viewPager fragment.
You're using
activity?.let {
mPagerAdapter = ForgotPasswordPagerAdapter(it.supportFragmentManager)
}
I.e., using the activity's FragmentManager. That's always the wrong thing to do and the source of your issue. Instead, you need to use the Fragment's childFragmentManager:
mPagerAdapter = ForgotPasswordPagerAdapter(childFragmentManager)
I am developing a Kotlin app and at some point I want to implement viewPager to swipe between fragments. I have one activity to navigate to the rest of the app through the navigation graph. I have not really understood how this swiping should work.
My question is, do I need to implement a new activity as well besides the pageAdapter? And how this activity is going to cooperate with the main one? My app currently has a splash screen, and after that I would like to have the swipe mode between the fragments.
I want to implement viewPager to swipe between fragments.
nice
I have one activity to navigate to the rest of the app through the navigation graph.
cool
do I need to implement a new activity as well besides the pageAdapter?
no
And how this activity is going to cooperate with the main one?
don't have a second activity, then it doesn't need to "cooperate"
I would like to have the swipe mode between the fragments.
https://gist.github.com/Zhuinden/c643f03a023a9cbe83fff6c75c948d3b
class MyFragmentPagerAdapter(
private val context: Context,
fragmentManager: FragmentManager
) : FragmentPagerAdapter(fragmentManager) {
override fun getCount() = 2
override fun getItem(position: Int) = when(position) {
0 -> FirstFragment()
1 -> SecondFragment()
else -> throw IllegalStateException("Unexpected position $position")
}
override fun getPageTitle(position: Int): CharSequence = when(position) {
0 -> context.getString(R.string.first)
1 -> context.getString(R.string.second)
else -> throw IllegalStateException("Unexpected position $position")
}
}
class ParentFragment: Fragment() {
override fun onCreateView(...) = ...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewPager = view.findViewById(R.id.view_pager)
viewPager.adapter = MyFragmentPagerAdapter(requireContext(), childFragmentManager)
tabLayout.setupWithViewPager(viewPager)
}
}
Try this
in your viewPager
class pageradapter (fm: FragmentManager) : FragmentStatePagerAdapter(fm){
override fun getItem(position: Int): Fragment {
when(position){
0-> return fragment1()
1-> return fragment2() // you can add more if you have more fragments
else-> return fragment3()
}
}
override fun getCount(): Int {
return 3
number of fragments that you have so the swiping could work
}
in your fragments 1 or 2 or 3 ...etc'
class fragment1 : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment1, container, false)
// write your codes
}
in your Activity after the splash screen
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main4)
val adapter = pageradapter(supportFragmentManager)
val pager = findViewById<View>(R.id.pager) as ViewPager
pager.adapter = adapter
}
I'm trying to use a ViewPager that contains a fragment.
When I add a fragment using a FragmentPagerAdapter it doesn't work, and I noticed the 'onCreateView' function doesn't get called.
I tried following some tutorials but I can't seem to find the problem.
Here is the code:
class GuestListFragment : Fragment()
{
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
println("onCreateView")
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_guest_list, container, false)
}
}
class EventPagerAdapter(fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager)
{
override fun getCount(): Int
{
return 0
}
override fun getItem(position: Int): Fragment
{
println("getItem")
return GuestListFragment()
}
}
class EventActivity : AppCompatActivity()
{
private lateinit var viewPager: ViewPager
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event)
viewPager = findViewById(R.id.guests_view_pager)
val pagerAdapter = EventPagerAdapter(supportFragmentManager)
pagerAdapter.getItem(0)
viewPager.adapter = pagerAdapter
})
}
}
Change return count to 1 in getCount()
override fun getCount(): Int
{
return 1
}
is there any possible way to change the swiping of a fragments from left to right?
my Fragments swipes from right to left but I need the fragments to swipe from left to right.
Pager Adapter.kt
class pageradapter (fm: FragmentManager) : FragmentStatePagerAdapter(fm){
override fun getItem(position: Int): Fragment {
when(position){
0-> return fragment1()
1-> return fragment2()
else-> return fragment3()
}
}
override fun getCount(): Int {
return 3
}
fragment1.kt // 2 and 3 has the same code
class fragment1 : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment1, container, false)
}
Main4Activity.kt
class Main4Activity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main4)
val adapter = pageradapter(supportFragmentManager)
val pager = findViewById<View>(R.id.pager) as ViewPager
pager.adapter = adapter
}
You should reverse the Fragment positions like following:
In PagerAdapter.kt
class pageradapter (fm: FragmentManager) : FragmentStatePagerAdapter(fm){
override fun getItem(position: Int): Fragment {
when(rotatePosition(position)) {
0-> return fragment1()
1-> return fragment2()
else-> return fragment3()
}
}
override fun getCount(): Int {
return 3
}
private fun rotatePosition(position: Int): Int {
return (getCount() - 1) - position
}
}
In Main4Activity:
class Main4Activity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main4)
val adapter = pageradapter(supportFragmentManager)
val pager = findViewById<View>(R.id.pager) as ViewPager
pager.adapter = adapter
// setting new start position
pager.setCurrentItem(adapter.rotatePosition(0), false);
}
You can animate it using a PageTransformer
Or,
if you want the animation on replacing a fragment, you can set a custom animation on the FragmentTransaction