From 6fa7ed27edf42247a0f3dbc1287c0ce40858c28e Mon Sep 17 00:00:00 2001 From: vicmikhailau Date: Tue, 8 Aug 2023 17:52:38 +0300 Subject: [PATCH] [+] Added convenient MaskedEditText listeners. --- MaskedEditText/build.gradle | 2 +- .../maskededittext/MaskedEditText.kt | 86 ++++++++++++++++--- .../maskededittext/MaskedWatcher.kt | 42 +++++++-- README.md | 27 ++++-- .../maskededittextsample/MainActivity.kt | 24 ++++++ 5 files changed, 158 insertions(+), 23 deletions(-) diff --git a/MaskedEditText/build.gradle b/MaskedEditText/build.gradle index 8af4021..4ee3fa0 100644 --- a/MaskedEditText/build.gradle +++ b/MaskedEditText/build.gradle @@ -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" diff --git a/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedEditText.kt b/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedEditText.kt index 7aa9f75..4fa92a6 100644 --- a/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedEditText.kt +++ b/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedEditText.kt @@ -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.* @@ -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 } @@ -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) } } @@ -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 + ) } diff --git a/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedWatcher.kt b/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedWatcher.kt index 6c93ed9..c8e9eea 100644 --- a/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedWatcher.kt +++ b/MaskedEditText/src/main/java/com/vicmikhailau/maskededittext/MaskedWatcher.kt @@ -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 @@ -16,15 +16,39 @@ class MaskedWatcher(maskedFormatter: MaskedFormatter, editText: EditText) : Text // Fields // =========================================================== - private val mMaskFormatter: WeakReference = WeakReference(maskedFormatter) + private val mMaskFormatter: MaskedFormatter = maskedFormatter private val mEditText: WeakReference = 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 @@ -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)) @@ -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) + } } diff --git a/README.md b/README.md index 4d976ff..5922723 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ It allows you to add a mask to EditText # Version -5.0.0 +5.0.1 # Installation @@ -29,7 +29,7 @@ allprojects { ```sh dependencies { - implementation 'io.github.vicmikhailau:MaskedEditText:5.0.0' + implementation 'io.github.vicmikhailau:MaskedEditText:5.0.1' } ``` @@ -37,7 +37,7 @@ dependencies { ```sh dependencies { - implementation("io.github.vicmikhailau:MaskedEditText:5.0.0") + implementation("io.github.vicmikhailau:MaskedEditText:5.0.1") } ``` @@ -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 = * @@ -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. diff --git a/app/src/main/java/com/vicmikhailau/maskededittextsample/MainActivity.kt b/app/src/main/java/com/vicmikhailau/maskededittextsample/MainActivity.kt index c691724..851c1a1 100644 --- a/app/src/main/java/com/vicmikhailau/maskededittextsample/MainActivity.kt +++ b/app/src/main/java/com/vicmikhailau/maskededittextsample/MainActivity.kt @@ -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() { @@ -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 { } + } + } } // ===========================================================