Need Help in Kotlin Android - fragments - android

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().)

Related

Fragments are getting destroyed when back pressed. I want it to be not destroy when back press so that I can save the states of fragments

I created an Activity and added 3 fragments into it. One is HomeFragment, second is ProductDetailFragment, and third is CartFragment.
I have added multiple quantities of a product from ProductDetailFragment and I want the quantity detail to be saved even if I am back to HomeFragment and again coming back to ProductDetailFragment. But as soon as I press back to go to HomeFragment, ProductDetailFragment is destroyed and if coming again to ProductDetailFragment quantity values are all wiped out.
I have tried onSaveInstanceState to store the state even if fragment is destroyed but not working in this case. Lemme show you my code:
ProductDetailFragment
lateinit var binding: FragmentProductDetailBinding
var quantityValue = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Toast.makeText(activity,"onCreateCalled",Toast.LENGTH_SHORT).show()
if(savedInstanceState == null) {
quantityValue = 0
} else {
quantityValue = savedInstanceState.getInt("quantity",0)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
/// return inflater.inflate(R.layout.fragment_product_detail, container, false)
binding = FragmentProductDetailBinding.inflate(inflater,container,false)
binding.productDetails1 = arguments?.getParcelable("product")
var productDetail = binding.productDetails1
// var priceAmount = binding.priceAmount.text.toString().toFloat()
Log.d("PriceAmount", binding.priceAmount.text.toString())
// Log.d("ProductTitle", binding.productDetails1.title)
binding.addButton.setOnClickListener {
// Log.d("ProductDetail",initialValue.toString())
if (productDetail == null) return#setOnClickListener
quantityValue = quantityValue + 1
binding.totalPriceText.text = "Total price"
binding.quantityText.text = quantityValue.toString()
binding.priceAmount.text = (productDetail.price * quantityValue).toString()
// Log.d("ProductDetail",priceAmount.toString())
}
binding.removeButton.setOnClickListener {
if (quantityValue != 0) {
if (productDetail == null) return#setOnClickListener
quantityValue = quantityValue - 1
binding.quantityText.text = quantityValue.toString()
// binding.priceAmount.text = (priceAmount*quantityValue).toString()
binding.priceAmount.text = (productDetail.price * quantityValue).toString()
}
}
binding.addCartButton.setOnClickListener{
val bundle = Bundle()
bundle.putString("quantity_text", String.format("%d",quantityValue))
val cartFragment = CartFragment()
cartFragment.arguments = bundle
val ft = fragmentManager?.beginTransaction()
ft?.replace(R.id.flFragment,cartFragment)
ft?.addToBackStack("")
ft?.commit()
// Create the transaction
// Create the transaction
}
Toast.makeText(activity,"onCreateViewCalled",Toast.LENGTH_SHORT).show()
return binding.root
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("quantity", quantityValue)
}
HomeFragment
class HomeFragment : Fragment() {
lateinit var binding: FragmentHomeBinding
private val viewModel: HomeFragmentViewModel by lazy {
ViewModelProvider(this).get(HomeFragmentViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentHomeBinding.inflate(inflater,container,false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
val clickListener = object: ClickListener{
override fun onClick(itemProduct: ProductDataModel) {
val productDetailFragment = ProductDetailFragment().apply {
arguments = bundleOf(Pair("product",itemProduct))
}
val ft : FragmentTransaction = fragmentManager!!.beginTransaction()
ft.replace(R.id.flFragment, productDetailFragment,"PRODUCT_DETAIL_FRAGMENT")
ft.addToBackStack(null)
ft.commit()
}
}
viewModel.products.observe(viewLifecycleOwner){
if (it.size>0){
//Log.d("HomeFragment",it.get(0).title)
//Log.d("HomeFragment", it.get(0).image)
binding.productRecyclerView.adapter = ProductListAdapter(it,clickListener)
}
}
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity?)!!.supportActionBar!!.hide()
}
override fun onStop() {
super.onStop()
(activity as AppCompatActivity?)!!.supportActionBar!!.show()
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(activity,"onDestroyHomeFrag",Toast.LENGTH_SHORT).show()
}
}
MainActivity ( In which all fragments are there )
class MainActivity : AppCompatActivity() {
private val viewModel:MainViewModel by viewModels()
`enter code here`var productDetailFragment = ProductDetailFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().apply {
setKeepOnScreenCondition{
viewModel.isLoading.value
}
}
setContentView(R.layout.activity_main)
val homeFragment = HomeFragment()
//Starting the fragment transaction from SupportFragmentManager class
val ft : FragmentTransaction = supportFragmentManager.beginTransaction()
ft.add(R.id.flFragment, homeFragment,"HOME_FRAGMENT")
ft.addToBackStack(null)
ft.commit()
enter code here}
}
You can use SharedViewModel since both fragments share same activity. You can add data that you want to preserve and when you go to the detail page view model still has the data you want because it depends on activity' lifecycle.
Offical doc:
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

passing data from activity to fragment in android studio kotlin(not duplicate, those method is not working)

