Get word from EditText on current cursor position - android

I am new to android programing. I added a context menu to edittext. I wish to get the word under the cursor on long press.
I can get selected text by following code.
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
EditText edittext = (EditText)findViewById(R.id.editText1);
menu.setHeaderTitle(edittext.getText().toString().substring(edittext.getSelectionStart(), edittext.getSelectionEnd()));
menu.add("Copy");
}
edittext has some text e.g "Some text. Some more text". When the user clicks on "more", the cursor will be in some where in the word "more". When the user long presses the word I want to get the word "more" and other words under the cursor.

There is better and simpler solution : using pattern in android
public String getCurrentWord(EditText editText) {
Spannable textSpan = editText.getText();
final int selection = editText.getSelectionStart();
final Pattern pattern = Pattern.compile("\\w+");
final Matcher matcher = pattern.matcher(textSpan);
int start = 0;
int end = 0;
String currentWord = "";
while (matcher.find()) {
start = matcher.start();
end = matcher.end();
if (start <= selection && selection <= end) {
currentWord = textSpan.subSequence(start, end).toString();
break;
}
}
return currentWord; // This is current word
}

EditText et = (EditText) findViewById(R.id.xx);
int startSelection = et.getSelectionStart();
String selectedWord = "";
int length = 0;
for(String currentWord : et.getText().toString().split(" ")) {
System.out.println(currentWord);
length = length + currentWord.length() + 1;
if(length > startSelection) {
selectedWord = currentWord;
break;
}
}
System.out.println("Selected word is: " + selectedWord);

Please try following code as it is optimized. Let me know if you has more specification.
//String str = editTextView.getText().toString(); //suppose edittext has "Hello World!"
int selectionStart = editTextView.getSelectionStart(); // Suppose cursor is at 2 position
int lastSpaceIndex = str.lastIndexOf(" ", selectionStart - 1);
int indexOf = str.indexOf(" ", lastSpaceIndex + 1);
String searchToken = str.substring(lastSpaceIndex + 1, indexOf == -1 ? str.length() : indexOf);
Toast.makeText(this, "Current word is :" + searchToken, Toast.LENGTH_SHORT).show();

I believe that a BreakIterator is the superior solution here. It avoids having to loop over the entire string and do the pattern matching yourself. It also finds word boundaries besides just a simple space character (commas, periods, etc.).
// assuming that only the cursor is showing, no selected range
int cursorPosition = editText.getSelectionStart();
// initialize the BreakIterator
BreakIterator iterator = BreakIterator.getWordInstance();
iterator.setText(editText.getText().toString());
// find the word boundaries before and after the cursor position
int wordStart;
if (iterator.isBoundary(cursorPosition)) {
wordStart = cursorPosition;
} else {
wordStart = iterator.preceding(cursorPosition);
}
int wordEnd = iterator.following(cursorPosition);
// get the word
CharSequence word = editText.getText().subSequence(wordStart, wordEnd);
If you want to get it on a long press then just put this in the onLongPress method of your GestureDetector.
See also
How does BreakIterator work in Android?

#Ali Thank you for providing your solution.
Here is an optimized variant, which does break if the word has been found.
This solution does not create a Spannable, because it is not needed to find the word.
#NonNull
public static String getWordAtIndex(#NonNull String text, #IntRange(from = 0) int index) {
String wordAtIndex = "";
// w = word character: [a-zA-Z_0-9]
final Pattern pattern = Pattern.compile("\\w+");
final Matcher matcher = pattern.matcher(text);
int startIndex;
int endIndex;
while (matcher.find()) {
startIndex = matcher.start();
endIndex = matcher.end();
if ((startIndex <= index) && (index <= endIndex)) {
wordAtIndex = text.subSequence(startIndex, endIndex).toString();
break;
}
}
return wordAtIndex;
}
Example: Get the word at the current cursor position:
String text = editText.getText().toString();
int cursorPosition = editText.getSelectionStart();
String wordAtCursorPosition = getWordAtIndex(text, cursorPosition);
Use this instead if you want to find all connected characters (including punctuation):
// S = non-whitespace character: [^\s]
final Pattern pattern = Pattern.compile("\\S+");
Java regex documentation (regular-expression): https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

Related

How to capitalize letters in editText?

