Skip to content

Commit

Permalink
[+] Added convenient MaskedEditText listeners.
Browse files Browse the repository at this point in the history
  • Loading branch information
VicMikhailau committed Aug 8, 2023
1 parent 1be2086 commit 6fa7ed2
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 23 deletions.
2 changes: 1 addition & 1 deletion MaskedEditText/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'

ext {
PUBLISH_GROUP_ID = 'io.github.vicmikhailau'
PUBLISH_VERSION = '5.0.0'
PUBLISH_VERSION = '5.0.1'
PUBLISH_ARTIFACT_ID = 'MaskedEditText'
}
apply from: "${rootProject.projectDir}/scripts/publish-mavencentral.gradle"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.vicmikhailau.maskededittext

import android.content.Context
import android.text.Editable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatEditText
import com.vicmikhailau.maskededittext.R.*
Expand All @@ -15,20 +16,20 @@ class MaskedEditText(context: Context, attrs: AttributeSet) : AppCompatEditText(
// Fields
// ===========================================================

private var mMaskedFormatter: MaskedFormatter? = null
private var mMaskedWatcher: MaskedWatcher? = null
private var maskedFormatter: MaskedFormatter? = null
var maskedWatcher: MaskedWatcher? = null

// ===========================================================
// Getter & Setter
// ===========================================================

val maskString: String?
get() = mMaskedFormatter?.maskString
get() = maskedFormatter?.maskString

val unMaskedText: String?
get() {
val currentText = text?.toString()
val formattedString = currentText?.let { mMaskedFormatter?.formatString(it) }
val formattedString = currentText?.let { maskedFormatter?.formatString(it) }
return formattedString?.unMaskedString
}

Expand All @@ -39,7 +40,7 @@ class MaskedEditText(context: Context, attrs: AttributeSet) : AppCompatEditText(
if (typedArray.hasValue(styleable.MaskedEditText_mask)) {
val maskStr = typedArray.getString(styleable.MaskedEditText_mask)

if (maskStr != null && maskStr.isNotEmpty()) {
if (!maskStr.isNullOrEmpty()) {
setMask(maskStr)
}
}
Expand All @@ -48,14 +49,79 @@ class MaskedEditText(context: Context, attrs: AttributeSet) : AppCompatEditText(
}

fun setMask(mMaskStr: String) {
mMaskedFormatter = MaskedFormatter(mMaskStr)
maskedFormatter = MaskedFormatter(mMaskStr)

if (mMaskedWatcher != null) {
removeTextChangedListener(mMaskedWatcher)
if (maskedWatcher != null) {
removeTextChangedListener(maskedWatcher)
}

mMaskedFormatter?.let { mMaskedWatcher = MaskedWatcher(it, this) }
addTextChangedListener(mMaskedWatcher)
maskedFormatter?.let { maskedWatcher = MaskedWatcher(it, this) }
addTextChangedListener(maskedWatcher)
}
}

/**
* Add an action which will be invoked before the text changed.
* @return the [MaskedWatcher] added to the MaskedEditText
*/
fun MaskedEditText.doBeforeMaskedTextChanged(
action: (
text: CharSequence?,
start: Int,
count: Int,
after: Int
) -> Unit
) = maskedWatcher?.apply {
addTextChangedListener(beforeTextChanged = action)
}

/**
* Add an action which will be invoked when the text is changing.
* @return the [MaskedWatcher] added to the MaskedEditText
*/
fun MaskedEditText.doOnMaskedTextChanged(
action: (
text: CharSequence?,
start: Int,
before: Int,
count: Int
) -> Unit
) = maskedWatcher?.apply {
addTextChangedListener(onTextChanged = action)
}

/**
* Add an action which will be invoked after the text changed.
* @return the [MaskedWatcher] added to the MaskedEditText
*/
fun MaskedEditText.doAfterMaskedTextChanged(
action: (text: Editable?) -> Unit
) = maskedWatcher?.apply {
addTextChangedListener(afterTextChanged = action)
}

/**
* Add a text changed listener to this MaskedEditTest using the provided actions
* @return the [MaskedWatcher] added to the MaskedEditText
*/
fun MaskedEditText.addMaskedTextChangedListener(
beforeMaskedTextChanged: ((
text: CharSequence?,
start: Int,
count: Int,
after: Int
) -> Unit)? = null,
onMaskedTextChanged: ((
text: CharSequence?,
start: Int,
before: Int,
count: Int
) -> Unit)? = null,
afterMaskedTextChanged: ((text: Editable?) -> Unit)? = null
) = maskedWatcher?.apply {
addTextChangedListener(
beforeMaskedTextChanged,
onMaskedTextChanged,
afterMaskedTextChanged
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import android.widget.EditText

import java.lang.ref.WeakReference

class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : TextWatcher {
open class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : TextWatcher {

// ===========================================================
// Constructors
Expand All @@ -16,15 +16,39 @@ class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : Text
// Fields
// ===========================================================

private val mMaskFormatter: WeakReference<MaskedFormatter> = WeakReference(maskedFormatter)
private val mMaskFormatter: MaskedFormatter = maskedFormatter
private val mEditText: WeakReference<EditText> = WeakReference(editText)
private var oldFormattedValue = ""
private var oldCursorPosition: Int = 0

// Listeners.
private var beforeTextChanged: ((text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = null
private var onTextChanged: ((text: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? = null
private var afterTextChanged: ((text: Editable?) -> Unit)? = null

// ===========================================================
// Listeners, methods for/from Interfaces
// ===========================================================

fun addTextChangedListener(
beforeTextChanged: ((
text: CharSequence?,
start: Int,
count: Int,
after: Int
) -> Unit)? = null,
onTextChanged: ((
text: CharSequence?,
start: Int,
before: Int,
count: Int
) -> Unit)? = null,
afterTextChanged: ((text: Editable?) -> Unit)? = null) {
this.beforeTextChanged = beforeTextChanged
this.onTextChanged = onTextChanged
this.afterTextChanged = afterTextChanged
}

private fun setFormattedText(formattedString: IFormattedString) {
val editText = mEditText.get() ?: return

Expand All @@ -43,10 +67,10 @@ class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : Text
newCursorPosition -= 1
} else {
var mask: Mask? = null
mMaskFormatter.get()?.mMask?.let { mask = it }
mMaskFormatter.mMask?.let { mask = it }

var maskLength = 0
mMaskFormatter.get()?.maskLength?.let { maskLength = it}
mMaskFormatter.maskLength?.let { maskLength = it}
newCursorPosition = 1.coerceAtLeast(newCursorPosition.coerceAtMost(maskLength))


Expand All @@ -66,21 +90,25 @@ class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : Text
var value = s.toString()

var maskLength = 0
mMaskFormatter.get()?.maskLength?.let { maskLength = it}
mMaskFormatter.maskLength?.let { maskLength = it}

if (value.length > oldFormattedValue.length && maskLength < value.length) {
value = oldFormattedValue
}

val formattedString = mMaskFormatter.get()?.formatString(value)
val formattedString = mMaskFormatter.formatString(value)

formattedString?.let { setFormattedText(it) }
oldFormattedValue = formattedString.toString()
afterTextChanged?.invoke(s)
}

override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
mEditText.get()?.selectionStart?.let { this.oldCursorPosition = it }
beforeTextChanged?.invoke(s, start, count, after)
}

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
onTextChanged?.invoke(s, start, before, count)
}
}
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ It allows you to add a mask to EditText

# Version

5.0.0
5.0.1

# Installation

Expand All @@ -29,15 +29,15 @@ allprojects {

```sh
dependencies {
implementation 'io.github.vicmikhailau:MaskedEditText:5.0.0'
implementation 'io.github.vicmikhailau:MaskedEditText:5.0.1'
}
```

*Kotlin:*

```sh
dependencies {
implementation("io.github.vicmikhailau:MaskedEditText:5.0.0")
implementation("io.github.vicmikhailau:MaskedEditText:5.0.1")
}
```

Expand All @@ -57,8 +57,21 @@ Or add TextChangedListener for your EditText like in following code:
val formatter = MaskedFormatter("your_mask")
mEditText.addTextChangedListener(MaskedWatcher(formatter, mEditText))
```
Object of MaskedWatcher class has got a weakreference to formatter object, so you must to conside this.

Connect to the MaskedWatcher for your MaskedEditText through:

```
mMaskedEditText.addMaskedTextChangedListener
mMaskedEditText.doBeforeMaskedTextChanged
mMaskedEditText.doOnMaskedTextChanged
mMaskedEditText.doAfterMaskedTextChanged
```

Or use MaskedWatcher reference:

```
maskedWatcher.addTextChangedListener
```

**For create your mask you need to use following keys:**
```
ANYTHING KEY = *
Expand Down Expand Up @@ -96,6 +109,10 @@ Unfortunately, there is no way to devote much time to the project. Please feel f
# Change Logs
### v5.0.1
Added convenient MaskedEditText listeners.
### v5.0.0
Updated dependencies and targetSdk to 33.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.vicmikhailau.maskededittext.MaskedFormatter
import com.vicmikhailau.maskededittext.MaskedWatcher
import com.vicmikhailau.maskededittext.addMaskedTextChangedListener
import com.vicmikhailau.maskededittext.doAfterMaskedTextChanged
import com.vicmikhailau.maskededittext.doBeforeMaskedTextChanged
import com.vicmikhailau.maskededittext.doOnMaskedTextChanged
import com.vicmikhailau.maskededittextsample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
Expand Down Expand Up @@ -55,6 +59,26 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setMask("##/##/####")
setListener()
}

fun setListener() {
// Sample.
binding.apply{
edtMaskedCustomSample.apply {
// Common.
addMaskedTextChangedListener(
beforeMaskedTextChanged = { _: CharSequence?, _: Int, _: Int, _: Int -> },
onMaskedTextChanged = { _: CharSequence?, _: Int, _: Int, _: Int -> },
afterMaskedTextChanged = {}
)

// Separate.
doBeforeMaskedTextChanged { text, start, count, after -> }
doOnMaskedTextChanged { text, start, before, count -> }
doAfterMaskedTextChanged { }
}
}
}

// ===========================================================
Expand Down

0 comments on commit 6fa7ed2

Please sign in to comment.