I have an Activity -> MasterActivity and one more Activity ChildActivity which extends from Master Activity
Now the author of Master activity is expecting some values to come with the intent and parses these in the onCreate of Master activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view)
origUrl = intent.getStringExtra(EXTRA_URL)
if (origUrl == null) throw RuntimeException("did you forget to put extras?")
}
And I start the intent to my ChildActivity like:
context?.startActivity(Intent(context, TerminationWebview::class.java).apply {
putExtra(ChildActivity.EXTRA_URL, it)
Now the question is how do I pass this data to parent? So that parent can parse it in onCreate?
This question does not deal with returning data from an activity, it deals with passing the data to parent activity through intent and hence is not . a duplicate
You could do this by getting your intent data directly from the MasterActivity#onCreate
You might also want to change the tag to kotlin instead of java.
Related
this is what I want to achieve:
I'm in Activity A with couple options to choose from (buttons)
after clicking any of them, I want to be taken to Activity B
Activity B should contain a constant part (audio) and a Fragment with image and text, depending on the button you choose in Activity A.
Is it achievable? I tried to both startActivity and getSupportFragmentManager (etc.) as my onClick method but with no use, maybe there's another way?
In activity B create a method (static in java, companion in kotlin) to create an intent:
companion object {
const val ARG_FRAGMENT_TO_LOAD = "argFragment"
fun newIntent(context:Context, fragmentTagToLoad:String): Intent {
return Intent(context, ActivityB::class.java).apply {
putString(ARG_FRAGMENT_TO_LOAD, fragmentTagToLoad)
})
}
}
Then, in activity A you can use this intent to start activity B.
myButton?.setOnClickListener {
startActivity(ActivityB.newIntent(this, SomeFragmentToLoad.ARG_TAG))
}
Then, again in activity B you will receive this argument in the intent, so that you can handle it. It is typically done inside onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val fragmentTagToLoad = intent.getStringExtra(ARG_FRAGMENT_TO_LOAD) ?: ""
if(fragmentToLoad.isNotEmpty() {
// Use fragment manager to load fragment into container.
}
...
}
Finally, in activity B you can use the argument to load the desired fragment using a (usually support) fragment manager.
Of course you should define ARG_TAG as a String constant inside SomeFragmentToLoad.
I have one fragment page in android, the first time I open it, I get data from bundle and show. Now, when I go to web page from this page and come back by deep-link, I lose my current data
(in other words, I have to go to the web page for payment, after that, I have to come back to the current fragment by deep link without losing current data)
Recently, I've been faced with a the same problem. I'm still a bit newbie in Android, so it's possibly not the best way to do it, but it does work.
First, in the Android Manifest, we have to add the attribute android:launchMode="singleTop" to the Activity that's hosting the Fragment. This way, when we go back to the app via deep link, the old Activity will receive the Intent instead of creating a new instance.
Then, in the code of the hosting Activity, we use the onNewIntent() method to look for the existing attached Fragment. As Hossein Kurd pointed out in his answer, this step depends on how we are managing our Fragments. In my case, I'm using a NavHostFragment, so first I have to find that and then I can search for the Fragment I want to come back to (in my case, an instance of LoginFragment):
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null && intent.action == Intent.ACTION_VIEW && intent.data != null) {
val navHostFragment = supportFragmentManager.fragments.find { fragment -> fragment is NavHostFragment }
navHostFragment?.let { navHostFrag ->
val loginFragment = navHostFrag.childFragmentManager.fragments.find { childFragment -> childFragment is LoginFragment }
loginFragment?.let { loginFrag ->
val bundle = bundleOf("uri" to intent.data)
loginFrag.arguments = bundle
val fragmentTransaction = loginFrag.parentFragmentManager.beginTransaction()
fragmentTransaction.show(loginFrag).commit()
}
}
}
}
This method will receive the Intent from the browser, with the corresponding data (in my case, the Uri). We wrap the data in a Bundle and pass that to the Fragment's arguments and then show() the Fragment.
Finally, in the onResume() method of the Fragment, we check if the arguments include the data we are expecting, then we can go on with our logic:
override fun onResume() {
super.onResume()
this.arguments?.get("uri")?.let { data ->
// Your logic with the data you received from the browser
}
}
You can use getInstance static method, Better way is update fragment from activity
and the solution depends on the way you call your fragments: SupportFragmentManager, NavController ...
get current fragment by view Id or ...
Kotlin:
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { fragment ->
// fragment.onActivityResult(requestCode, resultCode, boundle)
fragment.method(param1, param2)
}
Is there any better way to send back the data to the previous fragment/parent fragment other than listener?
I have a fragment which consists of list of items. Clicking on the items will open a bottom sheet fragment. While closing the bottom sheet popup I need to pass data back to the fragment itself.
What I have done so far is created a listener and implemented it.
It really depends on what components you're using. If you are using Android Jetpack components then check out this article: LINK
You should be able to pass data back and forth similar to passing data with startActivityForResult()
Also, while you're at it please check out the official documentation too, there's a good example that will help you understand this better: LINK
Although you mention any way other than listener, but according to
documents:
Starting with Fragment 1.3.0-alpha04, each FragmentManager
implements FragmentResultOwner. This means that a FragmentManager can
act as a central store for fragment results. This change allows
components to communicate with each other by setting fragment results
and listening for those results...
Sets the FragmentResultListener for a given requestKey. Once the given
LifecycleOwner is at least in the STARTED state, any results set by
setFragmentResult using the same requestKey will be delivered to the
callback. The callback will remain active until the LifecycleOwner
reaches the DESTROYED state or clearFragmentResultListener is called
with the same requestKey.
To pass data back to fragment A from fragment B, first set a result listener on fragment A, the fragment that receives the result. Call setFragmentResultListener() on fragment A's FragmentManager, as shown in below:
in your BottomSheet Class:
btncloseBottomSheet.setOnClickListener {
val result = Bundle().apply {
// put your data in bundle
putInt("MY_KEY", 6)
}
setFragmentResult("requestCode", result)
dismiss()
}
in your previous fragment/parent fragment, you need to implement FragmentResultListener:
class PreviousFragment : FragmentResultListener {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set fragment listener
parentFragmentManager.setFragmentResultListener(
"requestCode",
viewLifecycleOwner,
this
)
}
...
// get result from other fragments by FragmentResultListener
override fun onFragmentResult(requestKey: String, result: Bundle) {
when (requestKey) {
"requestCode" -> {
val resultFromBundle = result.getInt("MY_KEY")
// Do somthing
}
}
}
}
I am trying to call a new activity from my current one dynamically. So I created an object class that taking activity, class, and bundle as optional.
object ActivityHelper {
fun start(context: Context, activity: Class<out BaseCompatActivity>, extras: Bundle? = null) {
val intent = Intent(context, activity)
extras?.let {
intent.putExtras(extras)
} ?: run {
intent.putExtra("flag", context.javaClass.getSimpleName())
}
context.startActivity(intent)
}
}
after that, I am calling this from all activities like this
ActivityHelper.start(this, Activity::class.java, extras)
But I noticed it makes the app loading time slower than before. Am I doing it correctly? or is it bad idea to start activity like this?
On the activity you are trying to load, hide views in layout by default. onResume you can delay and then set the view visibility to true. If your UI layout is complex or you are using some library for anim, it would take more time to start.
I am working on an android app developed in kotlin that has two activities. Being that activity 1 is the main activity and when an specific layout is clicked activity 2, which is a dialog activity is started.
The problem I am having is that each time I click the specific layout on activity 1, the onCreate method on activity 2 is invoked (number of times it was invoked before) + 1 times. Meaning that the first time I click the layout are, the onCreate method is called one time. Then I close activity 2 with finish() and if I click the layout area again, the oncreate method of activity 2 is called two times. If I do that again, 3 times, and then 4, 5, etc.
Activity one goes like the below, where I add the onClick listener to layout area:
class ActivityOne : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_one)
Timber.plant(Timber.debugTree())
layoutAddNewProject.setOnClickListener(this)
}
override fun onClick(view: View?){
Timber.d("Click on view ${view.toString()}")
if (view?.id == layoutAddNewProject.id){
val intent = Intent(this, ActivityTwo::class.java)
startActivity(intent)
}
}
}
Then activity two goes like the below. Note that it has a button which when clicked closes the activity:
class ActivityTwo : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstance)
setContentView(R.layout.activity_two)
setFinishOnTouchOutside(false)
Timber.plant(Timber.DebugTree())
Timber.d("Starting activity two")
btnCancel.setOnClickListener(this)
}
override fun onClick(view: View?){
if (view?.id == btnCancel.id){
Timber.d("Cancel pressed")
finish()
}
}
}
The names of the activities, layout, etc are fictitious, but the code is inline with my real code.
So, the way I figure out onCreate on activity two is being called multiple times, is because I see the log message "Starting activity two" multiple times.
Note also, that when btnCancel is clicked on activity two, I also get the log message "Cancel pressed" multiple times (as many times as the number of times of the onCreate method being invoked).
Your help as always, is much appreciated.
Thank you.
When you call Timber#plant it adds new tree to the list. And then on every Timber#d it send message to all trees in that list. So to fix that, you need to call plant just once in your application class