I can't call function of my class from object inside this class.
How should i do this ?
class LoginActivity: AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
}
private fun disableLoginButton(){
button_login.isEnabled = false
}
private object textChangeListener: TextWatcher{
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//Here i cannot call function
disableLoginButton() // unresolved reference.
}
}
}
But when i call LoginActivity().disableLoginButton() instead disableLoginButton()it's visible, but fails with
NullPointerException
on login_button
Try this :
class LoginActivity: AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
editTextSample.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
disableLoginButton()
}
})
}
}
private fun disableLoginButton(){
button_login.isEnabled = false
}
EDIT: Doesn't work
From jetbrains team:
In Java and Kotlin, "inner" means "capturing the outer instance", where as "nested" means simply declared inside something else. Java's static classes are only nested, non-static nested classes are inner. In Kotlin you have to explicitly declare something as "inner" (we reversed the Java's convention). So, your object is not inner, btu only nested. And no named object can be inner, in fact: named objects are singletons, so the can not depend on any kind of outer instance.
Try specifying the object as inner:
private inner object textChangeListener: TextWatcher{
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int {
//Here i cannot call function
disableLoginButton() // unresolved reference.
}
}
This should allow you to access the outer scope.
Related
I have a recyclerview where each view contains a TextView and an EditText. The part where I am stuck is when I start typing in any of the row's EditText box, it automatically updates another row further down the list. I know this is occurring because of the whole recycling aspect of recyclerview and the same view is re-used for the rows further down. I just don't know how to implement this correctly.
class TodoAdapter(private val tasks : List<TodoItem>) : RecyclerView.Adapter<TodoAdapter.ViewHolder>() {
inner class ViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
var editText = view.findViewById<EditText>(R.id.edit_text)
init {
editText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(s: Editable?) {
tasks[adapterPosition].task = s.toString()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.todo_item,parent,false)
return ViewHolder(view)
}
override fun getItemCount() = tasks.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = tasks[position]
holder.editText.setText(item.task)
}
}
class TodoItem(var task: String = "")
editText.addTextChangedListener(object TextWatcher() {
override fun afterTextChanged(s : Editable) {
item.textFieldValue = s.toString()
}
});
Put textChangeListener inside init block this should work
#MuhammadAhmed
Data class
class todolist(var hours: String, var task: String)
Adapter
override fun onBindViewHolder(holder: TodoAdapter.TodoViewHolder, position: Int) {
val currentItem = todo[position]
holder.todo.text = currentItem.hours
holder.task.text = currentItem.task
holder.task.addTextChangedListener(object: TextWatcher{
override fun afterTextChanged(s: Editable?) {
currentItem.task = s.toString()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
}
In my Recyclerview's Adapter class, I have done something as below:
holder.mEdtDescription.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
listener.onDeleteOrDescribe(position, "" + holder.mEdtDescription.text.toString(), "describe")
}
})
holder.mIvDelete.setOnClickListener {
listener.onDeleteOrDescribe(position, "", "delete")
}
mEdtDescription is EditText.
On deleting some value from the recycler my adapter calls method onDeleteOrDescribe.
Unfortunately, At the same time onTextChanged is also calling.
How can I avoid calling onTextChanged when I am deleting soemthing?
Hope you got the point.
As per the solution, I have tried disabling textWatcher as below :
val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
ToastUtil.displayShortDurationToast(mContext,"called")
listener.onDeleteOrDescribe(
position,
"" + holder.mEdtDescription.text.toString(),
"describe"
)
}
}
holder.mEdtDescription.addTextChangedListener(textWatcher)
holder.mIvDelete.setOnClickListener {
//listener.onDeleteStep(position)
holder.mEdtDescription.removeTextChangedListener(textWatcher)
listener.onDeleteOrDescribe(position, "", "delete")
holder.mEdtDescription.addTextChangedListener(textWatcher)
}
But, still is calling onTextChange() :(
You could try disabling the TextWatcher temporarily:
val textWatcher : TextWatcher = object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
listener.onDeleteOrDescribe(position, holder.mEdtDescription.text.toString(), "describe")
}
}
holder.mEdtDescription.addTextChangedListener(textWatcher)
holder.mIvDelete.setOnClickListener {
holder.mEdtDescription.removeTextChangedListener(textWatcher)
listener.onDeleteOrDescribe(position, "", "delete")
holder.mEdtDescription.addTextChangedListener(textWatcher)
}
I don't get any errors when running the code but the addTextChangedListener isn't running when I execute it. I am trying to have the continue button disabled until the user input text. When run, the continue is still enabled. Here is my code
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.Button
import android.widget.EditText
class First_Name_Activity : AppCompatActivity() {
lateinit var editFirstNameText: EditText
lateinit var btnOpenActivity: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first_name)
editFirstNameText = findViewById(R.id.first_name_edit_text)
btnOpenActivity = findViewById(R.id.first_name_continue_btn)
editFirstNameText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
btnOpenActivity.isEnabled=true
}
}
btnOpenActivity.setOnClickListener {
val intent = Intent(this, Last_Name_Activity::class.java)
startActivity(intent)
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
}
}
override fun finish() {
super.finish()
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
}
}
You mentioned "When run, the continue is still enabled". Have you set the button to be disabled in the XML or through code (in the onCreate method) to be disabled when the app runs? After that, you can do:
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
btnOpenActivity.isEnabled = s?.isNotBlank() == true
}
First check edittext is empty or not then
Write below code inside textwatcher
If(s?.isEmpty()){
btnOpenActivity.isEnabled
}
You can check for empty text in edittext,
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (!s.toString().isEmpty()) {
btnOpenActivity.isEnabled=true
} else {
btnOpenActivity.isEnabled=false
}
}
Or
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (!editFirstNameText.text.toString().trim().isEmpty()) {
btnOpenActivity.isEnabled=true
} else {
btnOpenActivity.isEnabled=false
}
}
onTextChanged() may be called even when the CharSequence is empty or null. There is a very simple fix to your problem, a null and non-empty check :
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if(s != null && s.isNotEmpty()){
btnOpenActivity.isEnabled=true
}
}
Edit your code like this
editFirstNameText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
//btnOpenActivity.isEnabled==true
//Change this line to line bellow
btnOpenActivity.isEnabled==!s.toString().isEmpty()
}
})//you miss this bracket ')' in your code
Hi this will works for you...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sample_text_watcher)
btnOpenActivity.setOnClickListener(View.OnClickListener { })
editFirstNameText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (s != null) {
btnOpenActivity.isEnabled = s.trim().isNotEmpty()
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
happy_coding :)
Here is a text box
<EditText
android:id="#+id/textBox"
android:width="150px"
android:layout_width="128dp"
android:layout_margin="12dp"/>
Here's some code.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main) // Unresolved reference activity_main
val tb = findViewById(R.id.textBox) as EditText
tb .addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
Toast.makeText(
this#MainActivity,
"Text changed.",
Toast.LENGTH_SHORT
).show()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}) }
}
The findViewById doesn't work because Unresolved reference textBox.
What's going on?
None of the examples I've found online work.
This is rubbish!
Why is Android development so exceptionally difficult?
you have to add this back : setContentView(R.layout.activity_main)
why ? setContentView(R.layout.yourLayoutName) is how you associate a layout file to an activity. think of it as connecting the xml you made in the layout to the activity. when you leave out the setContentView, your activity has no layout at all.
then, when it says it's unresolved, you have use the import:
import your.package.name.R (you can usually use alt+enter to bring up these types of suggestions for you)
just start typing setContentView(R.layout.activity_main) then, there should be a line underneath the letter R, just click to the right of it and use alt+ enter to bring up suggestions for imports.
Additional info :
The R class is generated automatically from the application's resources, It contains the ids for the majority of resources created underneath the res folder
Entirely irrlevant note :
Why is Android development so exceptionally difficult?
What's normal for the spider is chaos for the fly :)
Please uncomment setContentView(R.layout.activity_main)from your activity. In kotlin you do not need to explicitly call findViewById. You can simply do this.
textBox.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
Toast.makeText(
this#MainActivity,
"Text changed.",
Toast.LENGTH_SHORT
).show()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
}) }
How do you build a lambda expression for the EditText addTextChangeListener in Kotlin? Below gives an error:
passwordEditText.addTextChangedListener { charSequence ->
try {
password = charSequence.toString()
} catch (error: Throwable) {
raise(error)
}
}
addTextChangedListener() takes a TextWatcher which is an interface with 3 methods. What you wrote would only work if TextWatcher had only 1 method. I'm going to guess the error you're getting relates to your lambda not implementing the other 2 methods. You have 2 options going forward.
Ditch the lambda and just use an anonymous inner class
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
Create an extension method so you can use a lambda expression:
fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
})
}
And then use the extension like so:
editText.afterTextChanged { doSomethingWithText(it) }
Add this core ktx dependence
implementation 'androidx.core:core-ktx:1.0.0'
You simply have to do
passwordEditText.doAfterTextChanged{ }
A bit old, but using Kotlin Android extensions you can do something like that:
editTextRequest.textChangedListener {
afterTextChanged {
// Do something here...
}
}
No extra code needed, just add:
implementation 'androidx.core:core-ktx:1.0.0'
Sorry for being late!
If you add implementation 'androidx.core:core-ktx:1.1.0' to your module's build.gradle file then you can use
etPlayer1.doOnTextChanged { text, start, count, after -> // Do stuff }
Test it :
passwordEditText.addTextChangedListener(object:TextWatcher{
override fun afterTextChanged(s: Editable?) { }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
})
hope this Kotlin sample help making it clear:
class MainFragment : Fragment() {
private lateinit var viewModel: MainViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.main_fragment, container, false)
view.user.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable) {
userLayout.error =
if (s.length > userLayout.counterMaxLength) {
"Max character length is: ${userLayout.counterMaxLength}"
} else null
}
})
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// TODO: Use the ViewModel
}
}
With this XML layout:
<android.support.design.widget.TextInputLayout
android:id="#+id/userLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterMaxLength="5"
app:counterEnabled="true"
android:hint="user_name">
<android.support.design.widget.TextInputEditText
android:id="#+id/user"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
And this Gradle:
android {
compileSdkVersion 'android-P'
...
}
api 'com.android.support:design:28.0.0-alpha1'
implementation 'com.android.support:appcompat-v7:28.0.0-alpha1' // appcompat library
In case you're using Material Filled text field or Outlined text field, attempt to respond to input text change as mentioned by documentation, respectively:
filledTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
// Respond to input text change
}
and
outlinedTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
// Respond to input text change
}
if you use implementation 'androidx.core:core-ktx:1.1.0-alpha05' you can use
For android.widget.TextView
TextWatcher
TextView.doBeforeTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked before the text changed.
TextWatcher
TextView.doOnTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked when the text is changing.
TextWatcher
TextView.doAfterTextChanged(crossinline action: (text: Editable?) -> Unit)
https://developer.android.com/reference/kotlin/androidx/core/widget/package-summary#extension-functions
Add the core ktx dependency
implementation 'androidx.core:core-ktx:1.3.0'
And you can simply implement like this
edit_text.addTextChangedListener { it: Editable? ->
// Do your stuff here
}
Another alternative is the KAndroid library -
implementation 'com.pawegio.kandroid:kandroid:0.8.7#aar'
Then you could do something like this...
editText.textWatcher { afterTextChanged { doSomething() } }
Obviously it is excessive to use an entire library to solve your problem, but it also comes with a range of other useful extensions that eliminate boilerplate code in the Android SDK.
You can make use of kotlin's named parameters:
private val beforeTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val onTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val afterTextChangedStub: (Editable) -> Unit = {}
fun EditText.addChangedListener(
beforeTextChanged: (CharSequence, Int, Int, Int) -> Unit = beforeTextChangedStub,
onTextChanged: (CharSequence, Int, Int, Int) -> Unit = onTextChangedStub,
afterTextChanged: (Editable) -> Unit = afterTextChangedStub
) = addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
beforeTextChanged(charSequence, i, i1, i2)
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
onTextChanged(charSequence, i, i1, i2)
}
override fun afterTextChanged(editable: Editable) {
afterTextChanged(editable)
}
})
This is the lambda function with edit text field with TextWatcher
searchField.addTextChangedListener(
afterTextChanged = {
},
onTextChanged = {s, start, before, count->
TODO("DO your code")
},
beforeTextChanged = {s, start, before, count->
TODO("DO your code")
}
)
This looks neat:
passwordEditText.setOnEditorActionListener {
textView, keyCode, keyEvent ->
val DONE = 6
if (keyCode == DONE) {
// your code here
}
false
}