Trigger Multiple Times TextEdit onTextChanged - android

My basic code is works, but then i confuse about trigger, when edit some editText. When editText is edit or change..
my reference's :
TextWatcher events are being fired multiple times
My input type Xml is number, and i try it. other method is use ignore, and still got same result, stuck. number|textNoSuggestions
Compare to this, android edittext onchange listener my code still make in sense..
The strange result from this :
Events of TextWatcher are being called twice
.
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
editModelArrayList.get(position).setEditTextValueCrt(holder.editTextCrt.getText().toString());
if (mToggle) {
if (mToggle2) {
Log.d(TAG, "onTextChanged: HIT KEY" + mToggle);
}
mToggle2 = !mToggle2;
}
mToggle = !mToggle;
}
The first code will only fires/trigger for 1 times, compare with 2nd codes will result multiple trigger at once :
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
editModelArrayList.get(position).setEditTextValueCrt(holder.editTextCrt.getText().toString());
if (mToggle) {
if (mToggle2) {
Log.d(TAG, "onTextChanged: HIT KEY" + mToggle);
try {
Integer val = Integer.valueOf(holder.editTextSeq.getText().toString());
if exist data previously, so never replace in seq only
}
catch (Exception e){
//get all data, if empty > set 1, else set : check all data, count data +1
List<Integer> listQtySeq;
listQtySeq = new ArrayList<Integer>();
for (int j = 0; j < CustomeAdapter.editModelArrayList.size(); j++){
String nilaiSeq = editModelArrayList.get(j).getEditTextValueSeq();
nilaiSeq = checkNull(nilaiSeq);
if (Integer.parseInt(nilaiSeq) > 0) {
listQtySeq.add(Integer.parseInt(nilaiSeq));
}
}
Log.d(TAG, "afterTextChanged: size llistq: "+listQtySeq.size());
if(listQtySeq.size()<1){
editModelArrayList.get(position).setEditTextValueSeq("1");
}
else{
Integer totSize = listQtySeq.size()+1;
Log.d(TAG, "afterTextChanged: size newZise: "+totSize.toString());
editModelArrayList.get(position).setEditTextValueSeq(totSize.toString());
}
Log.d(TAG, "onTextChanged: error ~" + e);
}
}
mToggle2 = !mToggle2;
}
mToggle = !mToggle;
}
I want to use that method to execute only once, with some customize code. My question is why my second code invoke multiple trigger, i didn't use loop, or else,,
Thanks for help.

Related

runtime TextWatcher control

