Navigate to a new instance of the same Fragment type after Jetpack Navigation 2.4.x update - android-jetpack-navigation

I'm updating Jetpack Navigation from 2.3.4 to 2.4.2 and believe I have found a breaking change in my project.
Let's say I have a MainFragment and a SecondFragment. Launching SecondFragment from MainFragment (with safeArgs) works as expected. However, I also need to be able to launch a new instance of SecondFragment from SecondFragment with new arguments. I'm constructing a new navigation action and telling the navController to navigate to it. Before updating, I could see that this navigation action was taken, and I hit onCreateView and other lifecycle methods in my SecondFragment class.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.myButton)
button.setOnClickListener {
val action = SecondFragmentDirections.newSecondFragment("coming from second")
Log.d("SecondFragment", "click ${action.arguments.get("myArg")}")
findNavController().navigate(action)
}
val text = view.findViewById<TextView>(R.id.message)
text.text = safeArgs.myArg
}
My log statement there is being logged and is correct, but it seems like the navigation action never happens, as I'm never hitting onCreateView or onViewCreated, and my SecondFragment does not update with the new arguments.
I have a small reproducible example here: https://github.com/rahobbs/NavExperiments/tree/main/app/src/main/java/com/example/navexperiments

Related

Create single page with text variable in Android Studio (Kotlin)

Is it possible to create a single page in Android Studio (using Kotlin) that displays different text depending on the button you hit on the main screen?
Imagine loading the app, on the home screen there are five buttons, each button you click, displays different text. Instead of creating five pages for the five buttons, can I create the one page and do something with the code to show certain text based on which button I hit?
If it is, how would I go about this and code it?
Update 1
This is what I have done, two buttons to navigate to another activity.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btn = findViewById<Button>(R.id.btn)
// set on-click listener
btn.setOnClickListener {
val intent = Intent(this, Duas::class.java)
startActivity(intent)
btn1.setOnClickListener {
textView.text = "newText"
}
val btn2 = findViewById<Button>(R.id.btn2)
// set on-click listener
btn2.setOnClickListener {
val intent = Intent(this, Duas::class.java)
startActivity(intent)
btn2.setOnClickListener {
textView.text = "newText"
}
}
}
}
yes, you can do that, you make your XML layout which contains the textView and the buttons,
now in your activity class, you need to assign an onClickListener on each button
now inside each one your gonna change the text to whatever you want like so
Button1.setOnClickListener {
textView.text = "newText"
}
, so whenever you hit the button the text will change to what is assigned to

Hot to navigate to activity from fragment

