Comments (16)
@qbait your solution seems fine. alternatively the textwatcher can be stored as a property in the @ModelView class - whatever you prefer
if you prefer to use a more kotlin like approach and avoid needing to subclass edit text you can use extension functions instead
here's a small sample I just wrote up to illustrate (this is just scratch untested code, but should give you the right idea)
fun EditText.addTextChangedListener(watcher: TextWatcher) {
addTextChangedListener(watcher)
getWatchers().add(watcher)
}
fun EditText.clearWatchers() {
getWatchers().clear()
}
private fun EditText.getWatchers(): MutableList<TextWatcher> {
return getTag(R.id.text_watchers) ?: run {
val newList = mutableListOf<TextWatcher>()
setTag(R.id.text_watchers, newList)
newList
}
}
@shakil807g calling removeTextChangedListener(null)
doesn't do anything, i wouldn't recommend that.
from mavericks.
I had a problem with attaching TextWatcher
to EditText
and recycling.
I solved it by having WatcherAwareEditText
public class WatcherAwareEditText extends TextInputEditText
{
private ArrayList<TextWatcher> mListeners = null;
public WatcherAwareEditText(Context ctx)
{
super(ctx);
}
public WatcherAwareEditText(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
public WatcherAwareEditText(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
@Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
@Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
}
and clearing all Watchers before setting new one in my Model
@CallbackProp
fun setWatcher(watcher: TextWatcher?) {
editText.clearTextChangedListeners()
watcher?.let {
editText.addTextChangedListener(it)
}
}
It works. However I'll be greatful for your feedback @elihart .
It's a pleasure to create forms with MvRx
and Epoxy
after setting up everything, but coming up with a good pattern isn't trivial. I could give my code as a sample. Let me know if I should create PR.
from mavericks.
@qbait It would be great if you could share your code as a sample, either as a PR, gist, or otherwise.
@elihart This topic seems to come up in both Epoxy and MvRx repos, and proper and clean way of handling this doesn't seem very obvious. Maybe an official sample/wiki page would be a good idea.
from mavericks.
I'm planning to prepare a sample code and post on Medium when I have more time. Here is the extension
fun EditText.setTextAndCursor(text: CharSequence) {
if(setText(this,text) ) {
this.setSelection(this.length())
}
}
private fun setText(textView: TextView, text: CharSequence): Boolean {
if (!isTextDifferent(text, textView.text)) {
return false
}
textView.text = text
return true
}
private fun isTextDifferent(str1: CharSequence?, str2: CharSequence?): Boolean {
if (str1 === str2) {
return false
}
if (str1 == null || str2 == null) {
return true
}
val length = str1.length
if (length != str2.length) {
return true
}
if (str1 is Spanned) {
return str1 != str2
}
for (i in 0 until length) {
if (str1[i] != str2[i]) {
return true
}
}
return false
}
Instead of onChange
, I'm passing normal TextWatcher
. Need to polish it when I'm free.
from mavericks.
This pattern looks right to me, what's the problem with recycling exactly? what is it not recycling?
from mavericks.
you can put some thing like this in your model
@OnViewRecycled
public void clear(){
editText.removeTextChangedListener(null)
}
instead of your WatcherAwareEditText
from mavericks.
Yes sorry my bad , i am using my model this way is there anything that could go wrong with this approach ?
@EpoxyModelClass(layout = R.layout.item_text_field)
abstract class EditTextModel : EpoxyModelWithHolder<EditTextModel.EditTextHolder>() {
@EpoxyAttribute
var text: String? = null
@EpoxyAttribute
var errorText: String? = null
@EpoxyAttribute
var hint : String? = null
@EpoxyAttribute
@DrawableRes
var prefIcon: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var textWachter: TextWatcher? = null
override fun bind(holder: EditTextModel.EditTextHolder) {
if(holder.editext.extSetText(text)){
holder.editext.setSelection(holder.editext.length())
}
holder.editext.error = errorText
holder.editext.addTextChangedListener(textWachter)
}
override fun unbind(holder: EditTextHolder) {
super.unbind(holder)
holder.editext.removeTextChangedListener(textWachter)
holder.editext.error = null
}
inner class EditTextHolder : EpoxyHolder() {
lateinit var editext : EditText
override fun bindView(itemView: View) {
editext = itemView.editText
}
}
from mavericks.
@elihart It was working great when I had 1 big scrolling form. Now I separated sections into separate fragments, but I left single shared ViewModel
.
Unfortunately with this approach I have problem with TextWatcher
s again.
What's the best approach here? should I have ViewModel
for every Fragment
or I can leave single Shared ViewModel
and additionally clear watchers somewhere in my View
.
from mavericks.
why do you have problems with separate fragments, what is the problem exactly? You've gotta give more details.
sharing a viewmodel should work theoretically, but it may not make the cleanest abstraction if the fragments are unrelated. clearing the watchers seems like it would always be a good idea
from mavericks.
Ok, @elihart, the problem was in clearing Watchers, I was doing it like that:
@CallbackProp
fun setWatcher(watcher: TextWatcher?) {
editText.clearWatchers()
watcher?.let {
editText.addWatcher(it)
}
}
I changed it to watch only when EditText has focus
editText.setOnFocusChangeListener { _, hasFocus ->
if(!hasFocus) {
editText.clearWatchers()
} else {
watcher?.let {
editText.addWatcher(it)
}
}
}
I'm curious where you clear you watchers.
from mavericks.
@qbait Can you please share the setTextAndCursor and onChange extension or a full example? Thanks
from mavericks.
@qbait thanks for the update. It seems odd to me that you are needing to clear the watcher when focus changes - it seems like something else is going on here that isn't right. You should only need to clear the watcher when the view is unbound/recycled, which happens automatically with a callback prop
from mavericks.
@elihart my clear function was invoked ~2s after new fragment was created and meantime it was making a mess.
@OnViewRecycled
public void clear(){
editText.clearWatchers()
}
I can prepare a sample project tomorrow if you would have time to take a look.
from mavericks.
@elihart and @qbait I use similar solution with the approach of yours, but I ran into an issue, that for my custom EditField view, that has inputType, imeOptions, @ModelProp annotated properties, and using @AfterPropsSet to bind these properties all together to the EditText. The issue was, because setState on the text change event, and the soft keyboard is displaying, the imeAction, and inputType also changed when the bind is called resulting the changing of the soft keyboard
switching keyboard action icons, and keyboard type (according to imeOptions and inputType). It seems if you use functions instead of properties this issue is solved. Or should I examine the equality of this properties, and only set them if they are different, just like the text in @qbait solution?
from mavericks.
hi guys - does anyone have an example now ? especially with a text field and a photo field (where we click a photo through phone camera).
We are not able to figure this out properly
from mavericks.
Yea I'm running into the same issue with multiple fragments. It seems the edittexts are being removed and the clear isn't called immediately so if I cycle between fragments quickly I can have text in an edittext in one fragment changing text in an edittext in a a different fragment
from mavericks.
Related Issues (20)
- Calling `mavericksActivityViewModel` in fragment, not working properly HOT 1
- Type com.airbnb.mvrx.AndroidStrictModeExtensionsKt is defined multiple times HOT 1
- possible incompatibility with Kotlin coroutines 1.7.0 HOT 2
- Using androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 problems HOT 13
- Fail State call twice and make error HOT 2
- how to use stateflow in MavericksViewModel HOT 2
- How can I use MavericksView in a View or a Layout
- compare with mvc,mvp,mvvm HOT 2
- The viewModel() method may be missing the postInvalidate() logic. HOT 2
- Hilt and ViewModel initialization with Navigation Component arguments HOT 6
- Exploration of the correct way to update state with List<Data> in state HOT 1
- Ensure that your state properties properly implement hashCode. HOT 7
- I wanted to use the latest version of Epoxy, Mavericks, Paging3 (Epoxy Paging3), but couldn't find the Demo. Is it possible to combine all three?
- java.lang.SecurityException: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified
- java.lang.IllegalStateException HOT 1
- Value classes break State class no zero argument constructor usage HOT 1
- Discuss whenStarted function deprecation HOT 7
- I am using MavericksViewModel on Compose, I want to get the LifecycleOwner object in ViewModel how do I need to get this? HOT 3
- Crash when creating MavericksViewModel HOT 6
- Combining Mavericks with "custom" network response and error handling HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mavericks.