I have 4 edittext and i would like to implement a TextWatcher with a control value.
Et1Burro.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable value) {
// you can call or do what you want with your EditText here
Dvalue = GetEditValue(value);
double et4tot = 0, et2fibra = 0, et3zucc = 0;
// et1burro + et2fibra = et4tot
// et1burro + et2fibra + et3zucc = 100
try {
et4tot = Double.parseDouble(Et4Tot.getText().toString());
} catch (NumberFormatException e) { e.printStackTrace(); }
try {
et2fibra = Double.parseDouble(Et2Fibra.getText().toString());
} catch (NumberFormatException e) { e.printStackTrace(); }
try {
et3zucc = Double.parseDouble(Et3Zucc.getText().toString());
} catch (NumberFormatException e) { e.printStackTrace(); }
if ((Dvalue < 1) || (Dvalue > 100) || ((Dvalue + et2fibra) != et4tot ) || ((Dvalue + et2fibra + et3zucc) != 100 ))
{
//segnala errore
Et1Burro.setTextColor(getActivity().getBaseContext().getResources().getColor(R.color.Red));
}else
Et1Burro.setTextColor(getActivity().getBaseContext().getResources().getColor(R.color.Black));
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
i would like to have a red number if the range number is wrong and a black number if is correct.
I think is better to implement a AsynckTask for control the number or not?
for example the 4 edittext value are:
A,B,C,D
the relation for correct value are:
A+B = D
A+B+C = 100
C = 100 - D
correct example value are A=35, B=35, C=30, D=70
but if in teh first edit (A) the user insert the first caracter ex 35 the program respond with RED value because the other value are 0, and when the user compile all the edittext with the value 35,35,30,70 the anly value that are Black is the last.
I hope to be clear...
Because you are updating only one edit text's color
What i would suggest is,
Set default color to RED for all edit texts
Write a function for your "control value" logic. Call this function only when there are values in all edit texts.
Update all the editbox's color to black if the values entered are passing your control logic. Else you have to set it to RED

InputFilter on EditText cause repeating text

I'm trying to implement an EditText that limits input to Capital chars only [A-Z0-9] with digits as well.
I started with the InputFilter method from some post.But here I am getting one problem on Samsung Galaxy Tab 2 but not in emulator or Nexus 4.
Problem is like this :
When I type "A" the text shows as "A" its good
Now when I type "B" so text should be "AB" but it gives me "AAB"
this looks very Strange.
In short it repeats chars
Here's the code I'm working with this code :
public class DemoFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
int dend) {
if (source.equals("")) { // for backspace
return source;
}
if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
// here
{
return source.toString().toUpperCase();
}
return "";
}
}
XML file code :
<EditText
android:id="#+id/et_licence_plate_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:hint="0"
android:imeOptions="actionNext"
android:inputType="textNoSuggestions"
android:maxLength="3"
android:singleLine="true"
android:textSize="18px" >
</EditText>
I'm totally stuck up on this one, so any help here would be greatly appreciated.
The problem of characters duplication comes from InputFilter bad implementation. Rather return null if replacement should not change:
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
boolean keepOriginal = true;
StringBuilder sb = new StringBuilder(end - start);
for (int i = start; i < end; i++) {
char c = source.charAt(i);
if (isCharAllowed(c)) // put your condition here
sb.append(c);
else
keepOriginal = false;
}
if (keepOriginal)
return null;
else {
if (source instanceof Spanned) {
SpannableString sp = new SpannableString(sb);
TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0);
return sp;
} else {
return sb;
}
}
}
private boolean isCharAllowed(char c) {
return Character.isUpperCase(c) || Character.isDigit(c);
}
I've run into the same issue, after fixing it with solutions posted here there was still a remaining issue with keyboards with autocomplete. One solution is to set the inputType as 'visiblePassword' but that's reducing functionality isn't it?
I was able to fix the solution by, when returning a non-null result in the filter() method, use the call
TextUtils.copySpansFrom((Spanned) source, start, newString.length(), null, newString, 0);
This copies the auto-complete spans into the new result and fixes the weird behaviour of repetition when selecting autocomplete suggestions.
I have found many bugs in the Android's InputFilter, I am not sure if those are bugs or intended to be so. But definitely it did not meet my requirements. So I chose to use TextWatcher instead of InputFilter
private String newStr = "";
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String str = s.toString();
if (str.isEmpty()) {
myEditText.append(newStr);
newStr = "";
} else if (!str.equals(newStr)) {
// Replace the regex as per requirement
newStr = str.replaceAll("[^A-Z0-9]", "");
myEditText.setText("");
}
}
#Override
public void afterTextChanged(Editable s) {
// Do nothing
}
});
The above code does not allow users to type any special symbol into your EditText. Only capital alphanumeric characters are allowed.
InputFilters can be attached to Editable S to constrain the changes that can be made to them.
Refer that it emphasises on changes made rather than whole text it contains..
Follow as mentioned below...
public class DemoFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
int dend) {
if (source.equals("")) { // for backspace
return source;
}
if (source.toString().matches("[a-zA-Z0-9 ]*")) // put your constraints
// here
{
char[] ch = new char[end - start];
TextUtils.getChars(source, start, end, ch, 0);
// make the characters uppercase
String retChar = new String(ch).toUpperCase();
return retChar;
}
return "";
}
}
The following solution also supports the option of an autocomplete keyboard
editTextFreeNote.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) {
String newStr = s.toString();
newStr = newStr.replaceAll( "[a-zA-Z0-9 ]*", "" );
if(!s.toString().equals( newStr )) {
editTextFreeNote.setText( newStr );
editTextFreeNote.setSelection(editTextFreeNote.getText().length());
}
}
#Override
public void afterTextChanged(Editable s) {}
} );
Same for me, InputFilter duplicates characters. This is what I've used:
Kotlin version:
private fun replaceInvalidCharacters(value: String) = value.replace("[a-zA-Z0-9 ]*".toRegex(), "")
textView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
val newValue = replaceInvalidCharacters(s.toString())
if (newValue != s.toString()) {
textView.setText(newValue)
textView.setSelection(textView.text.length)
}
}
})
works well.
try this:
class CustomInputFilter implements InputFilter {
StringBuilder sb = new StringBuilder();
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
Log.d(TAG, "filter " + source + " " + start + " " + end + " dest " + dest + " " + dstart + " " + dend);
sb.setLength(0);
for (int i = start; i < end; i++) {
char c = source.charAt(i);
if (Character.isUpperCase(c) || Character.isDigit(c) || c == ' ') {
sb.append(c);
} else
if (Character.isLowerCase(c)) {
sb.append(Character.toUpperCase(c));
}
}
return sb;
}
}
this also allows filtering when filter() method accepts multiple characters at once e.g. pasted text from a clipboard
I've met this problem few times before.
Setting some kinds of inputTypes in xml propably is the source of problem.
To resolve it without any additional logic in InputFilter or TextWatcher just set input type in code instead xml like this:
editText.setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
recently i faced same problem
reason of the problem is... if there is a no change in the input string then don't return source string return null, some device doesn't handle this properly that's why characters are repating.
in your code you are returning
return source.toString().toUpperCase();
don't return this , return null; in place of return source.toString().toUpperCase(); , but it will be a patch fix , it will not handle all scenarios , for all scenario you can use this code.
public class SpecialCharacterInputFilter implements InputFilter {
private static final String PATTERN = "[^A-Za-z0-9]";
// if you want to allow space use this pattern
//private static final String PATTERN = "[^A-Za-z\\s]";
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
// Only keep characters that are letters and digits
String str = source.toString();
str = str.replaceAll(PATTERN, AppConstants.EMPTY_STRING);
return str.length() == source.length() ? null : str;
}
}
what is happening in this code , there is a regular expression by this we will find all characters except alphabets and digits , now it will replace all characters with empty string, then remaining string will have alphabets and digits.
The problem with most the answers here is that they all mess up the cursor position.
If you simply replace text, your cursor ends up in the wrong place for the next typed character
If you think you handled that by putting their cursor back at the end, well then they can't add prefix text or middle text, they are always jumped back to the end on each typed character, it's a bad experience.
You have an easy way to handle this, and a more universal way to handle it.
The easy way
<EditText
android:id="#+id/itemNameEditText"
android:text="#={viewModel.selectedCartItemModel.customName}"
android:digits="abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
android:inputType="textVisiblePassword"/>
DONE!
Visible password will fix the issue of double callbacks and problems like that. Problem with this solution is it removes your suggestions and autocompletes, and things like that. So if you can get away with this direction, PLEASE DO!!! It will eliminate so many headaches of trying to handle every possible issue of the hard way lol.
The Hard Way
The issue is related to the inputfilter callback structure being triggered by autocomplete. It is easy to reproduce. Just set your inputType = text, and then type abc# you'll see it get called two times and if you can end up with abcabc instead of just abc if you were trying to ignore # for example.
First thing you have to handle is deleting to do this, you must
return null to accept "" as that is triggered by delete.
Second thing you have to handle is holding delete as that updates every so often, but can come in as a long string of characters, so you need to see if your text length shrunk before doing replacement text or you can end up duplicating your text while holding delete.
Third thing you need to handle is the duplicate callback, by keeping track of the previous text change call to avoid getting it twice. Don't worry you can still type the same letters back to back, it won't prevent that.
Here is my example. It's not perfect, and still has some kinks to work out, but it's a good place to start.
The following example is using databinding, but you are welcome to just use the intentFilter without databinding if that's your style. Abbreviated UI for showing only the parts that matter.
In this example, I restrict to alpha, numeric, and spaces only. I was able to cause a semi-colon to show up once while pounding on the android keyboard like crazy. So there is still some tweaking I believe that may need done.
DISCLAIMER
--I have not tested with auto complete
--I have not tested with suggestions
--I have not tested with copy/paste
--This solution is a 90% there solution to help you, not a battle tested solution
XML FILE
<layout
xmlns:bind="http://schemas.android.com/apk/res-auto"
>
<EditText
bind:allowAlphaNumericOnly="#{true}
OBJECT FILE
#JvmStatic
#BindingAdapter("allowAlphaNumericOnly")
fun restrictTextToAlphaNumericOnly(editText: EditText, value: Boolean) {
val tagMap = HashMap<String, String>()
val lastChange = "repeatCheck"
val lastKnownSize = "handleHoldingDelete"
if (value) {
val filter = InputFilter { source, start, end, dest, dstart, dend ->
val lastKnownChange = tagMap[lastChange]
val lastKnownLength = tagMap[lastKnownSize]?.toInt()?: 0
//handle delete
if (source.isEmpty() || editText.text.length < lastKnownLength) {
return#InputFilter null
}
//duplicate callback handling, Android OS issue
if (source.toString() == lastKnownChange) {
return#InputFilter ""
}
//handle characters that are not number, letter, or space
val sb = StringBuilder()
for (i in start until end) {
if (Character.isLetter(source[i]) || Character.isSpaceChar(source[i]) || Character.isDigit(source[i])) {
sb.append(source[i])
}
}
tagMap[lastChange] = source.toString()
tagMap[lastKnownSize] = editText.text.length.toString()
return#InputFilter sb.toString()
}
editText.filters = arrayOf(filter)
}
}

