Fragments and Activities using Kotlin - android

I have the following activity with two integers
class ComplexActivity : AppCompatActivity() {
var clubs : Int = 0
var diamonds : Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_complex)
val fragment = ClubsFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.main_frame, fragment)
transaction.commit()
}
}
I want to change the value of the integer clubs from the fragment ClubsFragment when isScored is true
class ClubsFragment : Fragment(), SeekBar.OnSeekBarChangeListener{
private var isScored = false
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val v = inflater!!.inflate(R.layout.fragment_clubs, container, false)
v.image_clubs.setOnClickListener {
if(isScored){
activity.clubs = 4
}
}
}
}
I tried to use activity.clubs but It's not working. How can I access the activity constants from a fragment.

You would create an interface, let's say FragmentListener for your Activity that contains a function like fun updateClubs(count: Int). Your Activity should implement this interface.
Then, in your Fragment, add a fragmentListener property and override onAttach(context: Context):
private var fragmentListener: FragmentListener? = null
override fun onAttach(context: Context) {
this.listener = context as? FragmentListener
}
Then, in your OnClickListener, you can simply call fragmentListener?.updateClubs(4).

Related

End activity from fragment

I'm trying to implement an onboard activity which shows only for the first time of using the app,
after it finished it will move to the main activity.
I've made another activity which includes fragments,
afterwards I'd like to set a settings on shared-preferences which hold a value that tells if it first time or not.
onboard activity:
class OnboardActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_onboard)
}
fun finishOnBoarding() {
val preferences = getSharedPreferences("my_preferences", MODE_PRIVATE)
preferences.edit()
.putBoolean("onboarding_complete", true).apply()
val main = Intent(this, MainActivity::class.java)
startActivity(main)
finish()
}
}
InitialScreenFragment which is the last fragment in my code:
class InitialScreenFragment : Fragment() {
private var _binding: FragmentInitialScreenBinding? = null
private val binding get() = _binding!!
lateinit var callback: FragmentActivity
override fun onAttach(context: Context) {
super.onAttach(context)
callback = requireActivity();
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentInitialScreenBinding.inflate(inflater, container, false)
val view = binding.root
//finish button which suppose to end the session of boarding
// binding.finishBtn.setOnClickListener{
// callback.finishOnBoarding()
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
how can I set the listener on finish button to call my activity method to end the onboard please?
thanks
You may use separate interface as callback
interface OnboardingListener {
fun finishOnboarding()
}
implement this interface in your OnboardActivity
class OnboardActivity : AppCompatActivity(), OnboardingListener {
override fun finishOnboarding(){
val preferences = getSharedPreferences("my_preferences", MODE_PRIVATE)
preferences.edit().putBoolean("onboarding_complete", true).apply()
val main = Intent(this, MainActivity::class.java)
startActivity(main)
finish()
}
}
and for example set it to fragment from your activity
class InitialScreenFragment : Fragment() {
.....
var callback: OnboardingListener? = null
.....
}
val fragment: InitialScreenFragment = InitialScreenFragment()
fragment.callback = this // this should be in OnboardActivity where you create an instance of InitialScreenFragment
and then from your fragment call
callback?. finishOnboarding()

Need Help in Kotlin Android - fragments

I need to get session id to menu fragment. but it doesn't work. In this I get Session ID from Login Activity. Now I need to get that id to Menu fragment. In this one activity loaded five Fragments. I used bundle for load data.
Here's my activity.kt
class Home : AppCompatActivity() {
private val HomeFragment = HomeFragment()
private val CreditsFragment = CreditsFragment()
private val BusFragment = BusFragment()
private val NotificationsFragment = NotificationsFragment()
private val MenuFragment = MenuFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.home)
val sessionId = intent.getStringExtra("EXTRA_SESSION_ID")
textView4.setText(sessionId)
replaceFragment(HomeFragment)
bottomNavBar.setOnNavigationItemSelectedListener{
when (it.itemId){
R.id.menu_home -> replaceFragment(HomeFragment)
R.id.menu_credits -> replaceFragment(CreditsFragment)
R.id.menu_bus -> replaceFragment(BusFragment)
R.id.menu_notification -> replaceFragment(NotificationsFragment)
R.id.menu_menu -> replaceFragment(MenuFragment)
}
true
}
val bundle = Bundle()
bundle.putString("EXTRA_SESSION_ID", sessionId)
val myObj = MenuFragment()
myObj.setArguments(bundle)
}
private fun replaceFragment (fragment:Fragment){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
here's my updated menu fragment.kt. I think I have problem in this. can u plzzz help me to find it.
class MenuFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
var sessionId = it.toString()
emailAddressText.setText(sessionId)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_menu, container, false)
}
companion object {
}
}
change activity to this :
val bundle = Bundle()
bundle.putString("EXTRA_SESSION_ID", sessionId)
MenuFragment.setArguments(bundle)
bottomNavBar.setOnNavigationItemSelectedListener{
when (it.itemId){
R.id.menu_home -> replaceFragment(HomeFragment)
R.id.menu_credits -> replaceFragment(CreditsFragment)
R.id.menu_bus -> replaceFragment(BusFragment)
R.id.menu_notification -> replaceFragment(NotificationsFragment)
R.id.menu_menu -> replaceFragment(MenuFragment)
}
true
}
in the fragment you also need to use arguments.getString("EXTRA_SESSION_ID") to retreive sessionId value. and place emailAddressText.setText(sessionId)
in onViewCreated method because in onCreate() method views are not ready yet.
so change the fragment like this :
class MenuFragment : Fragment() {
var sessionId = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
sessionId = it.getString("EXTRA_SESSION_ID","")
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_menu, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
emailAddressText.setText(sessionId)
}
}
The MenuFragment instance you have in your replaceFragment transaction is different from the one where you set the sessionid argument to.
Remove the other instantiation val myObj = MenuFragment() and set arguments on MenuFragment instead.
(Naming convention hint: it's easier to keep types and objects apart if types are CamelCase and types lowerCamelCase. For example, private val menuFragment = MenuFragment().)

Why is my Viewpager not showing any fragments after opening the second time and why can childfragmentmanager not be used?

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){
}
}

