I'm having problems that all my fragments are called when the application start so when I do toast on the the second fragment it appear on the first fragment
here is my activity code
class HomePage : AppCompatActivity() {
var adapter: FPA? = null
val manager = supportFragmentManager
private lateinit var drawer: DrawerLayout
private lateinit var toggle: ActionBarDrawerToggle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
setContentView(R.layout.home_page_act)
adapter = FPA(supportFragmentManager)
container.adapter = adapter
container.addOnPageChangeListener(android.support.design.widget.TabLayout.TabLayoutOnPageChangeListener(tabs))
tabs.addOnTabSelectedListener(android.support.design.widget.TabLayout.ViewPagerOnTabSelectedListener(container))
}
class FPA(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
if(position==0 )
return TrainingForSale()
else if(position==1)
return Shops()
else
return Health()
}
override fun getCount(): Int {
return 3
}
}
fun ShowFramentShops (){
val transaction = manager.beginTransaction ()
val fragmet = Shops()
transaction.replace(R.id.container,fragmet)
transaction.addToBackStack(null)
transaction.commit()
}
}
and here is my fragment pages
fragment (1)
class Shops : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.shops_fragment, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onCreate(null);
//UserInformation.TrainingFilter=""
//UserInformation.TrainingList.clear()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(null)
Toast.makeText(activity,"Shopes",Toast.LENGTH_LONG).show()
}
}// Required empty public constructor
my second fragment
class Health : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.health_fragment, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onCreate(null);
super.onViewCreated(view, savedInstanceState)
Toast.makeText(activity,"Health", Toast.LENGTH_LONG).show()
}
}// Required empty public constructor
So when I run the the app both toast appears in the same page
The ViewPager will not only create the current page, but also the some before or after. With setOffscreenPageLimit(int) you can adapt these values a little.
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
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 (InnerFragment) placed inside a Viewpager (MainViewPager).
This Fragment (InnerFragment) does also contain a ViewPager (NestedViewPager) with multiple Fragments. When I swipe or open the InnerFragment everything works fine and the NestedViewPager shows all Fragments the way it should.
When I leave the InnerFragment after swiping the MainViewPager and go back to the InnerFragment it is blank and nothing shows up.
One solution as described in the internet is using the childfragmentmanager. Unfortunately this doesn't work because if I do so following exception is thrown.
java.lang.IllegalStateException: Fragment ProductImageFragment{c458f99 (1213d869-3715-4541-8nab-f87cyc350630) id=0x8f093165 android:switcher:2111xxxxxx:0} declared target fragment ProductImagesFragment{4b4f25e (99a6aaf6-5500-4821-902f-7bf30f87554c) id=0x7f090126 android:switcher:2131296550:2} that does not belong to this FragmentManager!
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:805)
Maybe it is also important to note, that the MainFragment is implementing an Interface of the Innerfragment.
MainFragment
class ProductImagesFragment : Fragment(),
ProductImageFragment.IProductImageHandler,
ProductImageUploadDialog.ProductImageUploadDialogEventListener {
private lateinit var viewPager: ViewPager
interface IProductImageUploadHandler{
fun onImageUploadToServerFinished(bitmap: Bitmap, imageData: ProductImage)
}
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
return layoutInflater.inflate(R.layout.fragment_product_images,container,false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewPager = view.findViewById(R.id.viewPagerImages)
setViewPagerAdapter()
}
private fun setViewPagerAdapter(){
val manager = fragmentManager
if(manager!=null){
viewPager.adapter = product?.let {product ->
ImageAdapter(this,
product,productImageList,manager)
}
}
}
class ImageAdapter(private val productImagesFragment: ProductImagesFragment, private val product: Product, private val imagesList:ArrayList<ProductImage>, fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) {
override fun getCount(): Int {
return imagesList.size
}
override fun getItem(position: Int): Fragment {
val fragment = ProductImageFragment()
val bundle = Bundle()
val image = imagesList[position]
bundle.putInt("productId",product.id)
bundle.putParcelable("productImage",image)
bundle.putInt("maxImages",imagesList.size)
fragment.setTargetFragment(productImagesFragment,1)
fragment.arguments = bundle
return fragment
}
}
}
InnerFragment
class ProductImageFragment : Fragment() {
private lateinit var productImageHandler: IProductImageHandler
interface IProductImageHandler{
fun onImageDeleted(id: Int)
fun onOrderChanged(url: String, newValue: Int, oldValue: Int)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onAttach(context: Context) {
super.onAttach(context)
try {
productImageHandler = targetFragment as IProductImageHandler
}catch (exception: Exception){
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_product_image, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
}
private fun setValues(view: View){
}
}
You must use the childFragmentManager in order for the state to be restored properly.
You shouldn't be using the target fragment API at all. In fact, if you're using the childFragmentManager, your child fragments already have a reference to the parent via requireParentFragment() - just use that instead of targetFragment.
override fun onAttach(context: Context) {
super.onAttach(context)
try {
productImageHandler = requireParentFragment() as IProductImageHandler
}catch (exception: Exception){
}
}
Guys I need your help.
I use android navigation component and want to save backstack after user press button and restore it after. I found 2 methods
navController.saveState(): Bundle and navController.restoreState(bundle: Bundle).
But i have problem in use it. Seems like saveState work greate (i see bundle, and backstack inside), but i dont understand how to use restoreState, because the documentation says:
Restores all navigation controller state from a bundle. This should be called before any call to setGraph.
https://developer.android.com/reference/kotlin/androidx/navigation/NavController#restorestate
Okay, i did it, seems like backstack restored, but on screen i see first fragment (instead of the one I had when I saved it). What i do wrong?
Code:
FirstFragment
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_forward.setOnClickListener { findNavController().navigate(R.id.action_firstFragment_to_secondFragment) }
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
SecondFragment
class SecondFragment : Fragment() {
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_forward.setOnClickListener { findNavController().navigate(R.id.action_secondFragment_to_thirdFragment) }
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
ThirdFragment
class ThirdFragment : Fragment() {
private val TAG = this::class.java.name
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_third, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
btn_finish.setOnClickListener {
(requireActivity() as MainActivity).saveState() //here save bundle
requireActivity().finishAfterTransition()
}
btn_back.setOnClickListener { requireActivity().onBackPressed() }
}
}
MainActivity
class MainActivity : AppCompatActivity() {
private val TAG = "MySuperActivity"
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "onCreate($savedInstanceState) called")
initNavController()
}
private fun initNavController() {
val navHostFragment = nav_host_fragment as NavHostFragment
val graphInflater = navHostFragment.navController.navInflater
val graph = graphInflater.inflate(R.navigation.main_graph)
navController = navHostFragment.navController
navHostFragment.childFragmentManager
if (App.instance.savedBundle != null) {
Log.d(TAG, "bundle: ${App.instance.savedBundle}")
navController.restoreState(App.instance.savedBundle)
graph.startDestination = R.id.thirdFragment
}
navController.graph = graph
Log.d(TAG, "navController.currentDestination: ${navController.currentDestination}")
Log.d(TAG, "navController.graph.startDestination: ${navController.graph.startDestination}")
}
fun saveState(){
App.instance.savedBundle = navController.saveState()
Log.d(TAG, "saveState() : ${App.instance.savedBundle}")
}
}
here some logs: logs
full code:github
I am not sure if my answer helps you, but I had many issues trying to save the navigation state from handling rotations. The issue that I had comes from an old version of the navigation component, I update to the most recent, and it fixes the issue:
def android_navigation = '2.3.4'
implementation "android.arch.navigation:navigation-fragment-ktx:$android_navigation"
implementation "android.arch.navigation:navigation-ui-ktx:$android_navigation"
implementation "androidx.navigation:navigation-dynamic-features-fragment:$android_navigation"
In an android app I have an activity with several fragments. In one of these fragments I have an ImageButton. On click I want to let something happen in the activity directly, not in the fragment. In this case I want to set an image. Summed up, the ImageButton is in the fragment, but the ImageView I want to change on click is in the activity.
How do I achieve that? Whatever I've tried resulted in an app crash.
This is how my fragment.kt looks:
class Fragment1 : Fragment() {
companion object{
fun newInstance() = Fragment1()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageButton.setOnClickListener{
imageView.setImageResource(R.drawable.example)
}
}
}
class Fragment1 : Fragment() {
var callbacks: OnFragmentCallbacks? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
callbacks = activity as OnFragmentCallbacks
}
interface OnFragmentCallbacks{
fun changeImage(resourceId: Int)
}
companion object{
fun newInstance() = Fragment1()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
imageButton.setOnClickListener{
callbacks?.changeImage(R.drawable.example)
}
}
In your Activity:
// Change FragmentName with name of your Fragment class
class MainActivity : AppCompatActivity(), FragmentName.OnFragmentCallbacks{
override fun changeImage(resouseId: Int){
imageView.setImageDrawable(getResources().getDrawable(resourceId))
// or
imageView.setImageResource(resourceId)
}
Just coded it in stackoverflow, may not be perfect
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
}