Output string with decimal format limited to 2 dp

Stuck on this which im sure there is an easy solution to, just cannot work it out!!
I have tried decmialformat, numberformat, string.format() etc and nothing works. .
code below, i want to calculation to just show the output limited to 2 decimal places. Have spent the last 2 hours trying various methods all of which causes the app to crash when run...
Output = (Output1 / (1 -(Output2/100)))
String OutputString = String.valueOf(Output);
Num.setText(OutputString);
Try this :
String OutputString = String.format("%.2f", Output);
Num.setText(OutputString);
String.format() to make sure you only get 2 decimal places in your output.
please try this:
double Output = (Output1 / (1 -(Output2/100d)))
Num.setText(String.format("%.2f",Output));
Hope this solves your problem.
Best regards
If you want to Limit the number of Digits before and after the 'decimal_point' then you can use my solution.
private class DecimalNumberFormatTextWatcher implements TextWatcher{
int pos;
int digitsBeforeDecimal = 6;
int digitsAfterDecimal = 2;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(s.length() > 2)
pos = start;
else {
pos = start + 2;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
mEdittext.removeTextChangedListener(this);
String text = s.toString();
if(text!= null && !text.equals("")){
if(!text.contains("$")){ //if it does not contains $
text = "$"+text;
} else {
if (text.indexOf("$") > 0) { //user entered value before $
text = s.delete(0, text.indexOf("$")).toString();
}else {
if(!text.contains(".")){ // not a fractional value
if(text.length() > digitsBeforeDecimal+1) { //cannot be more than 6 digits
text = s.delete(pos, pos+1).toString();
}
} else { //a fractional value
if(text.indexOf(".") - text.indexOf("$") > digitsBeforeDecimal+1){ //non fractional part cannot be more than 6
text = s.delete(pos,pos+1).toString();
}
if((text.length() - text.indexOf(".")) > digitsAfterDecimal+1) { //fractinal part cannot be more than 2 digits
text = s.delete(text.indexOf(".") + 2, text.length() - 1).toString();
}
}
}
}
}
mEdittext.setText(text);
mEdittext.setSelection(pos);
mEdittext.addTextChangedListener(this);
}
}
mEdittext.addTextChangedListener(new DecimalNumberFormatTextWatcher());
This also adds a currency sign as soon as the user types the value.
HOPE THIS HELPS ANYONE.

