Binary Search Vietnamese word from array string in java android - android

I am making an android dictionary Vietnam - English (over 20,000 entries).
In my application contain edittext and Listview.
To search item in my listview, i use Binary Search.
But the problem is with the search method and Vietnamese, it doesn't search well
some words it can find, some it can't.
Following is my search code to find word by prefix when edittext change.
public void searchWords(String[] words, String prefix)
{
int first = 0, last = words.length - 1;
int mid = 0;
while (first <= last)
{
mid = (first + last) / 2;
int c = prefix.compareTo(words[mid]);
if (c == 0)
{
first = mid; // first indicates the beginning
break;
}
if (c > 0)
first = mid + 1;
else
last = mid - 1;
}
int i;
for (i = first; i < words.length; i++)
{
if (words[i].startsWith(prefix))
{
pos=i;
break;
}
}
lv.setSelection(pos);
// Toast.makeText(getApplicationContext(), ""+pos,Toast.LENGTH_SHORT).show();
}
and onTextchange code i call my method like this:
public void onTextChanged(CharSequence s, int start, int before, int count){
// TODO Auto-generated method stub
searchWords(w,s.toString());
}
and this is how i load my entries from database to array:
d=handle.retrieve();
if(d.moveToFirst())
{
do
{
w[ii++]=d.getString(1);
}while(d.moveToNext());
}
So, what should i do to make my search working correctly?

Binary Search with "compareTo()" works only with SORTED words (or sorted Strings, i.e. sorted in alphabetic sequence -regardless of language). Meaning: Array starts with A, AA, AAA,... and ends with z, zz, zzz...Btw, UPPER case before lower case. Example:
String[] seq = {"Ape", "Bird", "Donkey", "Eagle", "Fish", "Gnu", "Horse", "Koala"};
String[] ran = {"Gnu", "Koala", "Horse", "Fish", "Bird", "Donkey", "Eagle", "Ape"};
String[] queries = {"Eagle", "Bird", "Donkey", "Fish", "Ape", "Horse", "Eagle", "Gnu"};
// binarySearch with random[] will produce unpredictable results when prefix starts with
// a word that is either after "Bird" or "Ape" or "Gnu"
for (String s : queries) {
System.out.println("Random: BS for " + s + " = " + binarySearch(ran, s));
}
//
// binarySearch with sequence[] will produce correct results whatever prefix is.
for (String s : queries) {
System.out.println("Sequence: BS for " + s + " = " + binarySearch(seq, s));
}
//-------------------------------------------------------------------------
public static int binarySearch(String[] words, String value) {
return binarySearch(words, value, 0, words.length - 1);
}
//
public static int binarySearch(String[] words, String value, int min, int max) {
if (min > max) {
return -1;
}
int mid = (max + min) / 2;
if (words[mid].equals(value)) return mid;
if(words[mid].compareTo(value) > 0)
return binarySearch(words, value, min, mid - 1);
return binarySearch(words, value, mid + 1, max);
}

Related

Android: Space after every 10 digit in edit text using text watcher

In android edit text, how to separate 10 digit number input by space? I am using android text watcher and I am trying to input multiple 10 digit numbers in the field. The issue arises when multiple numbers are copied and pasted in the field and that time, it doesn't take those spaces. Kindly let me know a solution in order to allow multiple number input with a space after every 10 digit number, when the number is copied from other place.
This will work for both type and copy/paste from other place.
yourEditText.addTextChangedListener(new TextWatcher() {
private static final char space = ' ';
#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) {
int pos = 0;
while (true) {
if (pos >= s.length()) break;
if (space == s.charAt(pos) && (((pos + 1) % 11) != 0 || pos + 1 == s.length())) {
s.delete(pos, pos + 1);
} else {
pos++;
}
}
pos = 10;
while (true) {
if (pos >= s.length()) break;
final char c = s.charAt(pos);
if (Character.isDigit(c)) {
s.insert(pos, "" + space);
}
pos += 11;
}
}
});
Edit and Use the following code as per your needs
StringBuilder s;
s = new StringBuilder(yourTxtView.getText().toString());
for(int i = 10; i < s.length(); i += 10){
s.insert(i, " "); // this line inserts a space
}
yourTxtView.setText(s.toString());
and when you need to get the String without spaces do this:
String str = yourTxtView.getText().toString().replace(" ", "");