This is my first post, so I will try to explain myself the best I can.
This is my first project in android with kotlin, and Im creating a simple app with 3 tabs. Im trying to make a simple navigation from a button in one of the 3 fragments associated with the 3 tab views to a new activity. This button have the function "push" associated.
I attach the code from the fragment, where you can see the function to navigate "push" which start the new activity. If I place that function in the onCreateView section, it works fine when I enter the fragment. I had no problem doing this navigation from an activity to another using a similar button and function but i dont know how fragments properly works, so i guess there is a thing relative to them i am missing. It seems like the function is not recognized outside the onCreate section, so maybe i have to declare it in another way:
class NotificationsFragment : Fragment() {
private var _binding: FragmentNotificationsBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
return binding.root
}
private val binding get() = _binding!!
fun push(view: View) {
val intent = Intent(activity, tutorialPrueba::class.java)
startActivity(intent)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
I was not able to find any solution that helps me in this case. As long this is my first post, i will thank any help or recommendation relative to the way I posted or how i can make it better. Also sorry for my poor english.
Thanks in advance.
If you are using the DataBinding, the onClick declared in the layout xml does not work. So to make the button, once pressed, call your method you have to do this:
private var _binding: FragmentNotificationsBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
_binding.idOfButtonToPress.setOnClickListener{ //replace idOfButtonToPress with the real android:id of button
push()
}
return binding.root
}
private val binding get() = _binding!!
fun push() {
val intent = Intent(activity, tutorialPrueba::class.java)
startActivity(intent)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

How to test navigation from DialogFragment to another DialogFragment?

I'm using the Navigation component for my two DialogFragments and when I press a button on the first DialogFragment it is dismissed and then the second one is shown. I need to test that clicking this button will take me to the second dialog. I have a simple home fragment that is overlayed by the first DialogFragment at the start of the app. The following code is from the first DialogFragment.
/**
* Redirects users to another dialog after pressing button
*/
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button.setOnClickListener {
if (findNavController().currentDestination?.id == R.id.firstDialogFragment) {
findNavController().navigateUp()
val action = HomeFragmentDirections.actionHomeFragmentToSecondDialogFragment()
findNavController().navigate(action)
}
}
}
This next bit of code comes from the developer's guide and only checks for the behavior of dismissing a DialogFragment back to the previous Fragment.
#RunWith(AndroidJUnit4::class)
class MyTestSuite {
#Test fun testDismissDialogFragment() {
// Assumes that "MyDialogFragment" extends the DialogFragment class.
with(launchFragment<MyDialogFragment>()) {
onFragment { fragment ->
assertThat(fragment.dialog).isNotNull()
assertThat(fragment.requireDialog().isShowing).isTrue()
fragment.dismiss()
fragment.requireFragmentManager().executePendingTransactions()
assertThat(fragment.dialog).isNull()
}
// Assumes that the dialog had a button
// containing the text "Cancel".
onView(withText("Cancel")).check(doesNotExist())
}
}
}
I need some way to test the behavior of a DialogFragment's button and see that it dismisses itself and starts the second DialogFragment.
When I test for the button being clicked, the first DialogFragment is correctly dismissed, but the second DialogFragment is not launched. I've used both Espresso and UiAutomator and the click does occur, but reading the code snippet's explanation for testing DialogFragments it says,
"Even though dialogs are instances of graphical fragments, you use the launchFragment() method so that the dialog's elements are populated in the dialog itself, rather than in the activity that launches the dialog".
Is the reason that I am unable to check if the second DialogFragment exists or not, because it is an instance of a graphical fragment and my click listener for the button on the first DialogFragment cannot implement launchFragment() for the second DialogFragment?

How to move from activity 2 to activity 3 in android studio

