I want to make custom AutoCompleteView like this..
It should be populated when my special character is added (like facebook.. When you type #B then all friends those name starts with 'B' will be populated and we can select name).
It should not be populate while typing until '#' is added.
When '#' is added autocompleteview is dropped down and we can select name and after selecting it will be appended.
I found this but not satisfied. I need clue. They have implemented...like when you type '#' dropdown populates.But I have to customize more. Right now I need help if other is having idea or any in-completed source.
I have to try myself but let me ask before implementing customview to save my time.
You have to make custom autocompleteview by extending class.. and code mentioned in your question to be changed.
public class CustomAutoComplete extends AutoCompleteTextView {
private String previous = "";
private String seperator = "#";
boolean isState = false;
public CustomAutoComplete(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
this.setThreshold(1);
}
public CustomAutoComplete(final Context context, final AttributeSet attrs) {
super(context, attrs);
this.setThreshold(1);
}
public CustomAutoComplete(final Context context) {
super(context);
this.setThreshold(1);
}
/**
* This method filters out the existing text till the separator and launched
* the filtering process again
*/
#Override
protected void performFiltering(final CharSequence text, final int keyCode) {
String filterText = text.toString().trim();
String lastchar = filterText.substring(filterText.length() - 1,
filterText.length());
if (filterText.length() == 1) {
if (lastchar.equals(seperator)) {
isState = true;
} else {
isState = false;
}
}
previous = filterText.substring(0,
filterText.lastIndexOf(getSeperator()) + 1);
filterText = filterText.substring(filterText
.lastIndexOf(getSeperator()) + 1);
if ((lastchar.equals(seperator)) || isState) {
super.performFiltering(filterText, keyCode);
isState = true;
}
}
/**
* After a selection, capture the new value and append to the existing text
*/
#Override
protected void replaceText(final CharSequence text) {
isState = false;
super.replaceText(previous + text);// + getSeperator());
}
public String getSeperator() {
return seperator;
}
public void setSeperator(final String seperator) {
this.seperator = seperator;
}
}
Hope this will help you...
You can achieve this using MultiAutoCompleteTextView. Just implement your own Tokenizer class and it works. For mentions I have written a class you can use it.
package howdysam.com.howdysuggesttext;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.widget.MultiAutoCompleteTextView;
public class AtTokenizer implements 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;
}
}
}
}
Then on View section (Activity or Fragment) instead of doing
edit.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
do following
edit.setTokenizer(new AtTokenizer());
It works.
Related
I am implementing a custom view for OTP of varying length upto 6 digits. I have extended a LinearLayout and use multiple edit text as its child view. Each edit text holds one digit. I want to implement the delete action from the soft keyboard for the above custom view. The following is the code for the OTP custom view.
public class OTPEditText extends LinearLayout {
private int mDigitSpacing = 8; // Space between digits
private int mDigitNumber = 6; // Number of digits
private int mDigitSize = 28; // Font size of the digits
private ArrayList<EditText> mEditTexts; // List of edit text each holding one digit
private OnCompleteListener mCompleteListener; //when all the edit text gets one digit each
public OTPEditText(Context context) {
super(context);
}
public OTPEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OTPEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Add the required number of Edit Texts
* #param number - number of digits
*/
public void setDigitNumber(int number){
this.mDigitNumber = number;
addViews();
}
public void setOnCompleteListener(OnCompleteListener listener) {
this.mCompleteListener = listener;
}
private void addViews() {
removeAllViews();
mEditTexts = new ArrayList<>();
for(int i = 0; i < mDigitNumber; i++){
EditText editText = new EditText(getContext());
//Set the necessary attributes
editText.addTextChangedListener(new GenericTextWatcher(i));
mEditTexts.add(editText);
addView(editText);
}
requestLayout();
if(mEditTexts.size() > 0) {
mEditTexts.get(0).requestFocus();
}
}
/**
* similar to setText of an edit text, but
* set one digit each to the edit text
* #param s - string for the edit text
*/
public void setText(String s){
if(s.length() > mDigitNumber){
s = s.substring(0, mDigitNumber);
}
int i;
for(i = 0; i < s.length(); i++){
mEditTexts.get(i).setText(s.charAt(i));
}
for(; i < mEditTexts.size(); i++){
mEditTexts.get(i).setText("");
}
}
/**
* Similar to the getText of an edit text,
* concatenates the text from each edit text
* #return - concatenated string from each edit text
*/
public String getText() {
String text = "";
if(!Utils.isEmptyList(mEditTexts)) {
for (EditText editText : mEditTexts){
text += editText.getText().toString();
}
}
return text;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event){
return true;
}
/**
* Called whenever onClick of the View is called. Simulates the click event of
* the required edit text.
*/
public void doClick() {
if(!Utils.isEmptyList(mEditTexts)){
for(EditText editText : mEditTexts){
if(editText.getText().toString().equals("")){
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN , 0, 0, 0));
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP , 0, 0, 0));
return;
}
}
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
}
}
public interface OnCompleteListener {
void onComplete();
}
// Generic edit text watcher
public class GenericTextWatcher implements TextWatcher {
private int index;
public GenericTextWatcher(int index){
this.index = index;
}
#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().length() >= 1){
if(index +1 < mEditTexts.size()){
mEditTexts.get(index + 1).requestFocus();
} else if(index == mEditTexts.size() - 1 && mCompleteListener != null){
mCompleteListener.onComplete();
}
}
}
}
}
edOtp1.addTextChangedListener(new OtpTextWatcher(edOtp1));
edOtp2.addTextChangedListener(new OtpTextWatcher(edOtp2));
edOtp3.addTextChangedListener(new OtpTextWatcher(edOtp3));
edOtp4.addTextChangedListener(new OtpTextWatcher(edOtp4));
create this class that handle text on addition or deletion.
private class OtpTextWatcher implements TextWatcher
{
private View view;
OtpTextWatcher(View view) {
this.view = view;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
String text = charSequence.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
else{
edOtp1.requestFocus();
}
break;
case R.id.edOtp2:
if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
else
edOtp1.setSelection(edOtp1.getText().length());
break;
case R.id.edOtp2:
if (text.length() == 1)
edOtp3.requestFocus();
else if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 1)
edOtp4.requestFocus();
else if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
}
I created a gist here https://gist.github.com/ShivamPokhriyal/8d0cf4aef062e6c59d00c04c53e03158 which you can simply copy paste in your project.
It creates a custom OTPEditText class which handles shifting the focus to next or previous edittext when user types in or deletes and also handles the paste event when user long presses and pastes the otp in the editText. All this can be done in the xml only. No need to pollute your activity with these stuff.
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* This class handles otp input in multiple edittexts.
* It will move focus to next edittext, if available, when user enters otp.
* And it will move focus to the previous edittext, if available, when user deletes otp.
* It will also delegate the paste option, if user long presses and pastes a string into the otp input.
*
* <b>XML attributes</b>
*
* #attr ref your_package_name.R.styleable#OTPView_nextView
* #attr ref your_package_name.R.styleable#OTPView_prevView
*
* #author $|-|!˅#M
*/
public class OTPEditText extends androidx.appcompat.widget.AppCompatEditText {
#Nullable
private View nextView;
#Nullable
private View previousView;
// Unfortunately getParent returns null inside the constructor. So we need to store the IDs.
private int nextViewId;
private int previousViewId;
#Nullable
private Listener listener;
private static final int NO_ID = -1;
public interface Listener {
void onPaste(String s);
}
public OTPEditText(#NonNull Context context) {
super(context);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {#link android.R.id#selectAll}, {#link android.R.id#cut},
* {#link android.R.id#copy}, {#link android.R.id#paste} or {#link android.R.id#shareText}.
*
* #return true if the context menu item action was performed.
*/
#Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
// Gets the clipboard as text.
CharSequence pasteData = item.getText();
if (listener != null && pasteData != null) {
listener.onPaste(pasteData.toString());
return true;
}
}
return super.onTextContextMenuItem(id);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
// If we've gotten focus here
if (focused && this.getText() != null) {
this.setSelection(this.getText().length());
}
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OTPView, 0, 0);
nextViewId = typedArray.getResourceId(R.styleable.OTPView_nextView, NO_ID);
previousViewId = typedArray.getResourceId(R.styleable.OTPView_prevView, NO_ID);
typedArray.recycle();
this.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction()!= KeyEvent.ACTION_DOWN) {
return true;
}
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
// Back pressed. If we have a previous view. Go to it.
if (getPreviousView() != null) {
getPreviousView().requestFocus();
return true;
}
}
return false;
});
this.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.length() == 1 && getNextView() != null) {
getNextView().requestFocus();
} else if (s.length() == 0 && getPreviousView() != null) {
getPreviousView().requestFocus();
}
}
});
// Android 3rd party keyboards show the copied text into the suggestion box for the user.
// Users can then simply tap on that suggestion to paste the text on the edittext.
// But I don't know of any API that allows handling of those paste actions.
// Below code will try to tell those keyboards to stop showing those suggestion.
this.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS | EditorInfo.TYPE_CLASS_NUMBER);
}
private View getNextView() {
if (nextView != null) {
return nextView;
}
if (nextViewId != NO_ID && getParent() instanceof View) {
nextView = ((View) getParent()).findViewById(nextViewId);
return nextView;
}
return null;
}
private View getPreviousView() {
if (previousView != null) {
return previousView;
}
if (previousViewId != NO_ID && getParent() instanceof View) {
previousView = ((View) getParent()).findViewById(previousViewId);
return previousView;
}
return null;
}
}
The gist also includes the xml and java code that you can directly add to your activity.
Hi, guys! I'm working on app which uses a external sqlite database . I'm using a sample source code and I would like to some excerpts from the entries present bold or italic style or a different color.
For example, if I have an entry like this: Black is my favorite color. I would like my string recognize the HTML tags. The word "black" in my example should be in bold.
I'm a newbie and I need a help in details. This is my code:
WordView.java
public class WordView extends TextView implements OnTouchListener {
private PopupView popup;
private String infoWord;
private ScrollView parent;
private boolean mClickEnabled;
private Typeface transTypeFace, transTypeFaceBold;
private static final Map<String, String[]> FONTS = new HashMap<String, String[]>();
static {
FONTS.put("sans", new String[] { "DejaVuSansCondensed.ttf", "DejaVuSansCondensed-Bold.ttf"});
FONTS.put("serif", new String[] { "DejaVuSerifCondensed.ttf", "DejaVuSerifCondensed-Bold.ttf"});
}
private class Type1Span extends ClickableSpan {
String word;
public Type1Span(String word) {
this.word = word;
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(touchedInstance == this);
ds.setTypeface(transTypeFace);
}
#Override
public void onClick(View widget) {
if (mClickEnabled) {
Uri uri = Uri.parse("ecidiomas://" + word);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addCategory(Intent.CATEGORY_DEFAULT);
widget.getContext().startActivity(intent);
}
}
};
private class Type2Span extends Type1Span {
public Type2Span(String word) {
super(word);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setTypeface(transTypeFaceBold);
}
}
private class TranscriptionTypeSpan extends ForegroundColorSpan {
public TranscriptionTypeSpan(int color) {
super(color);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setTypeface(transTypeFaceBold);
//ds.setTypeface(Typeface.DEFAULT_BOLD);
}
}
private Type1Span touchedInstance;
public WordView(Context context) {
super(context);
init();
}
public WordView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
Context ctx = getContext();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
String defaultType = "sans";
String fontType = sp.getString(App.PreferenceKeys.preference_font_idx, defaultType);
if (!FONTS.containsKey(fontType)) fontType = defaultType;
transTypeFace = Typeface.createFromAsset(ctx.getAssets(), FONTS.get(fontType)[0]);
transTypeFaceBold = Typeface.createFromAsset(ctx.getAssets(), FONTS.get(fontType)[1]);
setTypeface(transTypeFaceBold);
setMovementMethod(LinkMovementMethod.getInstance());
setOnTouchListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewParent p = getParent();
if (p instanceof ScrollView)
parent = (ScrollView) p;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return event.getAction() == MotionEvent.ACTION_DOWN ?
true : super.onTouchEvent(event);
}
public void setWordInfo(String word, String translation, String text) {
infoWord = word;
translation = translation.trim();
translation = TextUtils.isEmpty(translation) ? "" : (translation + '\n');
setText(infoWord.trim().toUpperCase() + "\n" + translation + "\n"+ text);
}
#Override
public void setText(CharSequence text, BufferType type) {
Integer currentType = LinksFinder.getType(infoWord != null ? infoWord : ""); //XXX
ArrayList<LinksFinder.LinkSpec> links = LinksFinder.getLinks(text.toString());
if (links == null) {
super.setText(text, type);
return;
}
SpannableString ss = new SpannableString(text);
int links_length = links.size();
for (int i = 0; i < links_length; ++i) {
LinksFinder.LinkSpec l = links.get(i);
CharacterStyle span;
if (LinksFinder.TRANSCRIPTION == l.type) {
span = new TranscriptionTypeSpan(0xffc91111);
} else if (currentType == l.type) {
span = new Type2Span(l.url);
} else {
span = new Type1Span(l.url);
}
ss.setSpan(span, l.start, l.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
super.setText(ss, type);
}
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_MOVE) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
int xx = getRealPosX(x);
int yy = getRealPosY(y);
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Type1Span[] candidates = ((Spannable) getText()).getSpans(off, off, Type1Span.class);
if (candidates.length > 0) {
touchedInstance = candidates[0];
} else {
touchedInstance = null;
}
if (mClickEnabled && popup != null && touchedInstance != null)
popup.setPopupText(xx, yy, touchedInstance.word);
} else {
if (popup != null)
popup.clear();
if (touchedInstance != null) {
touchedInstance = null;
}
}
return false;
}
public void setPopup(PopupView popup) {
this.popup = popup;
this.popup.setTypeFace(transTypeFaceBold);
}
private int getRealPosX(int val) {
return parent == null ? val : val - parent.getScrollX();
}
private int getRealPosY(int val) {
return parent == null ? val : val - parent.getScrollY();
}
public void setClickEnabled(boolean enabled) {
mClickEnabled = enabled;
}
}
WordAdapter.java
class WordAdapter extends CursorAdapter implements Filterable, DB {
private String[] QUERY_PROJECTION = new String[] { COLUMN_ID, COLUMN_WORD };
private final int WORD_COLUMN_INDEX;
private SQLiteDatabase db;
public WordAdapter(Context context, Cursor c, SQLiteDatabase db) {
super(context, c);
WORD_COLUMN_INDEX = c.getColumnIndexOrThrow(COLUMN_WORD);
this.db = db;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
final TextView view = (TextView) inflater.inflate(
R.layout.simple_dropdown_item_1line, parent, false);
view.setText(cursor.getString(WORD_COLUMN_INDEX));
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
((TextView) view).setText(cursor.getString(WORD_COLUMN_INDEX));
}
#Override
public String convertToString(Cursor cursor) {
return cursor.getString(WORD_COLUMN_INDEX);
}
#Override
public Cursor runQueryOnBackgroundThread(CharSequence s) {
Cursor c = null;
if (s != null)
c = db.query(DB.TABLE_WORDS, QUERY_PROJECTION, getLike(s.toString().toLowerCase()), null, null, null, null);
return c;
}
private String getLike(String s) {
return DB.COLUMN_WORD + ">= '" + s + "' AND " + DB.COLUMN_WORD + "< '" + s + '\u044F' +"'";
}
}
I have a costum built EditText Class, I was wondering if it's possible to change the EditText's Kerning, I have an issue with this, because it will be a password field, so it seams I can't use:
builder.append("\u00A0");
Edit: Here is the code.
public class MyEditText extends EditText {
private float mLetterSpacing = 0;
private CharSequence mOriginalText = "";
public MyEditText(Context context) {
super(context);
}
public MyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public float getLetterSpacing() {
return mLetterSpacing;
}
public void setLetterSpacing(float letterSpacing) {
mLetterSpacing = letterSpacing;
applyLetterSpacing();
}
#Override
public void setText(CharSequence text, BufferType type) {
mOriginalText = text;
applyLetterSpacing();
}
public void applyLetterSpacing() {
StringBuilder builder = new StringBuilder();
for(int i = 0; i < mOriginalText.length(); i++) {
String c = ""+ mOriginalText.charAt(i);
builder.append(c.toLowerCase());
if(i+1 < mOriginalText.length()) {
builder.append("\u00A0");
}
}
SpannableString finalText = new SpannableString(builder.toString());
if(builder.toString().length() > 1) {
for(int i = 1; i < builder.toString().length(); i+=2) {
finalText.setSpan(new ScaleXSpan((mLetterSpacing+1)/10), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
super.setText(finalText, BufferType.SPANNABLE);
}
public static Spannable applyKerning(CharSequence src, float kerning) {
if (src == null) return null;
final int srcLength = src.length();
if (srcLength < 2) return src instanceof Spannable
? (Spannable)src
: new SpannableString(src);
final String nonBreakingSpace = "\u00A0";
final SpannableStringBuilder builder = src instanceof SpannableStringBuilder
? (SpannableStringBuilder)src
: new SpannableStringBuilder(src);
for (int i = src.length() - 1; i >= 1; i--)
{
builder.insert(i, nonBreakingSpace);
builder.setSpan(new ScaleXSpan(kerning), i, i + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return builder;
}
}
I tried this, but ofc with no success. I was wondering if it's possible to add the spacing without messing with the "password String".
I think the best (only?) way to do this would be to use a font typeface that has the kerning you want and set that on the EditText. It doesn't look like there is a way to programmatically alter the kerning.
I am working with android 2.2.
How to know which character is get deleted on backspace when editing text in custom auto complete in android.
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// how to find here which character is get deleted
}
return false;
}
String prevText = "";
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
int pos = myEditText.getSelectionStart();
char c = prevText.charAt(pos);
// c is deleted
}
prevText = myEditText.getText.toString();
return false;
}
You can use add a TextWatcher to AutoCompleteTextView with addTextChangeListener(TextWatcher).
You don't need to listen to onKeyUp() the various TextWatcher methods inform you if the user is adding or removing text.
The easiest way is just keep last character that you type
int lastKeyCode;
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// print lastKeyCode here
// how to find here which character is get deleted
}
lastKeyCode = keyCode;
return false;
}
Try this, working for me
editText.addTextChangedListener(new TextWatcher() {
String initialChar = null;
int initCursorPosition = 0;
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int after) {
char addedChar = 0;
int finalCursorPosition = editText.getSelectionStart();
if (finalCursorPosition - initCursorPosition > 0) {
addedChar = charSequence.charAt(finalCursorPosition - 1);
Log.d(TAG, "onTextChanged added: " + addedChar);
//added char
} else {
char delChar = initialChar.charAt(initCursorPosition - 1);
Log.d(TAG, "onTextChanged deletedChar: " + delChar);
//deleted char
}
}
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int before, int after) {
Log.d(TAG, "textChange beforeTextChanged: " + charSequence);
Log.d(TAG, "textChange cursorPosition: " + editText.getSelectionStart());
initialChar = String.valueOf(charSequence);
initCursorPosition = editText.getSelectionStart();
}
#Override
public void afterTextChanged(Editable arg0) {
}
});
(Edittext).setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if(event.getKeyCode()==67)
{
if((EditText).getText().toString().length()>0)
{
int pos = (Edittext).getSelectionStart();
Char c = (EditText).getText().toString.charAt(pos);
Toast.makeText(getApplicationontext(),String.valueOf(c),Toast.LENGTH_SHORT).show();
}
}
return false;
}
});
I think it helps you
I know this is a very late answer, but this would be effective for future users.
First the KeyEvent.KEYCODE_DEL doesn't work for soft keyboard in latest android versions, so you have to create a custom EditText to handle that.
So we'll create a class to handle all null type in LatinIME
import android.text.SpannableStringBuilder;
public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder {
EditableAccomodatingLatinIMETypeNullIssues(CharSequence source) {
super(source);
}
public static CharSequence ONE_UNPROCESSED_CHARACTER = "/";
#Override
public SpannableStringBuilder replace(final int
spannableStringStart, final int spannableStringEnd, CharSequence replacementSequence,
int replacementStart, int replacementEnd) {
if (replacementEnd > replacementStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, replacementSequence, replacementStart, replacementEnd);
}
else if (spannableStringEnd > spannableStringStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, ONE_UNPROCESSED_CHARACTER, 0, 1);
}
return super.replace(spannableStringStart, spannableStringEnd,
replacementSequence, replacementStart, replacementEnd);
}
}
Then we'll go ahead to create another class to handle the InputConnection of the custom EditText to be created
import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
public class InputConnectionAccomodatingLatinIMETypeNullIssues extends BaseInputConnection {
Editable myEditable = null;
public InputConnectionAccomodatingLatinIMETypeNullIssues(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
#Override
public Editable getEditable() {
if(Build.VERSION.SDK_INT >= 14) {
if(myEditable == null) {
myEditable = new EditableAccomodatingLatinIMETypeNullIssues(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
else {
int myEditableLength = myEditable.length();
if(myEditableLength == 0) {
myEditable.append(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
}
return myEditable;
}
else {
return super.getEditable();
}
}
#Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if((Build.VERSION.SDK_INT >= 14) // && (Build.VERSION.SDK_INT < 19)
&& (beforeLength == 1 && afterLength == 0)) {
return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
else {
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
Then creating the custom EditText
import android.content.Context;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
public class CustomEditText extends androidx.appcompat.widget.AppCompatEditText {
public CustomEditText(#NonNull #NotNull Context context) {
super(context);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnectionAccomodatingLatinIMETypeNullIssues baseInputConnection =
new InputConnectionAccomodatingLatinIMETypeNullIssues(this, false);
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_NULL;
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
return baseInputConnection;
}
}
So in your .xml file, you can say something like
<your.package.name.CustomEditText
<!set all required attributes-->
/>
Now in your java code, you have to listen for the Backspace click event and get the character to be deleted before it's deleted.
editText.setOnKeyListener(new View.OnKeyListener(){
public boolean onKey(View v, int keyCode, KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL) {
int selStart = editText.getSelectionStart();
int selEnd = editText.getSelectionEnd();
//incase if some text in the textbox are selected, selStart and selEnd will
//return the positions of both selected ends and you can get the text(s) deleted.
//check if the selEnd is at 0, and if true, then there's not point going further
if(selEnd == 0)
return false;
//if there is no texts selected, then reduce selStart by one to get the text to be deleted
if(selStart == selEnd)
selStart -= 1;
String text = editText.getText().toString();
//now this will get you char or chars to be deleted
String charDeleted = text.substring(selStart, selEnd);
//So before returning false to deleted the char,
//let me answer the question asked also on the
//comment section in Bobs' answer
//which is to find the color code of the deleted char
//Note, this is for only a single char, if you want to
//handle for multiple chars, then you'll have to split the
//deleted chars into an array and get the color codes for each
//This will get all HTML text within the deleted char including the color code
String htmlText = Html.toHtml((Spanned) editText.getText().subSequence(selStart, selEnd));
//Now you have to use a HTML parser, and I'm using JSoup
//Don't forget to implement the JSoup library into your project
Document doc = Jsoup.parse(htmlText, "UTF-8");
//Here I'm selecting a SPAN html tag, hoping that the char should be in a SPAN
//If it's in a FONT or any other tag, then you select the tag instead and also
//the attribute containing the color code
Elements element = doc.select("span");
//Attribute containing the color code for SPAN is the STYLE
String style = element.attr("style");
//So the style string would be giving you something like "color:#AABBCC;"
//Where #AABBCC is the color code of the deleted char
}
return false;
}
});
Try this
edittext.addTextChangeListener(new TextWatcher{
#override
public void afterTextChanged(Editable s){
String changedtext = s.toString();
}
#override
public void beforeTextChanged (CharSequence s, int start, int count, int after){}
#override
public void onTextChanged(CharSequence s, int start, int before, int count){}
});
I am trying to show in asterisk (*) symbol in place of dots in EditText having inputType as textPassword. I came across post that ask to use setTransformationMethod() and implement PasswordTransformationMethod. But which method I of that class need I implement and how show asterisk? Is there other way to do that?
Thanks
I think you should go through the documentation. Create your PasswordTransformationMethod class, and in the getTransformation() method, just return a string of * characters that is the same length as the contents of your password field.
I did some fiddling and came up with an anonymous class that worked for me to make a field full of *s. I converted it into a usable class here:
public class MyPasswordTransformationMethod extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // Store char sequence
}
public char charAt(int index) {
return '*'; // This is the important part
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
};
// Call the above class using this:
text.setTransformationMethod(new MyPasswordTransformationMethod());
Use custom class that change DOT value from '\u2022' to '\u002A'
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.text.Editable;
import android.text.GetChars;
import android.text.NoCopySpan;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.TransformationMethod;
import android.text.style.UpdateLayout;
import android.view.View;
import java.lang.ref.WeakReference;
public class PasswordTransformationMethod
implements TransformationMethod, TextWatcher {
/* package */ static final Object ACTIVE = new NoCopySpan.Concrete();
/* package */ static final Object CAPPED = new NoCopySpan.Concrete();
/* package */ static final int SHOW_PASSWORD = 8;
public CharSequence getTransformation(CharSequence source, View view) {
if (source instanceof Spannable) {
Spannable sp = (Spannable) source;
/*
* Remove any references to other views that may still be
* attached. This will happen when you flip the screen
* while a password field is showing; there will still
* be references to the old EditText in the text.
*/
ViewReference[] vr = sp.getSpans(0, sp.length(),
ViewReference.class);
for (int i = 0; i < vr.length; i++) {
sp.removeSpan(vr[i]);
}
removeVisibleSpans(sp);
sp.setSpan(new ViewReference(view), 0, 0,
Spannable.SPAN_POINT_POINT);
}
return new PasswordCharSequence(source);
}
public static android.text.method.PasswordTransformationMethod getInstance() {
if (sInstance != null)
return sInstance;
sInstance = new android.text.method.PasswordTransformationMethod();
return sInstance;
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
// This callback isn't used.
}
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if (s instanceof Spannable) {
Spannable sp = (Spannable) s;
ViewReference[] vr = sp.getSpans(0, s.length(),
ViewReference.class);
if (vr.length == 0) {
return;
}
/*
* There should generally only be one ViewReference in the text,
* but make sure to look through all of them if necessary in case
* something strange is going on. (We might still end up with
* multiple ViewReferences if someone moves text from one password
* field to another.)
*/
View v = null;
for (int i = 0; v == null && i < vr.length; i++) {
v = vr[i].get();
}
if (v == null) {
return;
}
/* int pref = TextKeyListener.getInstance().getPrefs(v.getContext());
if ((pref & SHOW_PASSWORD) != 0) {
if (count > 0) {
removeVisibleSpans(sp);
if (count == 1) {
sp.setSpan(new Visible(sp, this), start, start + count,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}*/
}
}
public void afterTextChanged(Editable s) {
// This callback isn't used.
}
public void onFocusChanged(View view, CharSequence sourceText,
boolean focused, int direction,
Rect previouslyFocusedRect) {
if (!focused) {
if (sourceText instanceof Spannable) {
Spannable sp = (Spannable) sourceText;
removeVisibleSpans(sp);
}
}
}
private static void removeVisibleSpans(Spannable sp) {
Visible[] old = sp.getSpans(0, sp.length(), Visible.class);
for (int i = 0; i < old.length; i++) {
sp.removeSpan(old[i]);
}
}
private static class PasswordCharSequence
implements CharSequence, GetChars {
public PasswordCharSequence(CharSequence source) {
mSource = source;
}
public int length() {
return mSource.length();
}
public char charAt(int i) {
if (mSource instanceof Spanned) {
Spanned sp = (Spanned) mSource;
int st = sp.getSpanStart(ACTIVE);
int en = sp.getSpanEnd(ACTIVE);
if (i >= st && i < en) {
return mSource.charAt(i);
}
Visible[] visible = sp.getSpans(0, sp.length(), Visible.class);
for (int a = 0; a < visible.length; a++) {
if (sp.getSpanStart(visible[a].mTransformer) >= 0) {
st = sp.getSpanStart(visible[a]);
en = sp.getSpanEnd(visible[a]);
if (i >= st && i < en) {
return mSource.charAt(i);
}
}
}
}
return DOT;
}
public CharSequence subSequence(int start, int end) {
char[] buf = new char[end - start];
getChars(start, end, buf, 0);
return new String(buf);
}
public String toString() {
return subSequence(0, length()).toString();
}
public void getChars(int start, int end, char[] dest, int off) {
TextUtils.getChars(mSource, start, end, dest, off);
int st = -1, en = -1;
int nvisible = 0;
int[] starts = null, ends = null;
if (mSource instanceof Spanned) {
Spanned sp = (Spanned) mSource;
st = sp.getSpanStart(ACTIVE);
en = sp.getSpanEnd(ACTIVE);
Visible[] visible = sp.getSpans(0, sp.length(), Visible.class);
nvisible = visible.length;
starts = new int[nvisible];
ends = new int[nvisible];
for (int i = 0; i < nvisible; i++) {
if (sp.getSpanStart(visible[i].mTransformer) >= 0) {
starts[i] = sp.getSpanStart(visible[i]);
ends[i] = sp.getSpanEnd(visible[i]);
}
}
}
for (int i = start; i < end; i++) {
if (!(i >= st && i < en)) {
boolean visible = false;
for (int a = 0; a < nvisible; a++) {
if (i >= starts[a] && i < ends[a]) {
visible = true;
break;
}
}
if (!visible) {
dest[i - start + off] = DOT;
}
}
}
}
private CharSequence mSource;
}
private static class Visible
extends Handler
implements UpdateLayout, Runnable {
public Visible(Spannable sp, android.text.method.PasswordTransformationMethod ptm) {
mText = sp;
mTransformer = ptm;
postAtTime(this, SystemClock.uptimeMillis() + 1500);
}
public void run() {
mText.removeSpan(this);
}
private Spannable mText;
private android.text.method.PasswordTransformationMethod mTransformer;
}
/**
* Used to stash a reference back to the View in the Editable so we
* can use it to check the settings.
*/
private static class ViewReference extends WeakReference<View>
implements NoCopySpan {
public ViewReference(View v) {
super(v);
}
}
private static android.text.method.PasswordTransformationMethod sInstance;
private static char DOT = '\u002A';
}
In kotlin, you can do this.
editText.transformationMethod = object : PasswordTransformationMethod() {
override fun getTransformation(
source: CharSequence?,
view: View?
): CharSequence {
return PasswordCharSequence(super.getTransformation(source, view))
}
inner class PasswordCharSequence(private val source: CharSequence) : CharSequence {
override val length: Int get() = source.length
override fun get(index: Int): Char {
//Only modify char '\u2022' if you want to keep the default behaviour
return if(source[index] == '\u2022') '*' else source[index]
}
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
return source.subSequence(startIndex, endIndex)
}
}
}