Android edit text decimal format

Can I ask how to format string value e.g. 5000000.00 to 5,000,000.00? Apparently I'm doing currency related stuff for android application, I can managed to just format string value 5000000 to 5,000,000 without the dot separator in the edit text. I would like to store the string value for later to be used to parseDouble so that I will need to calculate and have some decimals. I managed to do with just comma separator but any idea on how to make the dot to be shown in the edit text as well?
The following is my code:
amountText.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) {
amountText.removeTextChangedListener(this);
if(!amountText.getText().toString().equals(""))
{
try {
String editText = amountText.getText().toString();
String newStr = editText.replace("$", "").replace(",", "");
customer.getProperty().get(groupPosition).setAmount(newStr);
String formattedString = formatString(customer.getProperty().get(groupPosition).getAmount());
amountText.setText(formattedString);
amountText.setSelection(amountText.getText().length());
// to place the cursor at the end of text
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
amountText.addTextChangedListener(this);
}
});
public String formatString(String s)
{
String givenstring = s.toString();
Long longval;
if (givenstring.contains(",")) {
givenstring = givenstring.replaceAll(",", "");
}
longval = Long.parseLong(givenstring);
DecimalFormat formatter = new DecimalFormat("#,###,###");
String formattedString = formatter.format(longval);
return formattedString;
}
I have tested use parseDouble but when I input "." in EditText, it just won't appear, and if I used long variable instead, it will give wrong format and error. (java.lang.NumberFormatException: Invalid long: "500000.00"). All values are done in string and later processing I will just parse the value when doing calculation.
Thank you and appreciate for anyone guidance and I apologize if there exists the post that is similar as I did not manage to find solution yet.
This is working & fully tested code just copy & paste it to try
TextWatcher amountTextWatcher = 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) {
int cursorPosition = etAmount.getSelectionEnd();
String originalStr = etAmount.getText().toString();
//To restrict only two digits after decimal place
etAmount.setFilters(new InputFilter[]{new MoneyValueFilter(Integer.parseInt(2))});
try {
etAmount.removeTextChangedListener(this);
String value = etAmount.getText().toString();
if (value != null && !value.equals("")) {
if (value.startsWith(".")) {
etAmount.setText("0.");
}
if (value.startsWith("0") && !value.startsWith("0.")) {
etAmount.setText("");
}
String str = etAmount.getText().toString().replaceAll(",", "");
if (!value.equals(""))
etAmount.setText(getDecimalFormattedString(str));
int diff = etAmount.getText().toString().length() - originalStr.length();
etAmount.setSelection(cursorPosition + diff);
}
etAmount.addTextChangedListener(this);
} catch (Exception ex) {
ex.printStackTrace();
etAmount.addTextChangedListener(this);
}
}
}
};
etAmount.addTextChangedListener(amountTextWatcher);
Here is method to add comma seperator to decimal number
/**
* Get decimal formated string to include comma seperator to decimal number
*
* #param value
* #return
*/
public static String getDecimalFormattedString(String value) {
if (value != null && !value.equalsIgnoreCase("")) {
StringTokenizer lst = new StringTokenizer(value, ".");
String str1 = value;
String str2 = "";
if (lst.countTokens() > 1) {
str1 = lst.nextToken();
str2 = lst.nextToken();
}
String str3 = "";
int i = 0;
int j = -1 + str1.length();
if (str1.charAt(-1 + str1.length()) == '.') {
j--;
str3 = ".";
}
for (int k = j; ; k--) {
if (k < 0) {
if (str2.length() > 0)
str3 = str3 + "." + str2;
return str3;
}
if (i == 3) {
str3 = "," + str3;
i = 0;
}
str3 = str1.charAt(k) + str3;
i++;
}
}
return "";
}
Method to restrict only two digits after decimal place in edittext
/**
* Restrict digits after decimal point value as per currency
*/
class MoneyValueFilter extends DigitsKeyListener {
private int digits;
public MoneyValueFilter(int i) {
super(false, true);
digits = i;
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
CharSequence out = super.filter(source, start, end, dest, dstart, dend);
// if changed, replace the source
if (out != null) {
source = out;
start = 0;
end = out.length();
}
int len = end - start;
// if deleting, source is empty
// and deleting can't break anything
if (len == 0) {
return source;
}
int dlen = dest.length();
// Find the position of the decimal .
for (int i = 0; i < dstart; i++) {
if (dest.charAt(i) == '.') {
// being here means, that a number has
// been inserted after the dot
// check if the amount of digits is right
return getDecimalFormattedString((dlen - (i + 1) + len > digits) ? "" : String.valueOf(new SpannableStringBuilder(source, start, end)));
}
}
for (int i = start; i < end; ++i) {
if (source.charAt(i) == '.') {
// being here means, dot has been inserted
// check if the amount of digits is right
if ((dlen - dend) + (end - (i + 1)) > digits)
return "";
else
break; // return new SpannableStringBuilder(source,
// start, end);
}
}
// if the dot is after the inserted part,
// nothing can break
return getDecimalFormattedString(String.valueOf(new SpannableStringBuilder(source, start, end)));
}
}
Try this:
public void afterTextChanged(Editable view) {
String s = null;
try {
// The comma in the format specifier does the trick
s = String.format("%,d", Long.parseLong(view.toString()));
} catch (NumberFormatException e) {
}
// Set s back to the view after temporarily removing the text change listener
}
Source: How to Automatically add thousand separators as number is input in EditText

