I'm trying to get a viewpager in an already existing fragment.
In another project I had no issues when the viewpager was in activity_main.xml
One of the things I tried is the following:
ViewPagerAdapter.kt
class ViewPagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> KapperKeuze1Fragment()
1 -> KapperKeuze2Fragment()
else -> KapperKeuze3Fragment()
}
}
override fun getCount(): Int {
return 3
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var homeFragment: HomeFragment
lateinit var afspraakFragment: AfspraakFragment
lateinit var overFragment: OverFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bottomNavigation : BottomNavigationView = findViewById(R.id.bot_nav)
homeFragment = HomeFragment()
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_layout, homeFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
bottomNavigation.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.home -> {
homeFragment = HomeFragment()
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_layout, homeFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
}
R.id.afspraak -> {
afspraakFragment = AfspraakFragment()
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_layout, afspraakFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
}
R.id.over -> {
overFragment = OverFragment()
supportFragmentManager
.beginTransaction()
.replace(R.id.frame_layout, overFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
}
}
true
}
val adapter = ViewPagerAdapter(supportFragmentManager)
val pager = findViewById<View>(R.id.viewPager) as ViewPager
pager.adapter = adapter
}
}
fragment_afspraak.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
The error occurs in MainActivity.kt at: val pager = findViewById<View>(R.id.viewPager) as ViewPager
And gives:
Caused by: kotlin.TypeCastException: null cannot be cast to non-null type androidx.viewpager.widget.ViewPager
Use childFragmentManager instead of supportFragmentManager
Because it returns a private FragmentManager for placing and managing Fragments inside of this Fragment.(You ViewPager fragments located in you afspraak fragment)
So set your ViewPager in your AfspraakFragment as below :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = ViewPagerAdapter(childFragmentManager)
val pager = view.findViewById(R.id.viewPager) as ViewPager
pager.adapter = adapter
}
Related
My activity has a FrameLayout container for fragments and a BottomNavigationView to navigate between the fragments: Home, shop, account and cart. Navigating between these fragments works fine, but when navigating to new fragment (SignUpFragment) from AccountFragment when pushing the button 'signup' it crashes.
using supportFragmentManager didnt work like in MAinActivity so i am trying to use childFragmentManager instead, but the app crashes because it can not find the FrameLayout container in MainActivity.
MainAct:
class MainActivity : AppCompatActivity() {
lateinit var homeFragment: HomeFragment;
lateinit var shopFragment: ShopFragment;
lateinit var accountFragment: AccountFragment;
lateinit var cartFragment: CartFragment;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
homeFragment = HomeFragment()
makeCurrentFragment(homeFragment)
btm_nav.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.home -> {
homeFragment = HomeFragment()
makeCurrentFragment(homeFragment)
}
R.id.shop -> {
shopFragment = ShopFragment()
makeCurrentFragment(shopFragment)
}
R.id.account -> {
accountFragment = AccountFragment()
makeCurrentFragment(accountFragment)
}
R.id.cart -> {
cartFragment = CartFragment()
makeCurrentFragment(cartFragment)
}
}
true
}
}
fun makeCurrentFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().replace(R.id.frame_layout, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) //open = adds a new fragment to the stack
.commit()
}
Layout main:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorwhite"
tools:context=".MainActivity"
tools:ignore="ExtraText">
<FrameLayout
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/btm_nav"
android:layout_marginBottom="12dp" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/btm_nav"
android:layout_alignParentBottom="true"
app:itemBackground="#color/colorwhite"
app:menu="#menu/bottom_nav"/>
</RelativeLayout>
AccountFragmenet:
class AccountFragment : Fragment() {
lateinit var signUpFragment: SignUpFragment;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_forgotPass.setOnClickListener {
Toast.makeText(
activity, "btn forgot pressed",
Toast.LENGTH_SHORT
).show()
}
btn_signIn_google.setOnClickListener {
Toast.makeText(
activity, "btn google pressed.",
Toast.LENGTH_SHORT
).show()
}
btn_login.setOnClickListener {
Toast.makeText(
activity, "btn login pressed.",
Toast.LENGTH_SHORT
).show()
}
/********** The problem is here ****/
btn_signup.setOnClickListener {
signUpFragment = SignUpFragment();
childFragmentManager.beginTransaction().replace(R.id.frame_layout, signUpFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
}
}
}
Errormessage in logcat is :
java.lang.IllegalArgumentException: No view found for id 0x7f0800b7 (no.store.maast:id/frame_layout) for fragment SignUpFragment{b0a086e (7c1e4b61-3b07-48dd-b700-83748b0714c6) id=0x7f0800b7}
you can't access your frame layout in account fragment .
you must replace your fragment with your account fragment root layout .
btn_signup.setOnClickListener {
signUpFragment = SignUpFragment();
childFragmentManager.beginTransaction()
// give an id to your layout root account fragment
.replace(R.id.accountFragmentId, signUpFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit()
}
Using ChildFragmentMAnager did not work on mine application no matter what I did, but i found a useful link that solved my problem:
How do i open fragment from fragment (kotlin)
My working code :
btn_signup.setOnClickListener {
signUpFragment = SignUpFragment();
val transaction = activity?.supportFragmentManager?.beginTransaction()
transaction?.replace(R.id.frame_layout, signUpFragment)
transaction?.addToBackStack("BackToSignInPage")
transaction?.commit()
}
I am trying to use Hilt on a sample project
but when i compiled my project, i have this sample error message :
Cause: com.example.pokemon.ui.Hilt_MainActivity
This is my activity aclass:
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private var binding: ActivityMainBinding? = null
private var isFavoriteListVisible = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding?.root)
supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, Home())
.commit()
binding?.changeFragment
?.setOnClickListener {
if (isFavoriteListVisible) {
isFavoriteListVisible = false
binding?.changeFragment?.text = "Favorites"
supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, Home())
.commit()
} else {
isFavoriteListVisible = true
binding?.changeFragment?.text = "Home"
supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, Favorites())
.commit()
}
}
}
}
What can i do to resolve it or have more details about this issue
I have a fragment class that contains a ViewPager with two sites. These sites contain a few widgets like CheckBoxes and I want them to stay checked when the orientation changes.
MainFragment:
class StatsFragment: Fragment() {
private val fragmentStatsCat = StatsCategoryFragment()
private val fragmentStatsMon = StatsMonthFragment()
private lateinit var pager: ViewPager
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_stats, container, false)
pager = view.findViewById(R.id.stats_container)
val pagerAdapter = StatsScreenSlidePagerAdapter(childFragmentManager)
pager.adapter = pagerAdapter
return view
}
private inner class StatsScreenSlidePagerAdapter(fm: FragmentManager): FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return if (position == 0) {
fragmentStatsCat
} else {
fragmentStatsMon
}
}
override fun getCount(): Int = 2
override fun saveState(): Parcelable? {
return null
}
}
}
One of the ViewPagerFragments:
class StatsMonthFragment: Fragment() {
companion object {
private const val CHECKBOX_KEY = "isChecked"
private const val CATEGORY_KEY = "category"
}
private lateinit var btnPrevCat: ImageButton
private lateinit var btnNextCat: ImageButton
private lateinit var cbAllCats: CheckBox
private lateinit var categories: List<Category>
private var i: Int = 0
private var isCbChecked: Boolean? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState != null) {
i = savedInstanceState.getInt(CATEGORY_KEY)
isCbChecked = savedInstanceState.getBoolean(CHECKBOX_KEY)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_stats_months, container, false)
btnNextCat = view.findViewById(R.id.btn_next_cat)
btnPrevCat = view.findViewById(R.id.btn_prev_cat)
cbAllCats = view.findViewById(R.id.cb_all_cats)
categories = DatabaseInitializer.getInstance().getAllCategories(AppDatabase.getInstance(context).categoryDao())
if(isCbChecked == null || isCbChecked!!) {
val initFragment = StatsSelectedFilterFragment() //This is just a fragment with a TextView; I use fragments for that for the CustomAnimations
initFragment.setText(categories[i].cat_name)
childFragmentManager.beginTransaction().add(R.id.container_stats_categories, initFragment).commit()
} else {
val fragment = StatsSelectedFilterFragment()
fragment.setText(getString(R.string.all_categories))
childFragmentManager.beginTransaction()
.replace(R.id.container_stats_categories, fragment)
.commit()
}
btnNextCat.setOnClickListener {
val fragment = StatsSelectedFilterFragment()
fragment.setText(categories[i].cat_name)
childFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left)
.replace(R.id.container_stats_categories, fragment)
.commit()
}
btnPrevCat.setOnClickListener {
val fragment = StatsSelectedFilterFragment()
fragment.setText(categories[i].cat_name)
childFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
.replace(R.id.container_stats_categories, fragment)
.commit()
}
cbAllCats.setOnCheckedChangeListener{_,checked->
if(checked) {
val fragment = StatsSelectedFilterFragment()
fragment.setText(getString(R.string.all_categories))
childFragmentManager.beginTransaction()
.replace(R.id.container_stats_categories, fragment)
.commit()
} else {
val fragment = StatsSelectedFilterFragment()
fragment.setText(categories[i].cat_name)
childFragmentManager.beginTransaction()
.replace(R.id.container_stats_categories, fragment)
.commit()
}
}
return view
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val isAllCatsChecked = cbAllCats.isChecked
outState.putBoolean(CHECKBOX_KEY, isAllCatsChecked)
outState.putInt(CATEGORY_KEY, i)
}
}
Now on every screen rotation, the fragment gets recreated twice, first with savedInstanceState != null and then with savedInstanceState == null, which means that I don't have access to the old settings in the Fragments. As I know, it is because the fragment gets recreated, and then the MainFragment containing the viewpager gets also recreated, which means that the fragments within the viewPager get created a second time. I have tried something like this in the MainFragment:
private lateinit var fragmentStatsCat: StatsCategoryFragment
private lateinit var fragmentStatsMon: StatsMonthFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState == null) {
fragmentStatsMon = StatsMonthFragment()
fragmentStatsCat = StatsCategoryFragment()
} else {
//Finding old fragments
}
}
Maybe this would solve the problem (I am not sure), but I don't know how I can find the fragments, as they are inside a ViewPager.
EDIT
I have now found a solution to find the fragments when the activity is recreated:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState == null)
{
fragmentStatsMon = StatsMonthFragment()
fragmentStatsCat = StatsCategoryFragment()
} else {
fragmentStatsMon = childFragmentManager.getFragment(savedInstanceState, FRAG_MONTH_TAG) as StatsMonthFragment
fragmentStatsCat = childFragmentManager.getFragment(savedInstanceState, FRAG_CAT_TAG) as StatsCategoryFragment
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
childFragmentManager.putFragment(outState, FRAG_CAT_TAG, fragmentStatsCat)
childFragmentManager.putFragment(outState, FRAG_MONTH_TAG, fragmentStatsMon)
}
This puts a reference in the Bundle with which I can get the fragment afterwards. But now, I have another problem. When the activity is recreated, it throws the following exception:
java.lang.IllegalStateException: Fragment already added: StatsCategoryFragment{f06ec65 (a1991f39-3af4-4a74-9aeb-79274beae04a) id=0x7f09014a}
I don't know how to solve that because I don't really manually add the fragments. Also, I don't really get it because the viewpager and the adapter also get recreated, don't they? So why are the fragments still attached to the adapter?
I also don't know in which line of code this happens, I could neither find it out in the logcat nor by debugging.
Put this line in AndroidManifest.xml in the entry of your Activity in which you are loading your Fragments:
android:configChanges="layoutDirection|keyboardHidden|orientation|screenSize"
By this, Data will not be reset when your Fragment is recreated by any condition.
Example:
<activity
android:name=".activity.MainActivity"
android:configChanges="layoutDirection|keyboardHidden|orientation|screenSize"
android:label="#string/app_name"></activity>
Check out this similar question
https://www.google.com/url?sa=t&source=web&rct=j&url=https://stackoverflow.com/questions/15313598/once-for-all-how-to-correctly-save-instance-state-of-fragments-in-back-stack&ved=2ahUKEwja6rasobzjAhVh1uAKHRakDXIQFjAAegQIBhAB&usg=AOvVaw0Hg_vwSnBayxV1EJfKOUZk
I have an Activity with a ViewPager containing Fragments that are summoned with custom buttons. Currently, my custom adapter's getItem method is being called, but the pager is going blank rather than going to the new fragment
Here is my adapter :
class ScreenSlidePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) {
private val NUM_FRAGMENTS = 2
override fun getCount(): Int = NUM_FRAGMENTS
override fun getItem(position: Int): Fragment {
var fragment: Fragment = WelcomeFragment.newInstance()
when (position) {
0 -> fragment = WelcomeFragment.newInstance()
1 -> fragment = LanguageSelectFragment.newInstance()
}
println(position)
return fragment
}
}
Here is the method that gets called on button click. The views are given a tag corresponding to their Fragment's position in the adapter, and this is retrieved on click to pull up the right fragment.
private fun switchScreens(view: View) {
val fragment = mPagerAdapter.getItem(view.getTag().toString().toInt())
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.main_view_pager, fragment)
fragmentTransaction.commit()
}
And my fragment:
class LanguageSelectFragment : Fragment() {
private var listener: OnFragmentInteractionListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_language_select, container, false)
}
fun onButtonPressed(uri: Uri) {
listener?.onFragmentInteraction(uri)
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnFragmentInteractionListener) {
listener = context
} else {
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
interface OnFragmentInteractionListener {
fun onFragmentInteraction(uri: Uri)
}
companion object {
#JvmStatic
fun newInstance() =
LanguageSelectFragment().apply {
arguments = Bundle().apply {
}
}
}
}
Do you have a ViewPager container for your framgents ?
If yes, that's not the way to use the pagerAdapter. You should see this link. It's in Java but you can easily adapt it to kotlin.
If no, I'm not sure what you are trying to do but I think the
ViewPager is what you need.
If you just want to switch fragments on a click, just use a FrameLayout to display the fragment you want and switch between them with transactions. I'm not familiar with kotlin but something like this should work.
private fun switchScreens(i: Integer) {
val fragment;
when (i) {
0 -> fragment = WelcomeFragment.newInstance()
1 -> fragment = LanguageSelectFragment.newInstance()
}
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.main_view_pager, fragment)
fragmentTransaction.commit()
}
If you want to slide to one fragment to another, you can use a ViewPager (see the link above).
Best
I would like to create an recyclerView in a fragment, but it shows an error " java.lang.IllegalStateException: recylerView_Main must not be null
at com.gph.bottomnavigation.FragmentMe.onCreateView(FragmentMe.kt:28)"
Question 1) Please kindly help to solve this issue.
Question 2) I created an recyclerView only in a empty project without any fragment, it is working properly.
But the same code is no working in Fragment, it shows error so I change "recylerView_Main.layoutManager = LinearLayoutManager(this)" to "recylerView_Main.layoutManager = LinearLayoutManager(context)"
It shows no error and I can run in simlulator, but when I click the navigation button of the Fragment, the app stops and show this error. Please kindly help to solve it.
Here with the code for FragmentMe.kt:
class FragmentMe : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
recylerView_Main.layoutManager = LinearLayoutManager(context)
recylerView_Main.adapter = Mainadapter()
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_me, container, false)
}
}
Here with the code of MainActivity.kt:
class MainActivity : AppCompatActivity() {
val manager = supportFragmentManager
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
//message.setText(R.string.title_home)
createFragmentQpon()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
//message.setText(R.string.title_dashboard)
createFragmentMe()
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
//message.setText(R.string.title_notifications)
createFragmentTools()
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Action Bar
val actionBar = supportActionBar
actionBar!!.setDisplayShowHomeEnabled(true)
actionBar.setBackgroundDrawable(ColorDrawable(Color.parseColor("#00FFFFFF")))
actionBar.setIcon(R.drawable.ic_home_black_24dp)
actionBar.setDisplayShowTitleEnabled(false)
createFragmentQpon()
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
fun createFragmentQpon() {
val transaction = manager.beginTransaction()
val fragment = FragmentQpon()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
fun createFragmentMe() {
val transaction = manager.beginTransaction()
val fragment = FragmentMe()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
fun createFragmentTools() {
val transaction = manager.beginTransaction()
val fragment = FragmentTools()
transaction.replace(R.id.fragmentholder,fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
Here with the code of Mainadapter.kt:
class Mainadapter: RecyclerView.Adapter<CustomViewHolder>() {
val videolist = listOf("aaa","bbbb","cccc")
override fun getItemCount(): Int {
return 3
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.tutorial_layout, parent, false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
var videoName = videolist.get(position)
holder.itemView.title.text = videoName
}
}
class CustomViewHolder(v: View): RecyclerView.ViewHolder(v) {
}
Move this code
recylerView_Main.layoutManager = LinearLayoutManager(context)
recylerView_Main.adapter = Mainadapter()
from onCreateView to onActivityCreated
override onActivityCreated and place the above code.
There are two things incorrect in your code :
You are trying to access recyclerView even before inflating the View.
The context of a Fragment is null in onCreateView and is usable in between onAttach and onDetach
recylerView_Main.layoutManager = LinearLayoutManager(this.context)
Try this out, worked fine for me.