EditText Cursor positioning Issue - android

I am using below function to forcefully making the first letter of each word to Capital letter in Edittext.
public String capitalizeFirstLetterWord(String s) {
StringBuilder cap = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
try {
char x = s.charAt(i);
if (x == ' ') {
cap.append(" ");
char y = s.charAt(i + 1);
cap.append(Character.toUpperCase(y));
i++;
} else {
cap.append(x);
}
} catch (IndexOutOfBoundsException ignored) {
}
}
//finally, capitalize the first letter of the sentence
String sentence = cap.toString();
if (sentence.length() > 0) {
sentence = String.valueOf(sentence.charAt(0)).toUpperCase(); //capitalize first letter
if (cap.toString().length() > 1) { //check if there's succeeding letters
sentence += cap.toString().substring(1); //append it also
}
}
return sentence;
}
and calling it in afterTextChange() method as below :
#Override
public void afterTextChanged(Editable editable) {
if (getActivity().getCurrentFocus() == mEdtName) {
if (editable.toString().length() > 0 &&
!editable.toString().equals(mOldName)) {
mOldName = editable.toString(); //prevent infinite loop
mEdtName.setText(capitalizeFirstLetterWord(editable.toString()));
mEdName.setSelection(mEdGymName.getText().length()); //set the cursor to the end of the editText
}
}
}
But, the issue is when I trying to erase a character from the middle of the Whole string in Editext. The Cursor is moving at the end of the Text.
.
It's because of the below line in afterTextChanged() method.
If I comment that line, Cursor moves to the first position.
.
What might be the solution ?

try this :
#Override
public void afterTextChanged(Editable editable) {
if (getActivity().getCurrentFocus() == mEdtName) {
if (editable.toString().length() > 0 && !editable.toString().equals(mOldName)) {
int selection = mEditName.getSelectionEnd();
mOldName = editable.toString(); //prevent infinite loop
mEditName.removeTextChangedListener(this);
mEditName.setText(capitalizeFirstLetterWord(editable.toString()));
mEditName.setSelection(selection);
mEditName.addTextChangedListener(this);
}
}
}

I think you should use InputFilter to do that.

Related

In between of EditText how to use autocomplete android

I know how to use EditText in android and how to use AutoComplete EditText in android. But AutoComplete EditText is used like dropdown list where user type a character and suggestions will be displayed and user clicks on one of the suggestions. If there is no suggestion then it won't display any text.
My requirement is like, Consider there is an EditText. User can type anything they want, but when the word starts from special character like for example "#" then the api will hit and display suggestions.
Complete Example:
"My Name is James Bond and I am #And"
In above example when user type this sentence at the end "#And" it will show the auto suggestion from hitting the API(should display Android) and user can select this suggested word from list.
Is there any way to achieve this??
Please Do help me.
Thank You.
Android provides MultiAutoCompleteTextView. Use this widget instead of EditText and override its tokenizer.
MultiAutoCompleteTextView myautocomplete = (MultiAutoCompleteTextView) findViewById(R.id.multy);
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, array_for_autocomplete);
myautocomplete.setAdapter(adapter);
myautocomplete.setTokenizer(new MultiAutoCompleteTextView.Tokenizer() {
#Override
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != '#') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
#Override
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ',') {
return i;
} else {
i++;
}
}
return len;
}
#Override
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ',') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + ", ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + ", ";
}
}
}
});
Documentation : http://developer.android.com/reference/android/widget/MultiAutoCompleteTextView.html

How to show dropdown only when inserting # character on MultiAutoCompleteTextView in android