Auto-capitalize first letters in compound names (EditText, Android) [duplicate]

This question already has answers here:
android - text input type person name isn't working
(4 answers)
Closed 7 years ago.
I need to automatic capitalise both first letters in compound names, for example: Gay-Lussac (French physicist), Иванов-Вано (Russian animation director).
Android EditText capitalises only first letter in whole name, and I get Gay-lussac and Иванов-вано.
Is there any method?
My EditText is
<EditText
android:id="#+id/nameET"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="31dp"
android:hint="#string/hint_name"
android:imeOptions="actionNext"
android:inputType="textCapWords|textCapSentences|textPersonName"
android:singleLine="true" />
Also I tried (also it does not work for second part):
<EditText
...
android:capitalize="words"
android:singleLine="true" />
and
<EditText
...
android:capitalize="words"
android:inputType="textCapWords|textCapSentences"
android:singleLine="true" />
You could operate in 2 steps:
1 - Have your strings spaced (ie.: "Gay - Lussac") in strings.xml, and let Android capitalize them.
2 - Then replace " - " with "-", in Java.
[EDIT]
If the input is from the user, then replace step 1 step:
1 - First, replace the user input string: "-" with " - "
You should create a Class that contains a static method like this:
public static CharSequence capitalize(CharSequence name){
if(name == null) return null;
String sName = name.toString().toLowerCase().trim();
String result = "";
sName = capFirst(sName);
if(sName.contains("-")){
result = "";
String[] subParts = sName.split("-");
for(int i = 0; i < subParts.length; i++){
String subPart = subParts[i];
result += capFirst(subPart);
if(i < subParts.length - 1) result += "-";
}
}
else result = sName;
return result.trim();
}
private static String capFirst(String text){
if(text == null) return null;
text = text.toLowerCase().trim();
String result = "";
if(text.length() > 0){
text = text.substring(0, 1).toUpperCase() + text.substring(1);
if(text.contains(" ")){
for(String word : text.split(" ")){
word = word.trim();
if(word.length() > 0) result += " " + word.substring(0, 1).toUpperCase() + word.substring(1);
}
}
else result = text;
}
return result.trim();
}
Then add a listener to the apropriate event of your EditText and set it´s text using YourClass.capitalize().
I made my own capitalisation.
It capitalise last entered character if this character is last character in string and it is located after any non-letter character (or it is single character in string).
public class EditName extends EditText {
public EditName(Context context, AttributeSet attrs) {
super(context, attrs);
addTextChangedListener(new TextWatcher() {
String lastChanged = "";
boolean isLastSingleCharChanged = false;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// check if only last character was changed
isLastSingleCharChanged =
count - before == 1 // was added one character
&& start + count == s.length() // last character was in changed group
&& (start == 0 || count == 1); // or total change, or last character
}
#Override
public void afterTextChanged(Editable s) {
if (!isLastSingleCharChanged) return; // only if last character changed (see above)
int len = s.length();
if (len == 0) return;
if (len > 1 && Character.isLetter(s.charAt(len - 2))) return; // correct letter only after none-letter
char c = s.charAt(len - 1);
if (!Character.isLetter(c)) return;
if (!Character.isLowerCase(c)) return;
String text = s.toString();
if (text.equals(lastChanged)) return; // do not do the same correction twice
lastChanged = text;
EditName.this.setText(text.substring(0, text.length() - 1)
+ Character.toUpperCase(c));
EditName.this.setSelection(text.length()); // position cursor
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
});
}
}