Passing a value from Activity to Fragment in Kotlin

I created a bottom navigation activity in my project, which contains one activity and two fragments. In Main Activity I have value stored in a variable but if I pass the value to the fragments then I am getting NullPointer Exception error. I am using kotlin in my project and any help is appreciated.
Expectation
Get Value into Fragment from MainActivity. MainActivity--->TestOneFragment
Language Used
Kotlin
Main Activity
class Test : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener
{
private val KEY_POSITION = "keyPosition"
private var navPosition: BottomNavigationPosition = BottomNavigationPosition.ONE
private lateinit var toolbar: Toolbar
private lateinit var bottomNavigation: BottomNavigationView
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
restoreSaveInstanceState(savedInstanceState)
setContentView(R.layout.activity_test)
toolbar = findViewById(R.id.toolbar)
bottomNavigation = findViewById(R.id.bottom_navigation)
setSupportActionBar(toolbar)
initBottomNavigation()
initFragment(savedInstanceState)
var Name:String=intent.getStringExtra("name")
println("Test CLLicked: $Name")
//This code is to pass the value to Fragment
var bundle=Bundle()
bundle.putString("name",Name)
var frag=TestFragment()
frag.arguments=bundle
}
override fun onSaveInstanceState(outState: Bundle?)
{
outState?.putInt(KEY_POSITION, navPosition.id)
super.onSaveInstanceState(outState)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean
{
navPosition = findNavigationPositionById(item.itemId)
return switchFragment(navPosition)
}
private fun restoreSaveInstanceState(savedInstanceState: Bundle?)
{
savedInstanceState?.also {
val id = it.getInt(KEY_POSITION, BottomNavigationPosition.ONE.id)
navPosition = findNavigationPositionById(id)
}
}
private fun initBottomNavigation()
{
bottomNavigation.active(navPosition.position)
bottomNavigation.setOnNavigationItemSelectedListener(this)
}
private fun initFragment(savedInstanceState: Bundle?)
{
savedInstanceState ?: switchFragment(BottomNavigationPosition.ONE)
private fun switchFragment(navPosition: BottomNavigationPosition): Boolean {
return supportFragmentManager.findFragment(navPosition).let {
if (it.isAdded) return false
supportFragmentManager.detach() // Extension function
supportFragmentManager.attach(it, navPosition.getTag()) // Extension function
supportFragmentManager.executePendingTransactions()
}
}
private fun FragmentManager.findFragment(position: BottomNavigationPosition): Fragment
{
return findFragmentByTag(position.getTag()) ?: position.createFragment()
}
}
TestOneFragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
val testName= arguments!!.getString("name")
....
}
Error
kotlin.KotlinNullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
Here is an example of the newInstance pattern for creating Fragments.
This is within a companion object, which is pretty much just a way to say "these things are Static."
First, you should define constants for your Bundle names, this will help keep everything aligned. Next, define a newInstance method that takes your parameters, such as the name.
And within there, you will create your Fragment and return it. This way, your Activity doesn't have to worry about the Bundle or anything. All your logic is within one place, for storing/retrieving, all within your Fragment.
class TestOneFragment {
companion object {
const val ARG_NAME = "name"
fun newInstance(name: String): TestOneFragment {
val fragment = TestOneFragment()
val bundle = Bundle().apply {
putString(ARG_NAME, name)
}
fragment.arguments = bundle
return fragment
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val name = arguments?.getString(ARG_NAME)
// ...
}
}
And now, you can easily get your Fragment by doing the following.
class Test : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
// ...
val name = intent.getStringExtra("name")
// Creating the new Fragment with the name passed in.
val fragment = TestFragment.newInstance(name)
}
}
Hopefully that helps!

