I am using TabLayout with ViewPager. I have 3 fragments and 3 tabs. I want to access the FloatingActionButton in activitymain.xml file.
In every fragment I write a code like this:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val fab :View= (requireActivity() as MainActivity).findViewById(R.id.fab_main)
fab.setOnClickListener {
Toast.makeText(context,"Fragment1",Toast.LENGTH_SHORT).show()
}
}
But when I click the FloatingActionButton in every fragment it gives me the same Toast message which is related to the last fragment. I want to perform different actions in every fragment when I clicked the button.
You could use FragmentStatePagerAdapter for tab function
class TabAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
..........
}
It will be make Fragment call onResume when the fragment is active.
So you can like this in Fragment
override fun onResume() {
super.onResume()
val fab :View= (requireActivity() as MainActivity).findViewById(R.id.fab_main)
fab.setOnClickListener {
Toast.makeText(context,"Fragment1",Toast.LENGTH_SHORT).show()
}
}
Related
What i have: Application with BottomNavigation using Android Navigation Component. One of fragments - fragment with other two fragments with recyclerviews in viewPager2. When i navigating to another tab and go back, recyclerview is fully redrawing content
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.schedulePager.apply {
isSaveFromParentEnabled = true
offscreenPageLimit = 2
adapter = ScheduleViewPager(childFragmentManager, lifecycle)
setPageTransformer(ZoomOutPageTransformer())
}
... other stuff
}
class ScheduleViewPager(
fragmentManager: FragmentManager,
lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager,lifecycle) {
override fun getItemCount(): Int = 2
override fun createFragment(position: Int): Fragment = PageFragment().apply { arguments = SchedulePageFragmentArgs.bundle(position) }
}
if i'm using FragmentActivity instead of FragmentManager, content in fragments is not redrawing, but i need to use FragmentManager fof access ViewModel from fragment with viewPager
class PageFragment : BaseFragment<FragmentPageBinding>(), SharedPreferences.OnSharedPreferenceChangeListener {
private val viewModel: ScheduleViewModel by viewModels({requireParentFragment()})
I have a Home fragment with multiple buttons and when I click the Contact button, another fragment is opened. Inside this fragment I have two child fragments and two buttons, and I can switch between those child fragments using those buttons. The problem is when I press the Back Button, it switches back between child fragments and only after that it goes back to Home fragment, but I want to directly go back to Home Fragment.
This is how I'm opening the child fragments:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val contactsListFragment = ContactsListFragment()
val groupsListFragment = GroupsListFragment()
activity?.title = getString(R.string.contacts_and_groups)
openChildFragment(contactsListFragment)
binding.contactsButton.setOnClickListener {
openChildFragment(contactsListFragment)
}
binding.groupsButton.setOnClickListener {
openChildFragment(groupsListFragment)
}
}
private fun openChildFragment(fragment: Fragment) {
val childFragmentManager = childFragmentManager
val transaction: FragmentTransaction = childFragmentManager.beginTransaction()
transaction.replace(binding.contactsGroupsFl.id, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
If anyone can help me with this issue would be great. Thanks!
I think you should add a listener for the back button in both of your fragments so that you can clear all the backstack when it's pressed. Something like this :
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
for (i: Int in 1..parentFragmentManager.backStackEntryCount) {
parentFragmentManager.popBackStack()
}
}
})
I'm creating a shopping app where the user can choose next product from the fragment. That will open the next fragment of the same class with more products and so on. I want to use swipe to dismiss behaviour, so when I swipe my finger down I dissmiss the current fragment. It's essential that while swiping the current fragment the user will be able to see the fragment underneath, so I can only use add instead of replace. Here's the problem: When I use add in my fragment manager, the created fragments UI's overlap, which makes the user swipe multiple fragments at once and not show swiping animation. The fragment uses one xml layout so it shares id across all fragments so I have no way of controlling which layouts are disabled. How do I make the user only swipe one fragment/disable touching fragments underneath? The swipe to dissmis is working well with replace but that's not the point. Here's my code.
Activity:
class PullDismissActivity : AppCompatActivity(), PullDismissLayout.Listener {
private lateinit var viewModel: StackingFragmentsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pulldismiss)
viewModel = ViewModelProvider(this).get(StackingFragmentsViewModel::class.java)
supportFragmentManager.beginTransaction()
.add(R.id.pull, SwipeFragment(), null)
.addToBackStack(null) // name can be null
.commit()
val layout = findViewById<PullDismissLayout>(R.id.pull)
layout.setListener(this)
}
override fun onDismissed() {
viewModel.popBackStack()
supportFragmentManager.popBackStack()
}
override fun onShouldInterceptTouchEvent(): Boolean {
return false
}
Fragment:
class SwipeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewModel = ViewModelProvider(requireActivity()).get(StackingFragmentsViewModel::class.java)
viewModel.addFragment(this.hashCode())
val fragid = view.findViewById<TextView>(R.id.fragid)
fragid?.text = System.currentTimeMillis().toString()
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
requireActivity().supportFragmentManager.beginTransaction()
.add(R.id.pull, SwipeFragment(), null)
.addToBackStack(null)
.commit()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment,container,false)
}
I have the weirdest bug on Kotlin, and after two days of trying I finally asking for help.
The problem is simple : I have two fragment and one activity, the first fragment A is a form, with a validate button, when I click on validate, the fragment B replace the fragment A, and if I press back, the fragment A show up again with the form filled.
My problem is that after the fragment is shown again, I can click on the button but the listener is not call, so I can't go to the fragment B again. The strange thing is that the other listener are properly working, so I'm thinking it's because the previous fragment is catching the onClick, but idk what to do. Here is some code :
ViewUtils :
fun addFragment(activity: Activity, fragment: androidx.fragment.app.Fragment, container: Int) {
val fragmentManager = (activity as AppCompatActivity).supportFragmentManager
val pendingTransaction = fragmentManager.beginTransaction().add(container, fragment, fragment.javaClass.name)
pendingTransaction.commitAllowingStateLoss()
}
fun replaceFragment(manager: FragmentManager, fragment: androidx.fragment.app.Fragment, container: Int) {
if (fragment.isAdded) return
val pendingTransaction = mangaer.beginTransaction()
pendingTransaction.replace(container, fragment, fragment.javaClass.name)
pendingTransaction.commitAllowingStateLoss()
}
fun removeFragment(activity: Activity, fragment: Fragment) {
val manager = (activity as AppCompatActivity).supportFragmentManager
val trans = manager.beginTransaction()
trans.remove(fragment)
trans.commit()
manager.popBackStack()
}
Activity :
fun displayFragmentA() {
ViewUtils.replaceFragment(supportFragmentManager, FragmentA,
R.id.fragmentLayout)
}
fun FragmentB() {
ViewUtils.replaceFragment(supportFragmentManager, FragmentB,
R.id.fragmentLayout)
}
Fragment A
class AFragment : BaseFragment(), AContract.View {
companion object {
#JvmStatic
fun newInstance(): AFragment {
val fragment = AFragment()
return fragment
}
}
#Inject
lateinit var APresenter: AContract.Presenter<AContract.View>
//end region
//region lifecycle
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_A_layout, container, false)
}
override fun onResume() {
super.onResume()
button_validate.setOnClickListener {
presenter.goToNextStep()
}
}
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
The listener was set in the onViewCreated but I tried moving it to onResume (didn't change anything)
Fragment B code is not important I think, but I can add it if it helps.
Any help is welcome, I really don't know what's going on, the replace/add methods were there before I came to the project, they are not perfect but they are working elsewhere on the project.
I try using breakpoint, the button is not null but we never enter the listener.
Edit : I tried on 3 differents devices, I don't have the bug with a Sony Android 9, but with Huawei et One plus 6 Android 10, the problem persist ..
Ok so after asking to a lot of people, the only solution I found is not using kotlin.synthetic, and using findById instead :
view.findViewById<Button>(R.id.button_validate.setOnClickListener
Using a PagerSlidingTabStrip with a Viewpager inside of a fragment:
The main Activity contains a "main fragment" that changes depending on what item you click in navigation drawer.
When loading the initial fragment containing the Viewpager everything shows up fine (all pages are populated).
Replacing that main fragment with another one and then going back to the viewpager fragment turns every page in the viewpager blank, but the PagerSlidingTabStrip tabs are still there.
Any ideas?
I had a problem like that
try this
mPager.setAdapter(new BasePagerAdapter(getChildFragmentManager(), getResources()));
you probably have this
mPager.setAdapter(new BasePagerAdapter(getFragmentManager(), getResources()));
EDIT:
and in your BasePagerAdapter extend FragmentStatePagerAdapter
public class BasePagerAdapter extends FragmentStatePagerAdapter {
Write ur code ie you are using for setting up your pager adapter inside
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
pager.setAdapter(adapter);
}
Replacing getFragmentManager() with getChildFragmentManager() helped me.
Sorry to reply on an old post, but writing this because non of Stackoverflow solutions for my particular problem helped me.
If you are using new architecture components view model with a master detail shared view model and after returning from detail fragment get blank view pager, do the view model initialization in onViewCreated method of master fragment and not in onCreate (only needed in master fragment).
Also as other answers say remember to use childFragmentManager in view pager adapter.
like this:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
private lateinit var model: SharedViewModel
// In the master fragment do the view model initialization in onViewCreated
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
model = activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}
class DetailFragment : Fragment() {
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}