Unable to input zero after decimal point in Android EditText

I have an Android EditText which when a user puts a number, it edits the number and adds thousand separators using Decimal Format, but when one is inputting floating point numbers, i does not add zeros after the decimal point. so i can not input 1.000000008 because the zeros won't go on but other numbers do.
Is there any java DecimalFormat pattern that will allow a user to input a zero after the decimal point?
Here's the code for my EditText.
am = new TextWatcher(){
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())))
{
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
}
public void afterTextChanged(Editable s) {
amount.removeTextChangedListener(this);
amount2.setText(s.toString());
try {
int inilen, endlen;
inilen = amount.getText().length();
String v = s.toString().replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
Number n = df.parse(v);
value = Double.parseDouble(v);
int cp = amount.getSelectionStart();
if (hasFractionalPart) {
amount.setText(df.format(n));
} else {
amount.setText(dfnd.format(n));
}
endlen = amount.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= amount.getText().length()) {
amount.setSelection(sel);
} else {
// place cursor at the end?
amount.setSelection(amount.getText().length() - 1);
}
} catch (NumberFormatException nfe) {
// do nothing?
} catch (ParseException e) {
// do nothing?
}
amount.addTextChangedListener(this);
}
};
Rewrite
First, when a decimal symbol is present let's count how many zeros will be trimmed off by the formatter. (If we find a non-zero character after the decimal, we'll reset our count. For example 1.00200 only has two trailing zeros.) In onTextChanged():
int index = s.toString().indexOf(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator()));
trailingZeroCount = 0;
if (index > -1)
{
for (index++; index < s.length(); index++) {
if (s.charAt(index) == '0')
trailingZeroCount++;
else {
trailingZeroCount = 0;
}
}
hasFractionalPart = true;
} else {
hasFractionalPart = false;
}
Next, append the appropriate number of zero's back on to the formatted String. In afterTextChanged():
if (hasFractionalPart) {
StringBuilder trailingZeros = new StringBuilder();
while (trailingZeroCount-- > 0)
trailingZeros.append('0');
amount.setText(df.format(n) + trailingZeros.toString());
} else {
amount.setText(dfnd.format(n));
}
Note: You haven't posted the formats you use, so I've had to make a few assumptions, but this is easily adaptable.

How can I implement digit grouping input mask using InputFilter?

