stackoverflow! I’m new to developing in android and I’m trying to figure out Fragments in android. I created a FrameLayout in activity_main.xml
<FrameLayout
android:id="@+id/mainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And in onCreate method of MainActivity I’m trying to pass the LoginFragment to the FrameLayout. Here’s the code of MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showFragment(LoginFragment())
passwordInput.addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
logInButton.isEnabled =
passwordInput.text.isNotBlank() && loginInput.text.isNotBlank()
}
})
loginInput.addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
logInButton.isEnabled =
passwordInput.text.isNotBlank() && loginInput.text.isNotBlank()
}
})
logInButton.setOnClickListener {
showFragment(LoginFragment())
}
}
private fun showFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.mainFragment, fragment)
.commit()
}}
The LoginFragment code:
class LoginFragment : Fragment(R.layout.fragment_login) {}
And the fragment_login.xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/loginInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="80dp"
android:layout_marginEnd="40dp"
android:autofillHints="username"
android:hint="@string/enter_login"
android:inputType="textEmailAddress" />
<EditText
android:id="@+id/passwordInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="20dp"
android:autofillHints="password"
android:hint="@string/enter_password"
android:inputType="textPassword" />
<Button
android:id="@+id/logInButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:enabled="false"
android:text="@string/log_in" />
</LinearLayout>
I got the null pointer:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.widget.EditText.addTextChangedListener(android.text.TextWatcher)’ on a null object reference
at com.example.fragmentactivity.MainActivity.onCreate(MainActivity.kt:18)
I can’t really understand what’s wrong. It says that I’m trying to refer an empty veiw but all thoose views are viewBinded and in IDE they are shown as they should be. Thanks in advance for all the hints and answers
2
Answers
for me it doesn’t make sense to import a fragment to your main activity, if you need a layout to use it anywhere , just create a layout
otherwise implement login requirements inside login fragment
What do you mean when you say "the views are viewBinded" – are you using View binding? Or are you using the (deprecated) Kotlin synthetic extensions? Because it looks like the latter
If you are using View binding (and you’ve not shown yourself accessing the binding object for some reason) you’ll have to create a new binding object for the
Fragment
‘s layout, since you’re adding it at runtime and it has nothing to do withMainActivity
‘s layout XML.If you’re using the synthetic extensions, the recommendation is don’t – they’re deprecated for a reason, and it’s better to move over to View binding. I’m not sure how that interacts with your added layout – from what I know it should work ok, but I also know it won’t complain if you reference a
View
ID in a completely different layout, because it doesn’t know what you’re actually doing at compile time. It can’t make any safety guarantees, so the fact the IDE isn’t complaining doesn’t tell you anything except "those IDs exist in some layout file" (another reason it’s recommended to move away from this extension).If you’re unsure about whether
View
lookups are working, do it the old-school way withfindViewById
. If the view has been added to theActivity
‘s layout hierarchy, that will find it.As for why it’s happening – well there’s a couple of things wrong here.
First you’re calling
commit()
instead ofcommitNow()
in yourFragmentTransaction
:Basically, if you just call
commit()
, theFragment
will be added to yourActivity
later. You’re immediately trying to access views on it, but it’s probably not added to the view hierarchy yet.The second issue is that to access views on a
Fragment
, it has to have at least got to theonCreateView
stage of its lifecycle, otherwise ain’t no views to access!commitNow()
up there seems to guarantee that it will ensure theFragment
will at least be at that stage when it returns, so the views should be there to access.The problem here really is that your
Fragment
should be handling all this stuff itself, not theActivity
. If you have a Fragment with some text fields that need validation, that should be something the Fragment takes care of, because it’s all happening inside the Fragment, as a piece of UI that does a particular task. It can communicate with its parent activity if it needs to, but theActivity
should ideally be doing as little as possible.So doing all this stuff through the
Fragment
itself, encapsulating it, would avoid a lot of these problems. InonCreateView
oronViewCreated
you’d just set up theTextWatcher
s on theView
s (which have definitely been created at this point) and now your Fragment is ready to go. YourActivity
doesn’t need to know what’s going on in there, doesn’t need to know whatView
s are in there or any of that. Separation of concerns makes things easier to work with, y’know? It helps!