How to use replaceAll() correct

I have a hangman-app which I fetch a random word from Db i created, then I save it to randomedWord and then i make another String for holding randomedWord but replaced with only "_". This hiddenWord is displayed so the user knows how many chars there is.
When a user hits Enter a onlicklistener fires guess() method:
I have following code which initates a local String which has the value of a TextView(userInput). Then if randomedWord contains the guess I want to put in guess into the same position as it is in the randomedWord, but now to hiddenWord and then update the TextView again.
Guess method:
public void guess()
{
String guess = userInput.getText().toString();
if(randomedWord.contains(guess))
{
hiddenWord = hiddenWord.replaceAll(guess, guess);
this.wordHolder.setText(hiddenWord);
} else
{
showImages();
}
}
The problem I think is this line:
hiddenWord = hiddenWord.replaceAll(guess, guess);
because hiddenWord just contains "_" and therefore I can't replace with (guess, guess) where the first is WHAT to be replaced and last is WITHWHAT.
How do I replace the same POSITION as it is in randomedWord with guess into hiddenWord?
I would change your approach slightly. When a user inputs and guess() is called, find all occurences of that guess in randomedWord and then set the characters in hiddenWord to guess. Using StringBuilder and String.indexOf(), it would look something like this. Also, guess will need to be a char:
StringBuilder builder = new StringBuilder(hiddenWord);
int index = word.indexOf(guess);
while (index >= 0) {
builder.setCharAt(index, guess);
index = word.indexOf(guess, index + 1);
}
hiddenWord = builder.toString();
My solution to the problem looks like this:
// INVWORD METHOD WHICH TURNS THE FETCHED WORD INTO A COPY BUT ONLY "_"
public void invWord()
{
hiddenWord = randomedWord;
hiddenWord = hiddenWord.replaceAll(".", "_");
}
// GUESS METHOD WHICH IS CALLED WHEN ENTER-BUTTON ISCLICKED
public void guess() throws IndexOutOfBoundsException
{
char guess = userInput.getText().charAt(0);
StringBuilder builder = new StringBuilder(hiddenWord);
String j = ""+guess;
int index = randomedWord.indexOf(guess);
if (randomedWord.contains(j))
{
while (index >= 0)
{
builder.setCharAt(index, guess);
index = randomedWord.indexOf(guess, index + 1);
hiddenWord = builder.toString().trim();
wordHolder.setText(hiddenWord);
if (hiddenWord.trim().contains(randomedWord))
{
winner();
}
}
}
else
{
showImages();
}
}
Problem is that I would need to add a +" " to the hiddenWord = hiddenWord.replaceAll(".", "_"); to make the word be like _ _ _ _ _ and makes the user see how many ketters the word is. In my solution it will only be long ____, but it works. If I set index*2 at builder.setCharAt(index, guess); the formatting will be good but then winner(); is never run, I guess because the " " makes the hiddenWord no longer = ranomedWord.
How can I solve this?
Got it all solved! The feeling when everything works smoothly.... :)
Heres the code:
// Set listener to enter-button and do guess when textfield is not empty and toast that input need if it is
enterLetterButton.setOnClickListener(new OnClickListener(){
public void onClick(View arg0) {
if (!(userInput.getText().toString().isEmpty()) )
{
guess();
} else if (userInput.getText().toString().isEmpty())
{
Toast toast = Toast.makeText(getApplicationContext(), "You need to insert a letter", Toast.LENGTH_SHORT);
toast.show();
}
}
});
// invWord() method. Converts the randomedWord to a string of "_".
public void invWord()
{
hiddenWord = randomedWord;
hiddenWord = hiddenWord.replaceAll(".", "_" +" ");
}
//guess() method. Puts the guess(char) at the index fetched and if the hiddenWord contains no more "_" winner() is called
public void guess() throws IndexOutOfBoundsException
{
char guess = userInput.getText().charAt(0);
StringBuilder builder = new StringBuilder(hiddenWord);
String j = ""+guess;
int index = randomedWord.indexOf(guess);
if (randomedWord.contains(j))
{
while (index >= 0)
{
builder.setCharAt(index*2, guess);
index = randomedWord.indexOf(guess, index + 1);
hiddenWord = builder.toString().trim();
wordHolder.setText(hiddenWord);
if (!(hiddenWord.toString().contains("_".toString())) )
{
winner();
}
}
}
else
{
showImages();
}
}

