I have and "edit" activity and corresponding "edit" fragment (both extending standard Activity and Fragment respectively). From the edit fragment, I can call two other fragments, A or B, (via interface with the activity). Framents A and B simply extend standard Fragment as well.
Both the fragments, A or B, return a value (again via interface with the activity), and I want to use this value in the edit fragment.
However, back in the activity, if I have 'replaced' the edit fragment with one of either fragment A, or B, how do I then update the edit fragment and show it again, with the new/updated value from the A or B fragments (as the edit fragment's view is no longer in the edit activity's container, a simple FrameLayout)
i have tried combinations of Add/Hide fragments, but can't get that to work. Also, somehow using savedInstanceState doesn't appear to be an option. At the moment, it seems the easiest solution is simply to make fragments A and B as DialogFragments, and interface between them and the edit fragment. Any help appreciated.
Related
I made an andorid application using the "activity" component. I used it for a while, but over time I found that one page was not enough for me. I needed to add tabLayout, viewPager2 and three fragments.
I needed to migrate the functionality from mainActivity to the firstFragment.
I encountered a problem with events during the XML migration. In the activity I called android: onClick = "myOnClicFunction". But is that not possible?
I know that this has been discussed several times here in several threads. However, I would like to ask how to proceed correctly.
Here in the thread Android Fragment onClick button Method is the advice "even if you are in the fragment, put the onClick functions into activity" there is another similar advice to, "if you insist to make it in a fragment"
But what is right? Should the onclick function be correct in the activity or in the fragment?
I don't want to put the source code here, because I don't know which procedure is correct at all.
I am creating a program that is for testing students. An example is displayed and a response is pending.
The first tab has a lot of buttons + saves the result to an xml files.
The second tab will contain statistics. From saved files displays how many calculations were correct, wrong, average response time, etc ...
In Activity I had functions like getQuestion(), evaluateQuestion(), saveToXml() and events zeroWasPressed(), oneWasPressed(), twoWasPressed(), threeWasPressed() ...
Where should I put all these functions when I change the application from Activity to Fragment?
on click function listener should be placed in corresponding fragment, where the button is added in fragment layout.
events zeroWasPressed(), oneWasPressed(), twoWasPressed(), threeWasPressed() ... should be placed inside corresponding fragment where those buttons were added in layout.
getQuestion(), evaluateQuestion(), saveToXml() , this function can be placed in your activity or some other custom classes, that would appropriate according to architecture viewpoint.
In my app, I'm using a Single Activity with multiple Fragments Architecture and I navigate between them using the Navigation Library. Within one of the fragments, I have multiple categories, each associated with an ID. When a category is clicked, I take the user to that respective category explainer screen with the code below.
val directions = MainNavGraphDirections.launchFragmentWithThisCategoryId(categoryId!!)
onRoute(AppRoute.Directions(directions))
The above code sends them to the explainer screen associated with the associated categoryId. All is well until this point, the right explainer screen gets launched based on the categoryId. Within this explainer screen, I have a deep-link with a tag chatbot://fragment/wizardintro that is supposed to let the main activity know the specific follow-up fragment to send the user to. I denote all the fragments that can receive this deep-link with the code below.
companion object{
const val DEEP_LINK = "chatbot://fragment/wizardintro"
}
In the MainActivity, I have a method that receives all the different deep linking intents and matches them to the tags that will launch the respective category fragment with the code below.
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.toString().also { deepLink ->
when (deepLink) {
IntroductionFragment.DEEP_LINK ->{
val categoryId = intent?.getLongExtra("categoryId", 0L)
val directions = MainNavGraphDirections.actionGlobalGoalWizard(categoryId)
navController.popBackStack()
navController.navigate(directions)
}
}
Now my problem arises when I try to retrieve this categoryId in the Main Activity and pass it to the next fragment. I don't get anything and only the default Long gets passed along. I think the function override fun onNewIntent(intent: Intent?) { } in the MainActivity recieve any intent. To be clear, these intents are sent from the explainer fragment which is technically a fragment that loads a json. Within the json there is a "route": {"type": "route", "url": "chatbot:///fragment/wizardintro"
In the MainActivity, the onNewIntent functions receive these intents unpacks them with this line intent?.data?.toString().also ...then in the when statement picks a fragment that has a matching chatbot:///fragment/wizardintro
I said all this to say that the main activity doesn't actually gets the categoryId, it simply picks launching the neccessary fragment without actually having anything associated with the categoryId
This makes me think that the first clicked categoryId doesn't actually get passed to the MainActivity. Although, to me, this seems like it shouldn't be this hard to pass objects/data from a fragment to an activity. What am I missing? What can I read to educate myself more on this?
Thanks for your time and responses!
Since we already got to the conclusion that MainActivity is not getting categoryId, you just need to pass that categoryId with the deep-link.
However, there is no need for any communication from Fragment to Activity.
You could achieve the same result through communication only between Fragment to Fragment, and Activity to Fragment.
What you want to do is to look more closely on deep-links and android navigation in the AndroidDocs, click here.
As you can tell, there are different ways to go around this, starting with arguments for each fragment. Assigning categoryId as an argument to the Fragment would help you use the navigationController and navigate to the new Fragment, while passing the categoryId to it.
Now, I am aware that you also wish to launch it with a deep-link; there's also a good explanation on here. According to the docs, you can place arguments in deep-links in the following manner...
Placeholders in the form of {placeholder_name} match one or more characters. For example, http://www.example.com/users/{id} matches http://www.example.com/users/4. The Navigation component attempts to parse the placeholder values into appropriate types by matching placeholder names to the defined arguments that are defined for the deep link destination. If no argument with the same name is defined, a default String type is used for the argument value.
The navigation is something amazing and capable, you just need to be aware of everything it can actually do. You can even bind it to a BottomNavBar, with extremely minimal amount of code.
Try going over the AndroidDocs about it, and it'll grow on you for sure.
I've got 2 ListViews (A and B) of the same base type. What I need to implement is:
- user drags items from A, drops them into B
- user drags items from B, drops them into B(rearrangement).
The changes has to be saved in an sqlite table. 2 different sql query runs, so the drag and drops must have a different outcome.
If I do 2X B.setOnDragDropped, can I differentiate the 2 depending on where did the drag start?
Thanks for your help, much apprich folks
If you call setOnDragDropped(...) twice on the same node, the second handler will replace the first one (it is a set method, and works the same way as any other set method: it sets the value of a property).
You need something along the lines of
listViewB.addEventHandler(DragEvent.DRAG_DROPPED, e -> {
// handler code here...
});
You can determine the source of the drag inside the handler with
e.getGestureSource()
and see which node it matches to determine the course of action.
I have an activity with two fragments say F1 and F2. On clicking the list view of F2 , a new fragment is called say F3 (Its single activity multi fragments design).
I have back button on right side of dual pane layout. On hitting this button i need to pass data from fragment F3 to F2, so that F2 views are updated based on data received(something like onActivityForResult for activity to activity interaction).
How can i achieve this?
Fragment class has setTargetFragment(), according to docs:
Optional target for this fragment. This may be used, for example, if
this fragment is being started by another, and when done wants to give
a result back to the first. The target set here is retained across
instances via FragmentManager.putFragment().
Please excuse the long post. I was playing around with a simple app and wanted to save a custom object in a fragment across an orientation change. Previously within activities this used to be handled using the onRetainNonConfigurationInstance() / getLastNonConfigurationInstance() methods. Seeing as these methods are now deprecated, the documentation encourages the use of fragments and the setRetainInstance(boolean) method.
I went ahead and played around with this method and then noticed a strange difference in behaviour when it came to saving the state of the fragments across orientation change. First up, a very brief explanation of the app I was playing with:
Main Activity
Fragment A (First fragment shown on app launch)
This is a simple fragment with 3 EditText controls. Each one has an ID in the layout file. The fragment also includes a button which when selected replaces Fragment A with Fragment B and saves the transaction on the backstack.
Fragment B
This is a fragment with an empty layout. If back is pushed, Fragment A is restored from the backstack.
Scenarios
Scenario A - setRetainInstance(false):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation once and hit the back key.
Fragment A is displayed with the entered values (view state) intact.
Scenario A - setRetainInstance(true):
The same behaviour takes place as above
Scenario B - setRetainInstance(false):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation twice and hit the back key.
Fragment A is still displayed with the entered values (view state) intact.
Scenario B - setRetainInstance(true):
App launches and fragment A is displayed.
I enter values into the EditText fields and select the button.
Fragment B is displayed. I change device orientation twice and hit the back key.
Fragment A is displayed with empty EditText controls, i.e. none of the entered values (view state) still intact.
For some reason the use of setRetainInstance(true) interferes with the view state of fragment A (on the backstack) when the orientation changes more than once.
Possible Explanation
I started getting nervous about the use of setRetainInstance while not having a full understanding of what was going on, so I dug around in the support library source code to try figure it out. At a very high level, I think this may be what is going on with setRetainInstance(true):
Fragment A is displayed, button is pressed and Fragment A is replaced by Fragment B. As part of this process, the FragmentManager (FM) removes Fragment A and fragmentA.mRemoving flag is set to true.
Change orientation the first time. At this point the FM attempts to save all state of the fragments:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
Fragment A has a CREATED state and has a null saved state, so it qualifies to have its state saved.
The activity is destroyed as part of the orientation change. Long story short, Fragment A has its state changed to INITIALIZING.
The activity is recreated and an attempt is made to move the state of the fragments to CREATED. However, at this point there is a check in the FM moveToState() method:
if (f.mRemoving && newState > f.mState) {
// While removing a fragment, we can't change it to a higher state.
newState = f.mState;
}
Because fragmentA.mRemoving remains true from step 1 as the fragment was retained (not recreated), it does not have its state increased to CREATED but remains in the INITIALIZING state. Note that even if one presses the back key now, Fragment A will still have its state intact, as a result of its state being saved in step 2.
Change orientation for the 2nd time. Once again the FM attempts to save all state of the fragments:
Parcelable saveAllState() {
...
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);
However, because Fragment A is in the INITIALIZING state it does not qualify to have its state saved. Hence, once orientation completes for the 2nd time, if the back key is pressed the state of Fragment A is no longer intact.
Questions
Is this behaviour expected? Perhaps this relates to the documentation discouraging the use of setRetainInstance and backstack fragments?
How should we deal with view state and the use of setRetainInstance? Perhaps my use case is incorrect, but I would be nervous using the setRetainInstance functionality with this difference in behaviour.
Once again, sorry for the long post. Feedback will be appreciated as always.