i have simple Edittext and when I'm going to change input letters in im setting listener new textWatcher and it's onTextChanged() method like:
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("qwer", "onTextChanged: " + s + " " + start + " " + before + " " + count);
String originalText = s.toString();
int originalTextLength = originalText.length();
int currentSelection = textHeading.getSelectionStart();
StringBuilder sb = new StringBuilder();
boolean hasChanged = false;
for (int i = 0; i < originalTextLength; i++) {
char currentChar = originalText.charAt(i);
if (isAllowed(currentChar) && i < 21) {
sb.append(currentChar);
} else {
hasChanged = true;
textHeading.setError("Please insert current letters");
}
}
if (hasChanged) {
String newText = sb.toString();
textHeading.setText(capitalize(newText));
textHeading.setSelection(currentSelection);
}
}
endless cycle begins when i'm setting validated data back to the edittext becouse it calls method ontextCahnged() again. so my goal is dynamically change input letters and i have to capitalize it. I know there is more the easiest way to do it. but i need to do by this way.
You problem is with TextWatcher not with the logic what you are writing. Below Code block is causing the issue (endless cycle begins when i'm setting validated data back to the edittext becouse it calls method ontextCahnged() again)
if (hasChanged) {
String newText = sb.toString();
textHeading.setText(capitalize(newText)); // <<<<<< This line is culprit which is calling Watcher's method again and again.
textHeading.setSelection(currentSelection);
}
To handle this issue, you need to do below steps
Remove Watcher from EditText
Set text
Add Watcher to EditText.
For more information read How can I change the EditText text without triggering the Text Watcher?
There is many ways to do that:
1- Using common Utils
Library: http://commons.apache.org/proper/commons-lang/
StringUtils.capitalize(..)
2- By Custom method
public static String upperCaseFirst(String value) {
// Convert String to char array.
char[] array = value.toCharArray();
// Modify first element in array.
array[0] = Character.toUpperCase(array[0]);
// Return string.
return new String(array);
}
3- From apache Common
Library: http://commons.apache.org/proper/commons-lang/
WordUtils.capitalize(java.lang.String)
Now you can assign that string to your input box.
You can set the input type (of EditText) to TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_CHARACTERS.
OR
Set android:inputType="textCapSentences" on your EditText.
You can follow this link https://developer.android.com/reference/android/text/InputFilter.AllCaps
You can use following InputFilter.AllCaps
or this
android:inputType="textCapCharacters"
Why not use a flag ? I've modified your code by adding a boolean setManually flag.
boolean setManually = false;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("qwer", "onTextChanged: " + s + " " + start + " " + before + " " + count);
if (setManually) {
setManually = false;
return;
}
String originalText = s.toString();
int originalTextLength = originalText.length();
int currentSelection = textHeading.getSelectionStart();
StringBuilder sb = new StringBuilder();
boolean hasChanged = false;
for (int i = 0; i < originalTextLength; i++) {
char currentChar = originalText.charAt(i);
if (isAllowed(currentChar) && i < 21) {
sb.append(currentChar);
} else {
hasChanged = true;
textHeading.setError("Please insert current letters");
}
}
if (hasChanged) {
String newText = sb.toString();
setManually = true;
textHeading.setText(capitalize(newText));
textHeading.setSelection(currentSelection);
}
}
You can just simply use in XML
android:inputType="textCapCharacters"
no need to write code for capitlize letter.
Try like this:
String originalText = s.toString().toUpperCase();
or
if (hasChanged) {
String newText = sb.toString().toUpperCase();
textHeading.setText(newText);
textHeading.setSelection(currentSelection);
}

How can I convert numbers to currency format in android