I am using InputFilter class to make a masked EditText supporting digit grouping. For example when the user inserts" 12345" I want to show "12,345" in EditText. How can I implement it?
This is my incomplete code:
InputFilter IF = new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!Character.isLetterOrDigit(source.charAt(i))) {
return "";
}
}
if (dest.length() > 0 && dest.length() % 3 == 0)
{
return "," + source;
}
return null;
}
};
edtRadius.setFilters(new InputFilter[] { IF });
Is there any other way to implement this kind of input mask?
This an improvement on the response from #vincent. It adds checks on deleting spaces in a number in the format 1234 5678 9190 so when trying to delete a space it just moves the cursor backon character to the digit before the space. It also keeps the cursor in the same relative place even if spaces are inserted.
mTxtCardNumber.addTextChangedListener(new TextWatcher() {
private boolean spaceDeleted;
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// check if a space was deleted
CharSequence charDeleted = s.subSequence(start, start + count);
spaceDeleted = " ".equals(charDeleted.toString());
}
public void afterTextChanged(Editable editable) {
// disable text watcher
mTxtCardNumber.removeTextChangedListener(this);
// record cursor position as setting the text in the textview
// places the cursor at the end
int cursorPosition = mTxtCardNumber.getSelectionStart();
String withSpaces = formatText(editable);
mTxtCardNumber.setText(withSpaces);
// set the cursor at the last position + the spaces added since the
// space are always added before the cursor
mTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));
// if a space was deleted also deleted just move the cursor
// before the space
if (spaceDeleted) {
mTxtCardNumber.setSelection(mTxtCardNumber.getSelectionStart() - 1);
spaceDeleted = false;
}
// enable text watcher
mTxtCardNumber.addTextChangedListener(this);
}
private String formatText(CharSequence text)
{
StringBuilder formatted = new StringBuilder();
int count = 0;
for (int i = 0; i < text.length(); ++i)
{
if (Character.isDigit(text.charAt(i)))
{
if (count % 4 == 0 && count > 0)
formatted.append(" ");
formatted.append(text.charAt(i));
++count;
}
}
return formatted.toString();
}
});
In case you're still searching, I ran into this problem the last day, and found that using a TextWatcher is the best (still not really good) option. I had to group digits of credit card numbers.
someEditText.addTextChagedListener(new TextWatcher()
{
//According to the developer guide, one shall only edit the EditText's
//content in this function.
#Override
public void afterTextChanged(Editable text)
{
//You somehow need to access the EditText to remove this listener
//for the time of the changes made here. This is one way, but you
//can create a proper TextWatcher class and pass the EditText to
//its constructor, or have the EditText as a member of the class
//this code is running in (in the last case, you simply have to
//delete this line).
EditText someEditText = (EditText) findViewById(R.id.someEditText);
//Remove listener to prevent further call due to the changes we're
//about to make (TextWatcher is recursive, this function will be
//called again for every change you make, and in my experience,
//replace generates multiple ones, so a flag is not enough.
someEditText.removeTextChangedListener(this);
//Replace text with processed the processed string.
//FormatText is a function that takes a CharSequence (yes, you can
//pass the Editable directly), processes it the way you want, then
//returns the result as a String.
text.replace(0, text.length(), FormatText(text));
//Place the listener back
someEditText.addTextChangedListener(this);
}
#Override
public void beforeTextChaged(CharSequence s, int start, int count, int after)
{
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
My formatting function for the credit card numbers looked like this:
String FormatText(CharSequence text)
{
StringBuilder formatted = new StringBuilder();
int count = 0;
for (int i = 0; i < text.length(); ++i)
{
if (Character.isDigit(text.charAt(i)))
{
//You have to be careful here, only add extra characters before a
//user-typed character, otherwise the user won't be able to delete
//with backspace, since you put the extra character back immediately.
//However, this way, my solution would put a space at the start of
//the string that I don't want, hence the > check.
if (count % 4 == 0 && count > 0)
formatted.append(' ');
formatted.append(text.charAt(i));
++count;
}
}
return formatted.toString();
}
You might have to mind other issues as well, since this solution actually rewrites the EditText's content every time a change is made. For example, you should avoid processing characters you inserted yourself (that is an additional reason for the isDigit check).
use simple function:
public String digit_grouping(String in_digit){
String res = "";
final int input_len = in_digit.length();
for(int i=0 ; i< input_len ; i++)
{
if( (i % 3 == 0) && i > 0 )
res = "," + res;
res = in_digit.charAt(input_len - i - 1) + res;
}
return res;
}

Categories

Resources