I am having an activity called Image activity inside it there are 2 fragments called image_display and image_change fragment, for image_activity I am getting data from an adapter(using getIntent), and it's working fine up to here. so I want to send the data
(i.e URL) to the fragment but my method is not working.
my image_activity code is like this
class ImageActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image)
var gi = getIntent()
var url = gi.getStringExtra("url")
val fragment = FragmentDisplayImage.newInstance(url.toString())
Toast.makeText(this,url , Toast.LENGTH_SHORT).show()
var manager =supportFragmentManager
manager.beginTransaction().commit()
}
}
and my FramentImage.kt is like
private const val ARG_PARAM1 = "url"
class FragmentDisplayImage : Fragment() {
private var param1: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString("url")
var image= view?.findViewById<ImageView>(R.id.Fragimage)
Picasso.get().load(param1).resize(700, 700).centerCrop().into(image);
Toast.makeText(context, param1.toString() , Toast.LENGTH_SHORT).show()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_display_image, container, false)
}
companion object {
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String) =
FragmentDisplayImage().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
}
but still, I can't get my url here on the adapter
I think so you're not starting your fragment.
You have just created fragment object but never opened it using manager.
Please updated your ImageActivity code as follow:
var gi = getIntent()
var url = gi.getStringExtra("url")
val fragment = FragmentDisplayImage.newInstance(url.toString())
val manager: FragmentManager = supportFragmentManager
val transaction: FragmentTransaction = manager.beginTransaction()
transaction.replace(R.id.container, fragment)
transaction.addToBackStack(null)
transaction.commit()
where R.id.container will be Layout inside activity_image where you want to put your fragment.
your activity_image should have
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>

Pass data between two fragments by bundle kotlin

I have two fragments and want to open second fragment (WordFragment) when pressing button of the first one(SearchFragment) by Bundle. But Fragment shwos default data instead of the passed one.
ClickListener in First Fragment:
searchDefAdapter = SearchDefAdapter(
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
val bundle = Bundle()
val arr = arrayOf("word", "слово", "I give you my word")
bundle.putStringArray(INFO_BUNDLE_ID, arr)
val wordFragment = WordFragment()
wordFragment.arguments = bundle
parentFragmentManager.beginTransaction().apply {
replace(R.id.searchFragment, WordFragment())
commit()
}
}
},
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
//viewModel.saveWord(position)
}
}
)
Second Fragment:
class WordFragment : Fragment() {
private var _binding: FragmentWordBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWordBinding.inflate(inflater, container, false)
val view = binding.root
arguments?.let {
val translation = it.getStringArray(INFO_BUNDLE_ID)
if (translation != null){
binding.wordTitleTv.text = translation[0]
binding.translationTv.text = translation[1]
binding.exampleTv.text = translation[2]
}
}
return view
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
this is because you create new WordFragment here replace(R.id.searchFragment, WordFragment()) instead of putting the one you added the bundle to it.

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

Bundle is nullable even though I haven't declared it as such

class UserFragment : Fragment(), View.OnClickListener {
private val userBinding: FragmentUserBinding by onCreateView<Fragment, FragmentUserBinding>(R.layout.fragment_user)
val user: ObservableField<String> = ObservableField()
private var bundle = Bundle()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
userBinding.main = this
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = userBinding.root!!
override fun onClick(p0: View?) {
runAnimation(500L, Techniques.RubberBand, p0)
Handler().postDelayed({
saveUserAndStartLetterFragment()
}, 700L)
}
private fun saveUserAndStartLetterFragment() {
var fragment = WordpackChooserFragment()
bundle.putString("User",user.get())
fragment.arguments = bundle
activity!!.supportFragmentManager.replaceFragment(fragment, activity!!.findViewById(R.id.flContainer))
}
}
In this fragment I declare the bundle. I do not specify it as nullable.
class WordpackChooserFragment : Fragment(), View.OnClickListener {
private val wordpackChooserBinding: FragmentWordpackChooserBinding by onCreateView<Fragment, FragmentWordpackChooserBinding>(R.layout.fragment_wordpack_chooser)
private var bundle: Bundle = Bundle()
private lateinit var fragment: Fragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
wordpackChooserBinding.main = this
setRecycler()
bundle = arguments
return wordpackChooserBinding.root
}
override fun onClick(v: View?) {
fragment = RoundsChooserFragment()
bundle.putStringArrayList("Wordpack", selectedPack)
fragment.arguments = bundle
activity!!.supportFragmentManager.replaceFragment(fragment, activity!!.findViewById(R.id.flContainer))
}
private fun setRecycler() {
wordpackChooserBinding.root.btn_recycler.layoutManager = LinearLayoutManager(this.context)
wordpackChooserBinding.root.btn_recycler.adapter = BtnAdapter(this)
wordpackChooserBinding.root.btn_recycler.adapter.notifyDataSetChanged()
}
}
In this fragment this line bundle = arguments errors with the following:
Type mismatch.
Required: Bundle
Found: Bundle?
I can use the non-null assertion operator '!!' but It seems like a hack.
arguments is nullable (note the "if any" in the description of the link), therefore you can not simply assign it to a non-nullable Bundle. You would either handle the case of it being null (using an if), using !! or you could write something like this:
arguments?.let { bundle = it }
However, I'd say its preferred to use let.

Categories

Resources