I've created three activities. My first MainActivity has two buttons, one that takes you to Activity2 and one that takes you to Activity3. Both of those buttons work, I've managed to code them correctly.
But then on Activity3 there's a button that's supposed to take you also to Activity2, and it's not working. I've tried several things but I can't seem to figure it out. Is it possible to code several buttons that lead to the same activity? If so please help cause I'm new at coding and stuff. Also here's how I've been coding the buttons :
1-after creating the activity, I go to the Java file and create a new class. In that class I write the following code :
class className : AppCompatActivity(){
override fun onCreate (savedInstance : Bundle?){
super.onCreate(savedInstance)
setContentView(R.layout.activity2)
}
}
Then I add it to the manifest
2-then I go back to the MainActivity and write this :
val anyName = buttonName
anyName.setOnClickListener {
startActivity(Intent(this, class Name :: class.java))
}
Of course android studio takes care of everything and imports everything that's needed but the second I add more than two of those in my MainActivity the whole app crashes.
Please explain this as simply as possible as, again, I'm really new to coding and android studio.
Thank you !
To go back on an activity you can just use finish() on any function, but if you want to take any information with you, you should look for more information about startActivityforResult().
However next time upload the code and not this pseudocode please, it would help a lot!
When you creating a new activity, you need also to add to it the new XML file, which will be display UI in the Activity page.
You can do it in two ways:
1. Custom.
Create a new ClassName.kt (Java.class in Java) and attach inside onCreate() method a XML layout, which will displaying all views in Activity page.
2. With Android Studio.
Just right-click in your package name folder, where appears your, for example, empty activity when you start new an Android Studio Project. Then select new, then in bottom side of drop-downed view select type of new activity what you want. For example, it is Empty Activity. So, lets sum above information: right-click at your package name folder -> new -> type of activity.
For what below info? I see in your example code, which you show as code in Activity number 3, what you have in onCreate() this line of code setContentView(R.layout.activity2). It is line means, what you add XML file into your activity. One XML file for activity can be used only for one activity if you want to show, after click on button another activity. Rather you will see only one screen because two activities use one layout. So, check your activities
need to be something like this:
1.In ActivityOne.
class ActivityOne : Activity() {
override fun onCreate (savedInstance : Bundle?) {
super.onCreate(savedInstance)
setContentView(R.layout.activity1)
}
}
1.In ActivityTwo.
class ActivityOne : Activity() {
override fun onCreate (savedInstance : Bundle?) {
super.onCreate(savedInstance)
setContentView(R.layout.activity2)
}
}
1.In ActivityThree.
class ActivityThree : Activity() {
override fun onCreate (savedInstance : Bundle?) {
super.onCreate(savedInstance)
setContentView(R.layout.activity3)
}
}
Make sure if you want to create a new activity First right click app then goto new -> Activity -> Empty Activity .
finally you can add onClick in your Button tag , then use startActivity method.
public void methodName(View view) {
startActivity(new Intent(this,ActivityName.class));
}
Example Code:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void gotoTwo(View view) {
startActivity(new Intent(this,Activity2.class));
}
public void gotoThree(View view) {
startActivity(new Intent(this,Activity3.class));
}
}
activity_main.xml :
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="200dp"
android:layout_marginRight="240dp"
android:onClick="gotoTwo"
android:text="Activity2" />
Don`t forgot to create a new java you should create a new xml file too

Object in Kotlin/Singleton -> different instance (?)

I have an app with a bottom navigation and a toolbar.
The bottom navigation opens different fragments and the toolbar is suppose to change depending on the fragment (not implemented yet).
The toolbar is initialized in the main activity and depending on which fragment will be opened the menu of the toolbar will be inflated differently. There are different methods in the main activity which inflate the menu and set up the onClickListeners.
Inside OnCreate in MainActivity
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.main_container, QuickNPangFragment.QuickNPangFragment.newInstance(), "activitySelection")
.addToBackStack("navigation_quick")
.commit()
supportFragmentManager.executePendingTransactions()
}
[...]
val toolbar: Toolbar = findViewById(R.id.toolbarNew)
when (supportFragmentManager.getBackStackEntryAt(supportFragmentManager.backStackEntryCount-1).name) {
"navigation_quick" -> setQuickMenu(toolbar)
"navigation_location" -> setLocationMenu(toolbar)
"navigation_friends" -> setFriendsMenu(toolbar)
}
[...]
private fun setQuickMenu(toolbar: Toolbar) {
toolbar.inflateMenu(R.menu.menu_main)
toolbar.setOnMenuItemClickListener {
QuickNPangFragment.QuickNPangFragment.newInstance().setQuickMenuItemClickListener(it.itemId)
true
}
}
Inside QuickNPangFragment
object QuickNPangFragment {
fun newInstance() = QuickNPangFragment()
}
There is something wrong in my understanding of this concept of the singleton: I think that only ONE instance of this fragment will be created, so whenever I call the newInstance() method of the fragment, it will return the same instance.
When running the debugger though it shows that the reference to the QuickNPangFragment object that is added when calling onCreate (this = {QuickNPangFragment#10915}... << this is the address, correct?) is different from the one when setting up the onClickListener in the setQuickMenu method (this = {QuickNPangFragment#11154}).
1) What am making wrong? and/or Where is the misunderstanding of that concept?
2) Is it ok to change the toolbar menu depending on the fragment?

Resources