I want to show my numbers in money format and separate digits like the example below:
1000 -----> 1,000
10000 -----> 10,000
100000 -----> 100,000
1000000 -----> 1,000,000
Thanks
Another approach :
NumberFormat format = NumberFormat.getCurrencyInstance();
format.setMaximumFractionDigits(0);
format.setCurrency(Currency.getInstance("EUR"));
format.format(1000000);
This way, it's displaying 1 000 000 € or 1,000,000 €, depending on device currency's display settings
You need to use a number formatter, like so:
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = 1000000;
String formattedNumber = formatter.format(myNumber);
//formattedNumber is equal to 1,000,000
Hope this helps!
double number = 1000000000.0;
String COUNTRY = "US";
String LANGUAGE = "en";
String str = NumberFormat.getCurrencyInstance(new Locale(LANGUAGE, COUNTRY)).format(number);
//str = $1,000,000,000.00
Currency formatter.
public static String currencyFormat(String amount) {
DecimalFormat formatter = new DecimalFormat("###,###,##0.00");
return formatter.format(Double.parseDouble(amount));
}
Use this:
int number = 1000000000;
String str = NumberFormat.getNumberInstance(Locale.US).format(number);
//str = 1,000,000,000
This Method gives you the exact output which you need:
public String currencyFormatter(String num) {
double m = Double.parseDouble(num);
DecimalFormat formatter = new DecimalFormat("###,###,###");
return formatter.format(m);
}
Try the following solution:
NumberFormat format = NumberFormat.getCurrencyInstance();
((TextView)findViewById(R.id.text_result)).setText(format.format(result));
The class will return a formatter for the device default currency.
You can refer to this link for more information:
https://developer.android.com/reference/java/text/NumberFormat.html
Here's a kotlin Extension that converts a Double to a Currency(Nigerian Naira)
fun Double.toRidePrice():String{
val format: NumberFormat = NumberFormat.getCurrencyInstance()
format.maximumFractionDigits = 0
format.currency = Currency.getInstance("NGN")
return format.format(this.roundToInt())
}
Use a Formatter class
For eg:
String s = (String.format("%,d", 1000000)).replace(',', ' ');
Look into:
http://developer.android.com/reference/java/util/Formatter.html
The way that I do this in our app is this:
amount.addTextChangedListener(new CurrencyTextWatcher(amount));
And the CurrencyTextWatcher is this:
public class CurrencyTextWatcher implements TextWatcher {
private EditText ed;
private String lastText;
private boolean bDel = false;
private boolean bInsert = false;
private int pos;
public CurrencyTextWatcher(EditText ed) {
this.ed = ed;
}
public static String getStringWithSeparator(long value) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
String f = formatter.format(value);
return f;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
bDel = false;
bInsert = false;
if (before == 1 && count == 0) {
bDel = true;
pos = start;
} else if (before == 0 && count == 1) {
bInsert = true;
pos = start;
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastText = s.toString();
}
#Override
public void afterTextChanged(Editable s) {
ed.removeTextChangedListener(this);
StringBuilder sb = new StringBuilder();
String text = s.toString();
for (int i = 0; i < text.length(); i++) {
if ((text.charAt(i) >= 0x30 && text.charAt(i) <= 0x39) || text.charAt(i) == '.' || text.charAt(i) == ',')
sb.append(text.charAt(i));
}
if (!sb.toString().equals(s.toString())) {
bDel = bInsert = false;
}
String newText = getFormattedString(sb.toString());
s.clear();
s.append(newText);
ed.addTextChangedListener(this);
if (bDel) {
int idx = pos;
if (lastText.length() - 1 > newText.length())
idx--; // if one , is removed
if (idx < 0)
idx = 0;
ed.setSelection(idx);
} else if (bInsert) {
int idx = pos + 1;
if (lastText.length() + 1 < newText.length())
idx++; // if one , is added
if (idx > newText.length())
idx = newText.length();
ed.setSelection(idx);
}
}
private String getFormattedString(String text) {
String res = "";
try {
String temp = text.replace(",", "");
long part1;
String part2 = "";
int dotIndex = temp.indexOf(".");
if (dotIndex >= 0) {
part1 = Long.parseLong(temp.substring(0, dotIndex));
if (dotIndex + 1 <= temp.length()) {
part2 = temp.substring(dotIndex + 1).trim().replace(".", "").replace(",", "");
}
} else
part1 = Long.parseLong(temp);
res = getStringWithSeparator(part1);
if (part2.length() > 0)
res += "." + part2;
else if (dotIndex >= 0)
res += ".";
} catch (Exception ex) {
ex.printStackTrace();
}
return res;
}
Now if you add this watcher to your EditText, as soon as user enter his number, the watcher decides whether it needs separator or not.
i used this code for my project and it works:
EditText edt_account_amount = findViewById(R.id.edt_account_amount);
edt_account_amount.addTextChangedListener(new DigitFormatWatcher(edt_account_amount));
and defined class:
public class NDigitCardFormatWatcher implements TextWatcher {
EditText et_filed;
String processed = "";
public NDigitCardFormatWatcher(EditText et_filed) {
this.et_filed = et_filed;
}
#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 editable) {
String initial = editable.toString();
if (et_filed == null) return;
if (initial.isEmpty()) return;
String cleanString = initial.replace(",", "");
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = new Double(cleanString);
processed = formatter.format(myNumber);
//Remove the listener
et_filed.removeTextChangedListener(this);
//Assign processed text
et_filed.setText(processed);
try {
et_filed.setSelection(processed.length());
} catch (Exception e) {
// TODO: handle exception
}
//Give back the listener
et_filed.addTextChangedListener(this);
}
}
Updated 2022 answer
Try this snippet. It formats a number in string complete with the currency & setting fractional digits.
Upvote if this helped you! :)
/**
* Formats amount in string to human-readable amount (separated with commas
* & prepends currency symbol)
*
* #param amount The amount to format in String
* #return The formatted amount complete with separators & currency symbol added
*/
public static String formatCurrency(String amount) {
String formattedAmount = amount;
try {
if (amount == null || amount.isEmpty())
throw new Exception("Amount is null/empty");
Double amountInDouble = Double.parseDouble(amount);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("en", "IN"));
numberFormat.setMaximumFractionDigits(2);
numberFormat.setMinimumFractionDigits(2);
formattedAmount = numberFormat.format(amountInDouble);
} catch (Exception exception) {
exception.printStackTrace();
return formattedAmount;
}
return formattedAmount;
}
private val currencyFormatter = NumberFormat.getCurrencyInstance(LOCALE_AUS).configure()
private fun NumberFormat.configure() = apply {
maximumFractionDigits = 2
minimumFractionDigits = 2
}
fun Number.asCurrency(): String {
return currencyFormatter.format(this)
}
And then just use as
val x = 100000.234
x.asCurrency()
If you have the value stored in a String like me, which was coming from the server like "$20000.00".
You can do something like this in Kotlin (JetpackCompose):
#Composable
fun PrizeAmount(
modifier: Modifier = Modifier,
prize: String,
)
{
val currencyFormat = NumberFormat.getCurrencyInstance(Locale("en", "US"))
val text = currencyFormat.format(prize.substringAfter("$").toDouble())
...
}
Output: "$20,000.00"
NumberFormat.getCurrencyInstance(Locale("ES", "es")).format(number)
here is a kotlin version to Format Currency, here i'm getting an argument from another fragment from an input Field then it will be set in the textView in the main Fragment
fun formatArgumentCurrency(argument : String, textView: TextView) {
val valueText = requireArguments().get(argument).toString()
val dec = DecimalFormat("#,###.##")
val number = java.lang.Double.valueOf(valueText)
val value = dec.format(number)
val currency = Currency.getInstance("USD")
val symbol = currency.symbol
textView.text = String.format("$symbol$value","%.2f" )
}
You can easily achieve this with this small simple library.
https://github.com/jpvs0101/Currencyfy
Just pass any number, then it will return formatted string, just like that.
currencyfy (500000.78); // $ 500,000.78 //default
currencyfy (500000.78, false); // $ 500,001 // hide fraction (will round off automatically!)
currencyfy (500000.78, false, false); // 500,001 // hide fraction & currency symbol
currencyfy (new Locale("en", "in"), 500000.78); // ₹ 5,00,000.78 // custom locale
It compatible with all versions of Android including older versions!