Optimizing my syntax highlighter

I've decided I have to write my own syntax highlighter. So far it's working but it's realtime (you type, it highlights) and it's slow.
I'll try to explain how it works. Each time the user types something into the EditText it runs the highlighter (via TextWatcher). The highlighter searches through the text until it finds the beginning of a word and then searches until it finds the end of the same word. Once it finds a word it searches through an array of keywords, if it finds a match it sets a spannable at that location. It keeps looping until it reaches the end of the document.
Again, it works so far (just trying out this idea before I continue with this method), but it's so slow. Some times it can take over a second just to go through a few lines. It slows down how fast the text appears in the EditText. - I also set where the highlighter starts after text is entered at the last position where the user typed so it doesnt have to go through the whole doc each time, it helps a little but not much.
Here's the basic of my EditText:
public class CodeView extends EditText {
private int mTxtChangeStart;
String mStructures[] = this.getResources().getStringArray(R.array.structures);
public CodeView(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(inputTextWatcher);
...
}
TextWatcher inputTextWatcher = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
syntaxHighlight();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
//Set where we should start highlighting
mTxtChangeStart = start;
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
};
private void syntaxHighlight() {
//Time how long it takes for debugging
long syntime = System.currentTimeMillis();
Log.d("", "Start Syntax Highlight");
//Get the position where to start searching for words
int strt = mTxtChangeStart;
//Get the editable text
Editable txt = getText();
//Back up the starting position to the nearest space
try {
for(;;) {
if(strt <= 0) break;
char c = txt.charAt(strt);
if(c != ' ' && c != '\t' && c != '\n' && c != '\r') {
strt--;
} else {
break;
}
}
} catch (IndexOutOfBoundsException e) {
Log.e("", "Find start position failed: " + e.getMessage());
}
//Just seeing how long this part took
long findStartPosTime = System.currentTimeMillis();
Log.d("", "Find starting position took " + String.valueOf(System.currentTimeMillis() - findStartPosTime) + " milliseconds");
//the 'end of a word' position
int fin = strt;
//Get the total length of the search text
int totalLength = txt.length();
//Start finding words
//This loop is to find the first character of a word
//It loops until the current character isnt a space, tab, linebreak etc.
while(fin < totalLength && strt < totalLength) {
for(;;) {
//Not sure why I added these two lines - not needed here
//fin++;
//if(fin >= totalLength) { break; } //We're at the end of the document
//Check if there is a space at the first character.
try {
for(;;) { //Loop until we find a useable character
char c = txt.charAt(strt);
if (c == ' ' || c == '\t' || c == '\n' || c == '\r'){
strt++; //Go to the next character if there is a space
} else {
break; //Found a character (not a space, tab or linebreak) - break the loop
}
}
}catch(IndexOutOfBoundsException e) {
Log.e("", e.getMessage());
break;
}
//Make sure fin isnt less than strt
if(strt > fin) { fin = strt; }
//Now we search for the end of the word
//Loop until we find a space at the end of a word
try {
for(;;) {
char c = txt.charAt(fin);
if(c != ' ' && c != '\t' && c != '\n' && c != '\r') {
fin++; //Didn't find whitespace here, keep looking
} else {
break; //Now we found whitespace, end of a word
}
}
break;
} catch (IndexOutOfBoundsException e) {
//If this happens it should mean it just reached the end of the document.
Log.e("", "End of doc? : " + e.getMessage());
break;
}
}
Log.d("", "It took " + String.valueOf(System.currentTimeMillis() - findStartPosTime) + " milliseconds to find a word");
//Make sure fin isnt less that start, again
if(strt > fin) { fin = strt; }
//Debug time, how long it took to find a word
long matchTime = System.currentTimeMillis();
//Found a word, see if it matches a word in our string[]
try {
for(String mStruct : mStructures) {
if(String.valueOf(txt.subSequence(strt, fin)).equals(mStruct)) {
//highlight
Spannable s = (Spannable) txt;
s.setSpan(new ForegroundColorSpan(Color.RED), strt, fin, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//Can someone explain why this is still setting the spannable to the main editable???
//It should be set to txt right???
break;
} else {
/*Spannable s = (Spannable) txt;
s.setSpan(new ForegroundColorSpan(Color.BLACK), strt, fin, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
txt.removeSpan(s);*/
}
}
}catch (IndexOutOfBoundsException e) {
e.printStackTrace();
Log.e("", "word match error: " + e.getMessage());
}
//Finally set strt to fin and start again!
strt = fin;
Log.d("", "match a word time " + String.valueOf(System.currentTimeMillis() - matchTime) + " milliseconds");
}//end main while loop
Log.d("", "Syntax Highlight Finished in " + (System.currentTimeMillis() - syntime) + " milliseconds");
mTextChanged = false;
}
}
"structures" resource (php.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="structures">
<item>if</item>
<item>else</item>
<item>else if</item>
<item>while</item>
<item>do-while</item>
<item>for</item>
<item>foreach</item>
<item>break</item>
<item>continue</item>
<item>switch</item>
<item>declare</item>
<item>return</item>
<item>require</item>
<item>include</item>
<item>require_once</item>
<item>include_once</item>
<item>goto</item>
</string-array>
</resources>
Anyone have any suggestions how to make this search faster? I know I have a lot of loops but I'm not sure how else to do it.
Thanks a lot!
Can you split the string on the delimiters you have there rather than looking at each character? That would speed it up some. (String.split())

Categories

Resources