I want to display some text in an EditText and do some work right away after the text is displayed. I have the following code in my onCreate() method:
this.editor.setText(text, TextView.BufferType.EDITABLE);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
// Work that needs to be done right after the text is displayed
}
}, 1000);
This works OK, but I want to minimize the delay between setText() rendering and the work being done-- a 1s delay is unacceptable. However, if I change the delay to 0ms or 1ms, then the work is done before the text gets rendered.
I could keep typing in numbers to search for the perfect delay time that would execute my code just after the text was rendered, but that seems very tedious/imprecise. Is there a better way to tell Android to run a callback right after that happens? Thanks.
edit: Here are two things I've tried that didn't work. For bonus points, it would be very helpful if you could explain to me why these didn't work.
Using Handler.post
new Handler(Looper.getMainLooper()).post(r) also runs r before the text finishes rendering. I thought setText adds rendering code to the queue, so shouldn't post(r) being called after that add r after the rendering code?
Using View.post
this.editor.post(r) didn't work either, r is still called before the text is rendered.
Use this it would hlp
mSongNameTextView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
You can assign a TextWatcher to your EditText.
A TextWatcher is basically a listener that listens for changes to the text (before, during and after) in the EditText.
It can be implemented as follows:
EditText et;
et.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// Work that needs to be done right after the text is displayed
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
}
So when you set the text explicitly, this listener should be called and after the text is changed, the // Work that needs to be done right after the text is displayed code will be run.
You can use ViewTreeObserver as below:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// do your work here. This call back will be called after view is rendered.
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// or below API 16: yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
I initially wanted to delay the work because it was CPU-intensive. I realized the solution was to spin up a new thread for the work, rather than post it to the UI thread.
Related
I have made an EditText where I type inside it some name.
I had like the app to perform search every time something changed in my EditText.
I used the following code:
txt_search.addTextChangedListener( new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
query = txt_search.getText().toString();
fetchBooks(query);
}
#Override
public void afterTextChanged(Editable s) {
query = txt_search.getText().toString();
fetchBooks(query);
}
} );
Where fetchBooks is the method that performs the search inside the API based on the query.
The problem im facing is that sometimes the search is stuck. If for example, I type pretty fast, it gives the results only for the first few letters and not for the whole query in the end.
Eventually, what I'm trying to get is that the app will constantly perform search based on the text inside the EditText.
Is there a way to obtain it without getting stuck results?
Thank you
First:
Remove search operation code for AfterChangedListener because after text is entered in EditText completely your search operation execute on both onTextChanged listener and afterTextChanged Listener
Second Replace txt_search.getText().toString(); with 's.toString()'
Change your TextWatcher as below
txt_search.addTextChangedListener( new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
query = s.toString();
fetchBooks(query);
}
#Override
public void afterTextChanged(Editable s) {
}
} );
For more information I recommend you to see below links:
getText vs onTextChanged charSequence
android Edittext with textwatcher
There's a similar answer here, but it doesn't state what I'm about to ask.
I have a 'Bold Text' option in my Android view and if a user selects that option, a boolean gets set as true in the code behind.
Using a TextWatcher, how can I change the text typed by the user to bold after a specific point in the EditText. If the user turns it off, the text typed after should be in normal styling. Everything depends on the boolean value.
Here's what I have so far:
Boolean isBolded = false;
// Code that turns the bold option true and false...
contentBox = (EditText) findViewById(R.id.contentBox);
contentBox.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(isBolded)
{
//Start bolding the text typed after that point
}
else
{
//Stop styling the text typed after that point
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
The difficulty would be to determine the points in the EditText where the user decides to turn bolding off and on while typing. What do you guys have in mind?
Set Html.fromHtml("<b>This part will be bold</b> This won't!") as the Text.
I'm brainstorming the next phase of my project, and I'm not certain which path to take. My idea is to have 2 EditText fields, one beneath the other. When the user types a unique ID into the first field, the second field will populate automatically with corresponding text. For example, entering "X5432" in box 1 will put "1957 Thunderbird" in box 2. I estimate having about 500 value pairs to work from, so I assume a SQLite structure would be better than just using arrays. Any suggestions would be appreciated. Thanks.
I think you should use TextWatcher.
Just get value from edittext1
Find appropriate text for edittext2
Set text to second edittext.
edittext.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// do some stuff
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// do some stuff
}
#Override
public void afterTextChanged(Editable s) {
// do some stuff
}
});
I have 3 EditText elements, and I want to jump from one field to the next if there are 4 characters in the input.
I use a TextWatcher for this:
getEditView(R.id.edit_code1).addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable input) {
if(input.length() == 4){
getEditView(R.id.edit_code2).requestFocus();
}
}
});
The inputType for the EditText is "textCapCharacters"
When doing a longpress on a key to get a number, like holding R to get a 4, most devices will not add the letter, until the user stops holding the button, and will fire afterTextChanged after the letter 4 is selected. On a HTC keyboard (In this case the HTC Desire on 2.3.5) this is not the case. Even though the user is still holding the button, the R is added to the EditText and afterTextChanged is fired, and the code does its job and puts the focus on the next field.
How can this undesired behavior be prevented? Watching onKeyDown/Up won't help, because it doesn't register normal keystrokes.
getEditView(R.id.edit_code1).addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
if(input.length() >3){
getEditView(R.id.edit_code2).requestFocus();
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable input) {
}
}
});
Unfortunately it's a bug in the HTC keyboard with no way of fixing it. This being an event in the IME, it is impossible intercept touch or key events, so you would probably be better off simply making the user skip to the next input instead of you doing it for them.
If it's that important for you though, you can implement your own kind of keyboard there, with only the keys you need.
In my Android application I need to implement a TextWatcher interface to implement onTextChanged. The problem I have is, I want to update the same EditText With some extra string. When I try to do this the program terminates.
final EditText ET = (EditText) findViewById(R.id.editText1);
ET.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
try
{
ET.setText("***"+ s.toString());
ET.setSelection(s.length());
}
catch(Exception e)
{
Log.v("State", e.getMessage());
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
#Override
public void afterTextChanged(Editable s)
{
}
});
My program terminates and even I try to catch the exception like in my code still it terminates.
Does anyone have any idea why this happens and how I can achieve this? Thanks.
The content of the TextView is uneditable on the onTextChanged event.
Instead, you need to handle the afterTextChanged event to be able to make changes to the text.
For more thorough explanation see: Android TextWatcher.afterTextChanged vs TextWatcher.onTextChanged
Note: Error onTextChanged
Obvioulsy, you are causing an endless loop by continuously changing the text on afterTextChanged event.
From the ref:
public abstract void afterTextChanged (Editable s)
This method is called to notify you that, somewhere within s, the text has been
changed. It is legitimate to make further changes to s from this
callback, but be careful not to get yourself into an infinite loop,
because any changes you make will cause this method to be called again
recursively. ...
Suggestion 1: if you can, check if the s is already what you want when the event is triggered.
#Override
public void afterTextChanged(Editable s)
{
if( !s.equalsIngoreCase("smth defined previously"))
s = "smth defined previously";
}
Suggestion 2: if you need to do more complex stuff (formatting,
validation) you can maybe use a synchronized method like in this
post.
Note 2 : Formatting the input as partially hidden with n stars till the last 4 chars ( ****four)
You can use something like this in suggestion 1:
#Override
public void afterTextChanged(Editable s)
{
String sText = ET.getText().toString()
if( !isFormatted(sText))
s = format(sText);
}
bool isFormatted(String s)
{
//check if s is already formatted
}
string format(String s)
{
//format s & return
}
To supplement Zortkun's answer (where the example code is quite broken), this is how you'd use afterTextChanged() to update the same EditText:
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable editable) {
if (!editable.toString().startsWith("***")) {
editable.insert(0, "***");
}
}
});
Get familiar with the Editable interface to learn about other operations besides insert().
Note that it's easy to end up in an infinite loop (the changes you do trigger afterTextChanged() again), so typically you'd do your changes inside an if condition, as above.
As afterTextChanged() javadocs say:
It is legitimate to make further changes to s from this callback, but
be careful not to get yourself into an infinite loop, because any
changes you make will cause this method to be called again
recursively.
late answer, if someone looking this is how i did it.
set addTextChangedListener initially
in one of the call back (say onTextChanged()) remove addTextChangedListener
Still interested in receiving updates add it back again.
here is the code.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
Log.d("log", "before");
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("log", "after");
editText.removeTextChangedListener(this);
ediText.setText("text you wanted to put");
editText.addTextChangedListener(this);
}
#Override
public void afterTextChanged(Editable s) {
}
});
Here is a snippet that worked for me
etPhoneNumber.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
#Override
public void afterTextChanged(Editable s) {
if (!s.toString().equals(Utils.getFormattedNumber(s.toString()))) {
s.replace(0, s.length(), Utils.getFormattedNumber(s.toString()));
}
}
});
where Utils.getFormattedPhoneNumber() is your method returning a formatted number