android spannable edititext with cursor manipulation

I am trying to implement code highlighting using spanned text and html.fromhtml() function in an edittext than implements text watcher.
The problem occurs when i try to manipulate cursor for custom brackets, the app crashes due to some spannable string setspan error.
How do i use spannable code highlighting and set cursor position adjusting according to the spanned text.
Edit:
The function:
Spanned matchtext(String s)
{
//Pattern p =Pattern.compile(check[0]);
String a=s;
for(int i=0;i<Constants.keyWords.length;i++) {
a = a.replaceAll(Constants.keyWords[i], "<font color=\"#c5c5c5\">" + Constants.keyWords[i] + "</font>");
//a = s.replaceAll(";", "<font color=\"#c5c5c5\">" + ";" + "</font>");
}
Spanned ab = Html.fromHtml(a);
return ab;
}
And the function call:
mCodeEditText.removeTextChangedListener(tt);
bs = matchtext(s.toString());
mCodeEditText.setText(bs);
mCodeEditText.addTextChangedListener(tt);
Edit 2:
This is my new implementation, I just can't get the highlighting to work.
Spannable matchtext(String s, int pos) {
Spannable abc = new SpannableString(s);
for (int i = 0; i < Constants.keyWords.length; i++) {
if (pos - Constants.keyWords[i].length() >= 0) {
int j = s.indexOf(Constants.keyWords[i]);
if (j != -1) {
if ((s.subSequence(j, pos)).equals(Constants.keyWords[i]))
abc.setSpan(new ForegroundColorSpan(Color.BLUE), j, pos, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
return abc;
}

Truncate word at end of string in Textview not char from last

Truncate String At End using this code
CharSequence charSequence;
float avail = 8 * content.getMeasuredWidth();
charSequence = TextUtils.ellipsize(textString,textview.getPaint(),avail, TextUtils.TruncateAt.END);
this code giving me result like this truncating last word and showing dots
hello wor...
but i need truncate also last word at end like below
hello
set character length . simply
protected String genrateString(String source, int numberOfWords){
BreakIterator iterator = BreakIterator.getSentenceInstance(Locale.US);
iterator.setText(source);
int start = iterator.first();
StringBuffer sb = new StringBuffer();
for (int end = iterator.next();end != BreakIterator.DONE;start = end, end = iterator.next()) {
String newSentence =source.substring(start,end);
int currentWordCount = countWord(sb.toString());
int newWordCount = currentWordCount + countWord(newSentence);
if(newWordCount-numberOfWords >= 0.1*numberOfWords) {
if(currentWordCount<numberOfWords && numberOfWords-currentWordCount > newWordCount-numberOfWords)
sb.append(newSentence);
else
break;
}
else
sb.append(newSentence);
}
return sb.toString();
}

Linkify any text Android

I want to add underline and colour to textview, but it is a simple text, not link, not phone number, just simple "Hello world", that I want to have with underline and that blue link-like colour.
It failed to do so:
view.setMovementMethod(LinkMovementMethod.getInstance());
Linkify.addLinks(view, Linkify.ALL);
Thank you! I just underlined the text as Const suggested and changed color of textview. But I guess xoxol_89's answer is correct in my case and should be accepted.
Do you try to use Spannable
For example
/**
* Method allocates filtering substring in all contacts yellow color,
* that satisfy the user's search
* #param inputText - DisplayName
* filtText - filtering Text
* #return String with allocating substring (Spannable)
*/
public static Spannable changeBackgroungFiltText(CharSequence inputText, String filtText, int color) {
Spannable str = null;
if(inputText != null)
{
String inputStr = inputText.toString();
String inputLowerCaseStr = inputStr.toLowerCase();
String filtLowerCaseStr = filtText.toLowerCase();
// Spannable str = new SpannableStringBuilder(inputStr);
str = new SpannableStringBuilder(inputStr);
if (filtText.length() != 0)
{
int indexStart = 0;
while (true)
{
int indexCur = inputLowerCaseStr.indexOf(filtLowerCaseStr, indexStart);
if (indexCur != -1) {
int start = indexCur;
int end = indexCur + filtText.length();
int flag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
str.setSpan(new ForegroundColorSpan(color),start, end, flag);
//str.setSpan(new BackgroundColorSpan(highlightColor), start, end, flag);
indexStart = indexCur + 1;
} else {
return str;
}
}
} else {
return str;
}
}
return str;
}
You can do that in 4 ways:
1.Automatically linkifies using android:autoLink=”all”
2.Link text by setMovementMethod
3.Link as html code using Html.fromHtml()
4.Link string by SpannableString
and you can find the examples here
You can use SpannableString like this:
final SpannableString text = new SpannableString("Hello World!");
final int startAt = 0;
final int endAt = text.length();
final int sampleColor = Color.parseColor("#3333ff");
text.setSpan(new UnderlineSpan(), startAt, endAt, 0);
text.setSpan(new ForegroundColorSpan(sampleColor), startAt, endAt, 0);
textView.setText(text);

Categories

Resources