I have a MultiAutoCompleteTextView. It works fine. But I want to show suggestion dropdown only when user type # on it (like tagging user in facebook app). I have no idea how to do it. Here is my code :
mChatbox = (MultiAutoCompleteTextView) findViewById(R.id.chatbox);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_dropdown_item_1line, userList);
mChatBox.setAdapter(adapter);
mChatBox.setTokenizer(new SpaceTokenizer());
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
Create a custom Text view extending MultiAutoCompleteTextView -> override enoughToFilter() -> set the threshold to 0 (the bold variable in the below given code) :
public boolean enoughToFilter() {
Editable text = getText();
int end = getSelectionEnd();
if (end < 0 || mTokenizer == null) {
return false;
}
int start = mTokenizer.findTokenStart(text, end);
if (end - start >= mThreshold && start != -1) {
return true;
} else {
return false;
}
}
Using this code you'll see the auto suggested list on press of #
If you want to detect your string starts with '#' for mention (tag) someone or '#' for hashTag, then do query or filter with it, you could follow this code belows:
#Override
public void onTextChanged(CharSequence s, int start, int before, final int count) {
if (s.length() > 0) {
// Todo: query mentions
Matcher mentionMatcher = Pattern.compile("#([A-Za-z0-9_-]+)").matcher(s.toString());
// while matching
while (mentionMatcher.find()) {
yourSearchText = s.toString().substring(mentionMatcher.start() + 1, mentionMatcher.end());
// do query with yourSearchText below
}
}
}
It references from the link Multiautocompletetextview, Show autocomplete drop down only when user presses a key after '#' key (like mention in FB app) please scroll down to find #Phuong Sala answer.
I got a solution by myself. I create custom view which extends MultiAutoCompleteTextView and override performFiltering in it. Check if first char is "#", then filter the next chars after it. Otherwise, replace chars with "*" to avoid filtering. Here is my code.
#Override
protected void performFiltering(CharSequence text, int start, int end, int keyCode) {
if (text.charAt(start) == '#') {
start = start + 1;
} else {
text = text.subSequence(0, start);
for (int i = start; i < end; i++) {
text = text + "*";
}
}
super.performFiltering(text, start, end, keyCode);
}

limit edit text in android with decimal