dialog containing ViewPager

I want to create a dialog which contain's ViewPager inside it which have 3 pages and all pages have different layout structure. I want a solution by that i can set the layout content programmatically . I think this can be done by making fragments for each page but i don't know how to do this.
I go through these answers but i am not getting idea how to use them in my case.
Viewpager in Dialog?
ViewPager in Custom Dialog
ViewPager in simple Dialog
You can try and build your custom dialog through DialogFragment. Consider the XML layout would contain a ViewPager and the code to go about would be:
class PagerDialog : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.element_fragment_pager_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupPager()
}
private fun setupPager() {
val pagerFragment1 = PagerFragment1.newInstance()
val pagerFragment2 = PagerFragment2.newInstance()
val pagerFragment3 = PagerFragment3.newInstance()
viewPager?.adapter = MyFragmentPagerAdapter(childFragmentManager).apply {
adapterReference = object : PageAdapterInterface {
override var fragmentList: List<Fragment> =
mutableListOf(pagerFragment1, pagerFragment2, pagerFragment3)
}
}
}
companion object {
const val tag = "PagerDialog"
}
}
I have used reference to the list because it might cause leaks when not handled correctly. So the PagerAdapterInterface would look like:
interface PageAdapterInterface {
var fragmentList: List<Fragment>
fun getItemCount() = fragmentList.size
#Throws(StackOverflowError::class)
fun getItemAt(index: Int) : Fragment {
if (index >= fragmentList.size) throw StackOverflowError()
return fragmentList[index]
}
}
Your view pager adapter can make use of this reference in manner that is accessing referentially like:
class MyFragmentPagerAdapter(childFragmentManager: FragmentManager) : FragmentStatePagerAdapter(childFragmentManager){
lateinit var adapterReference: PageAdapterInterface
override fun getItem(p0: Int): Fragment = adapterReference.getItemAt(p0)
override fun getCount(): Int = adapterReference.getItemCount()
}
Finally in your Activity or Fragment on create() or onViewCreated() functions respectively, you can initialize the dialog as shown:
class MyActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// use childFragmentManager if the code is
// used within the Fragment
val prev = supportFragmentManager.findFragmentByTag(PagerDialog.tag)
if (prev != null) {
supportFragmentManager.beginTransaction()
.remove(prev)
.addToBackStack(null)
.commit()
}
PagerDialog().show(supportFragmentManager, PagerDialog.tag)
}
}
Note: DialogFragment is deprecated on > API 28 check out https://developer.android.com/reference/android/app/DialogFragment

Categories

Resources