I have a question about limit number in editText. I want to user can enter only number from 1-70. When user want to put 70.01 or more I want to not allowed them.with only two decimal allowed.total length is 5 character including point.
i am able to limit before & after decimal. & also limit the 70 but user can able to enter 70.99(don't know why) in edit text that i want to block. my validation work when user enter 71 or more
this is constructor i im using in fragment
txtno.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(Integer.parseInt(getString(R.string.length)),2,txtno)});
this is for after & before decimal
#Override
public CharSequence filter(CharSequence source, int start, int end,Spanned dest, int dstart, int dend) {
mTextView.setKeyListener(DigitsKeyListener.getInstance(true,true));
String etText = mTextView.getText().toString();
String temp = mTextView.getText() + source.toString();
if (temp.equals(".")) {
return "0.";
} else if (temp.toString().indexOf(".") == -1) {
// no decimal point placed yet
if (temp.length() > mMyint) {
return "";
}
} else {
int dotPosition;
int cursorPositon = mTextView.getSelectionStart();
if (etText.indexOf(".") == -1) {
dotPosition = temp.indexOf(".");
} else {
dotPosition = etText.indexOf(".");
}
if (cursorPositon <= dotPosition) {
String beforeDot = etText.substring(0, dotPosition);
if (beforeDot.length() < mMyint) {
return source;
} else {
if (source.toString().equalsIgnoreCase(".")) {
return source;
} else {
return "";
}
}
} else {
temp = temp.substring(temp.indexOf(".") + 1);
if (temp.length() > mMydec) {
return "";
}
}
}
return null;
}
& this is textWatcher for limit 70
public void afterTextChanged(Editable s) {
try{
if(Integer.parseInt(s.toString())>70){
s.replace(0, s.length(), s.toString());
}
}catch(Exception e){}
Thanks in advance.
the problem is that you try to parse int - you need to parse double.

EditText selection on focus not working

Since I am very disappointed in a way that Android is doing decimal input I decided to write my own EditDecimal control that inherits from EditText. I want it to select all on click and to put cursor on first position on focus. I don't want cursor to show because it's usless on most Android phones (you mostly cannot put it in right place and it makes me very nervous)
Problem is when you tap on control - FocusChanged is called but it does not set position of (hidden) cursor on first position but on position where user tapped. I can not find the problem... is there some other event that happens after FocusChange that moves cursor?
public class EditDecimal : EditText
{
// Every constructor is calling Initialize ...
private void Initialize()
{
FocusChange += OnFocusChanged;
Click += OnClicked;
SetCursorVisible(false);
}
private void OnFocusChanged(object sender, FocusChangeEventArgs e)
{
if (IsFocused)
SetSelection(1);
}
private void OnClicked(object sender, EventArgs e)
{
SelectAll();
}
}
I finnaly solved it. I had to subscribe to OnTouch event and to change cursor position from there.
public class EditDecimal : EditText
{
// Every constructor is calling Initialize ...
private InputMethodManager _imm ;
public int DecimalSpaces { get; set; }
readonly DecimalFormatSymbols _dfs = new DecimalFormatSymbols();
private void Initialize(Context context, IAttributeSet attrs)
{
AfterTextChanged += OnAfterTextChanged;
SetSelectAllOnFocus(true);
SetCursorVisible(false);
Touch += OnTouch;
_imm = (InputMethodManager)context.GetSystemService(Context.InputMethodService);
var a = context.ObtainStyledAttributes(attrs, Resource.Styleable.EditDecimal);
try
{
DecimalSpaces = a.GetInteger(Resource.Styleable.EditDecimal_decimalSpaces, 2);
}
finally
{
a.Recycle();
}
}
private void OnTouch(object sender, TouchEventArgs e)
{
base.OnTouchEvent(e.Event);
if (e.Event.Action == MotionEventActions.Up)
{
SelectAll();
}
}
protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, Rect previouslyFocusedRect)
{
base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
SelectAll();
}
private void OnAfterTextChanged(object sender, AfterTextChangedEventArgs e)
{
const char ch1 = '.';
const char ch2 = ',';
switch (_dfs.DecimalSeparator)
{
case ch2:
{
if (Text.Contains(ch1.ToString()))
{
var position = Text.IndexOf(ch1);
if (Text.Contains(ch2.ToString()))
e.Editable.Delete(position, position + 1);
else
e.Editable.Replace(position, position + 1, ch2.ToString());
}
// we have to prevent showing two commas in number
var firstCommaPosition = Text.IndexOf(ch2);
var lastCommaPosition = Text.LastIndexOf(ch2);
if (firstCommaPosition > 0 && lastCommaPosition > 0 && firstCommaPosition != lastCommaPosition)
e.Editable.Delete(lastCommaPosition, lastCommaPosition+1);
}
break;
case ch1:
{
if (Text.Contains(ch2.ToString()))
{
var position = Text.IndexOf(ch2);
e.Editable.Delete(position, position + 1);
}
// we have to prevent showing two points in number
var firstPointPosition = Text.IndexOf(ch1);
var lastPointPosition = Text.LastIndexOf(ch1);
if (firstPointPosition > 0 && lastPointPosition > 0 && firstPointPosition != lastPointPosition)
e.Editable.Delete(lastPointPosition, lastPointPosition + 1);
}
break;
}
//thnx to http://stackoverflow.com/users/2240673/tom
var length = e.Editable.Length();
if (length <= 0) return;
if (NrOfDecimal(e.Editable.ToString()) > DecimalSpaces)
e.Editable.Delete(length - 1, length);
}
private int NrOfDecimal(string nr)
{
if (nr == null) return 0;
var nrCharArray = nr.ToCharArray();
var len = nr.Length;
var pos = len;
for (var i = 0; i < len; i++)
{
if (nrCharArray[i] != '.') continue;
pos = i + 1;
break;
}
return len - pos;
}
}
What came as suprize to me was a fact that EditText selection behaves differently if SetCursorVisible is set to true than when it is set to false. I thought that it was just visibility property.
Grepcode is your friend. It shows that TextView's onFocusChanged method checks if there's an Editor object defined for the View (which is the case with EditText) and a look at the Editor code shows this at line 889 ff:
// If a tap was used to give focus to that view, move cursor at tap position.
// Has to be done before onTakeFocus, which can be overloaded.
final int lastTapPosition = getLastTapPosition();
if (lastTapPosition >= 0) {
Selection.setSelection((Spannable) mTextView.getText(), lastTapPosition);
}
So perhaps the best way would be to write your own implementation of Editor. Although if have no idea right now how you attach that to your TextView resp. EditText. class.
Or you just set the selection back to the first position if that is what you want.

How to change color of words with hashtags

I need to be able to display text with all words starting with a # in a different color and they should be clickable. How can i do this?
This should do the trick
private void setTags(TextView pTextView, String pTagString) {
SpannableString string = new SpannableString(pTagString);
int start = -1;
for (int i = 0; i < pTagString.length(); i++) {
if (pTagString.charAt(i) == '#') {
start = i;
} else if (pTagString.charAt(i) == ' ' || pTagString.charAt(i) == '\n' || (i == pTagString.length() - 1 && start != -1)) {
if (start != -1) {
if (i == pTagString.length() - 1) {
i++; // case for if hash is last word and there is no
// space after word
}
final String tag = pTagString.substring(start, i);
string.setSpan(new ClickableSpan() {
#Override
public void onClick(View widget) {
Log.d("Hash", String.format("Clicked %s!", tag));
}
#Override
public void updateDrawState(TextPaint ds) {
// link color
ds.setColor(Color.parseColor("#33b5e5"));
ds.setUnderlineText(false);
}
}, start, i, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = -1;
}
}
}
pTextView.setMovementMethod(LinkMovementMethod.getInstance());
pTextView.setText(string);
}

Categories

Resources