multiline edittext where parts are not editable, like fill in the blanks - android

I need to have a view which contains textview and edittext.
Example:
Yay! you made it to ______ We should hang out! feel ____ to follow me.
Above "_____" could be of any length and it should feel like a paragraph in the end. Rest of the text given above is not changeable. Just like fill in the blanks.

From my perspective, a fill-in-the-blank widget should do the following:
Allow only certain identified portions of the text to be changed. The rest of the text is locked.
Not allow cursor movement into the locked text.
Flow from line to line like EditText.
Be generalized with variable placement of blanks.
Here is an implementation of such a widget based upon EditText. Editable spans are set up using a span (BlanksSpan) extended from StyleSpan. A blank span is identified by five underscores ("_____") in the text. Cursor movement is controlled in OnSelectionChanged() and various EditText callbacks. Changes to the text is monitor by a TextWatcher and adjustments to the displayed text are made there.
Here is the video of the widget in use:
FillInBlanksEditText.java
public class FillInBlanksEditText extends android.support.v7.widget.AppCompatEditText
implements View.OnFocusChangeListener, TextWatcher {
private int mLastSelStart;
private int mLastSelEnd;
private BlanksSpan mSpans[];
private Editable mUndoChange;
private BlanksSpan mWatcherSpan;
public FillInBlanksEditText(Context context) {
super(context);
init();
}
public FillInBlanksEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FillInBlanksEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSpans = setSpans();
setOnFocusChangeListener(this);
}
#Override
public void onRestoreInstanceState(Parcelable state) {
mSpans = null;
super.onRestoreInstanceState(state);
Editable e = getEditableText();
mSpans = e.getSpans(0, e.length(), BlanksSpan.class);
}
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
addTextChangedListener(this);
if (findInSpan(getSelectionStart(), getSelectionEnd()) != null) {
mLastSelStart = getSelectionStart();
mLastSelEnd = getSelectionEnd();
} else if (findInSpan(mLastSelStart, mLastSelEnd) == null) {
setSelection(getEditableText().getSpanStart(mSpans[0]));
}
} else {
removeTextChangedListener(this);
}
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (!isFocused() || mSpans == null ||
(getSelectionStart() == mLastSelStart && getSelectionEnd() == mLastSelEnd)) {
return;
}
// The selection must be completely within a Blankspan.
final BlanksSpan span = findInSpan(selStart, selEnd);
if (span == null) {
// Current selection is not within a Blankspan. Restore selection to prior location.
moveCursor(mLastSelStart);
} else if (selStart > getEditableText().getSpanStart(span) + span.getDataLength()) {
// Acceptable location for selection (within a Blankspan).
// Make sure that the cursor is at the end of the entered data. mLastSelStart = getEditableText().getSpanStart(span) + span.getDataLength();
mLastSelEnd = mLastSelStart;
moveCursor(mLastSelStart);
} else {
// Just capture the placement.
mLastSelStart = selStart;
mLastSelEnd = selEnd;
}
super.onSelectionChanged(mLastSelStart, mLastSelEnd);
}
// Safely move the cursor without directly invoking setSelection from onSelectionChange.
private void moveCursor(final int selStart) {
post(new Runnable() {
#Override
public void run() {
setSelection(selStart);
}
});
// Stop cursor form jumping on move.
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
return false;
}
});
}
#Nullable
private BlanksSpan findInSpan(int selStart, int selEnd) {
for (BlanksSpan span : mSpans) {
if (selStart >= getEditableText().getSpanStart(span) &&
selEnd <= getEditableText().getSpanEnd(span)) {
return span;
}
}
return null;
}
// Set up a Blankspan to cover each occurrence of BLANKS_TOKEN.
private BlanksSpan[] setSpans() {
Editable e = getEditableText();
String s = e.toString();
int offset = 0;
int blanksOffset;
while ((blanksOffset = s.substring(offset).indexOf(BLANKS_TOKEN)) != -1) {
offset += blanksOffset;
e.setSpan(new BlanksSpan(Typeface.BOLD), offset, offset + BLANKS_TOKEN.length(),
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
offset += BLANKS_TOKEN.length();
}
return e.getSpans(0, e.length(), BlanksSpan.class);
}
// Check change to make sure that it is acceptable to us.
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mWatcherSpan = findInSpan(start, start + count);
if (mWatcherSpan == null) {
// Change outside of a Blankspan. Just put things back the way they were.
// Do this in afterTextChaanged. mUndoChange = Editable.Factory.getInstance().newEditable(s);
} else {
// Change is OK. Track data length.
mWatcherSpan.adjustDataLength(count, after);
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing...
}
#Override
public void afterTextChanged(Editable s) {
if (mUndoChange == null) {
// The change is legal. Modify the contents of the span to the format we want.
CharSequence newContents = mWatcherSpan.getFormattedContent(s);
if (newContents != null) {
removeTextChangedListener(this);
int selection = getSelectionStart();
s.replace(s.getSpanStart(mWatcherSpan), s.getSpanEnd(mWatcherSpan), newContents);
setSelection(selection);
addTextChangedListener(this);
}
} else {
// Illegal change - put things back the way they were.
removeTextChangedListener(this);
setText(mUndoChange);
mUndoChange = null;
addTextChangedListener(this);
}
}
#SuppressWarnings("WeakerAccess")
public static class BlanksSpan extends StyleSpan {
private int mDataLength;
public BlanksSpan(int style) {
super(style);
}
#SuppressWarnings("unused")
public BlanksSpan(#NonNull Parcel src) {
super(src);
}
public void adjustDataLength(int count, int after) {
mDataLength += after - count;
}
#Nullable
public CharSequence getFormattedContent(Editable e) {
if (mDataLength == 0) {
return BLANKS_TOKEN;
}
int spanStart = e.getSpanStart(this);
return (e.getSpanEnd(this) - spanStart > mDataLength)
? e.subSequence(spanStart, spanStart + mDataLength)
: null;
}
public int getDataLength() {
return mDataLength;
}
}
#SuppressWarnings({"FieldCanBeLocal", "unused"})
private static final String TAG = "FillInBlanksEditText";
private static final String BLANKS_TOKEN = "_____";
}
activity_main.java
A sample layout.
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.fillintheblanks.FillInBlanksEditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#android:color/transparent"
android:inputType="textMultiLine"
android:padding="16dp"
android:text="Yay! You made it to _____. We should hang out! Feel _____ to follow me."
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.fillintheblanks.FillInBlanksEditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#android:color/transparent"
android:inputType="textMultiLine"
android:padding="16dp"
android:text="_____ says that it is time to _____. Are you _____?"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#id/editText" />
</android.support.constraint.ConstraintLayout>
A few things to note:
In extracted mode, cursor placement jumps around if a touch is made outside of a BlanksSpan. Things still work but misbehave a little.
The length of the blanks fields is fixed, but it can be made variable in length with some additional work.
The action mode in the control needs some work based upon requirements.

multiline edittext where parts are not editable, like fill in the blanks
You can use a TextWatcher() for this requirement
Try this he is the little work around for this
MainActivity
public class MainActivity extends AppCompatActivity {
EditText myEditText;
String startText = "I'm The First Part";
String lastText = "I'm The Last Part";
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SpannableStringBuilder firstStringBuilder = new SpannableStringBuilder(startText);
final SpannableStringBuilder lastStringBuilder = new SpannableStringBuilder(lastText);
StyleSpan firstStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);
firstStringBuilder.setSpan(firstStyleSpan, 0, firstStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
lastStringBuilder.setSpan(firstStyleSpan, 0, lastStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
myEditText = findViewById(R.id.myEditText);
spannableStringBuilder.append(firstStringBuilder);
spannableStringBuilder.append(" ");
spannableStringBuilder.append(lastStringBuilder);
myEditText.setText(spannableStringBuilder);
Selection.setSelection(myEditText.getText(), startText.length() + 1);
myEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
if (!s.toString().startsWith(firstStringBuilder.toString())
|| !s.toString().contains(lastText)) {
Log.e("StringBuilder_TAG", spannableStringBuilder.toString());
myEditText.setText(spannableStringBuilder);
Selection.setSelection(myEditText.getText(), myEditText.getText().length() - lastStringBuilder.length() - 1);
} else {
spannableStringBuilder.clear();
spannableStringBuilder.append(s.toString());
Log.e("My_TAG", spannableStringBuilder.toString());
}
}
});
}
}
layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/myEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:padding="5dp" />
</LinearLayout>
Here is the output video of above code https://www.youtube.com/watch?v=pfhUzLiFD6U
using above code you able to make not editble first and last parts of editext
Note
You can also use a TextDrawable
here are some links for that
How to put text in a drawable?
https://github.com/amulyakhare/TextDrawable
Set unchangeable some part of editText android
You can also create a custom EditText for this
Adding a prefix to an EditText
You can also use InputFilter

Solution one
Try using flexbox-layout - https://github.com/google/flexbox-layout.
Solution Two
Use textWatcher
Solution Three
Use html, css and javascript to design a simple webpage.
Use webview to load the html file.

Follow #Cheticamp 's answer, it works when you set a SpannableString in a setText() method.
Also you should override setText() method and set Spans to mSpans:
#Override
public void setText(CharSequence text, BufferType type) {
mSpans = Editable.Factory.getInstance().newEditable(text).getSpans(0, text.length(), BlanksSpan.class);
Log.d(TAG, "setText: " + mSpans.length);
super.setText(text, type);
}
if you don't set a SpannableString, Editable interface will get a simple String without Spans.
So if you call methods like:
Editable.getSpanStart(tag) returns -1;
Editable.getSpanEnd(tag) returns -1;
Editable.getSpans(start, end, Class) returns empty array

Related

using setText() don't want to call textwatcher events? [duplicate]

I have a ListView where each row has an EditText control. I want to add a TextChangedListener to each row; one that contains extra data which says which row the EditText was in. The problem is that as getView gets called, multiple TextWatchers are added; because the convertView already having a TextWatcher (and one that points to a different row).
MyTextWatcher watcher = new MyTextWatcher(currentQuestion);
EditText text = (EditText)convertView.findViewById(R.id.responseText);
text.addTextChangedListener(watcher);
MyTextWatcher is my class that implements TextWatcher; and handles the text events. CurrentQuestion lets me know which row I'm acting upon. When I type in the box; multiple instances of TextWatcher are called.
Is there any way to remove the TextWatchers before adding the new one? I see the removeTextChangedListener method, but that requires a specific TextWatcher to be passed in, and I don't know how to get the pointer to the TextWatcher that is already there.
There is no way to do this using current EditText interface directly. I see two possible solutions:
Redesign your application so you always know what TextWatcher are added to particular EditText instance.
Extend EditText and add possibility to clear all watchers.
Here is an example of second approach - ExtendedEditText:
public class ExtendedEditText extends EditText
{
private ArrayList<TextWatcher> mListeners = null;
public ExtendedEditText(Context ctx)
{
super(ctx);
}
public ExtendedEditText(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
public ExtendedEditText(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
#Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
#Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
}
And here is how you can use ExtendedEditText in xml layouts:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ua.inazaruk.HelloWorld.ExtendedEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="header"
android:gravity="center" />
</LinearLayout>
You can remove TextWatcher from your EditText. First of all I suggest you to move TextWatcher declaration outside the the editText.addTextChangedListener(...):
protected TextWatcher yourTextWatcher = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
// your logic here
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// your logic here
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// your logic here
}
};
After that you will be able to set TextWather little bit simpler:
editText.addTextChangedListener(yourTextWatcher);
Than you can remove TextWatcher like this:
editText.removeTextChangedListener(yourTextWatcher);
and set another if you want.
I also spent a lot of time finding the solution and finally ended up solving with the help of tag like below.
It would remove previous TextWatcher instances by getting references from tag of the convertView.
It perfectly solves the problem.
In your CustomAdapter file, set a new inner class like below:
private static class ViewHolder {
private TextChangedListener textChangedListener;
private EditText productQuantity;
public EditText getProductQuantity() {
return productQuantity;
}
public TextChangedListener getTextChangedListener() {
return textChangedListener;
}
public void setTextChangedListener(TextChangedListener textChangedListener) {
this.textChangedListener = textChangedListener;
}
}
Then in your overrided public View getView(int position, View convertView, ViewGroup parent) method implement the logic like below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
EditText productQuantity;
TextChangedListener textChangedListener;
if(convertView==null) {
LayoutInflater mInflater = (LayoutInflater)
context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.cart_offer_item, parent, false);
productQuantity=(EditText)convertView.findViewById(R.id.productQuantity);
addTextChangedListener(viewHolder, position);
convertView.setTag(viewHolder);
}
else
{
ViewHolder viewHolder=(ViewHolder)convertView.getTag();
productQuantity=viewHolder.getProductQuantity();
removeTextChangedListener(viewHolder);
addTextChangedListener(viewHolder, position);
}
return convertView;
}
private void removeTextChangedListener(ViewHolder viewHolder)
{
TextChangedListener textChangedListener=viewHolder.getTextChangedListener();
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.removeTextChangedListener(textChangedListener);
}
private void addTextChangedListener(ViewHolder viewHolder, int position)
{
TextChangedListener textChangedListener=new TextChangedListener(position);
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.addTextChangedListener(textChangedListener);
viewHolder.setTextChangedListener(textChangedListener);
}
Then implement TextWatcher class as below:
private class TextChangedListener implements TextWatcher
{
private int position;
TextChangedListener(int position)
{
this.position=position;
}
#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) {
Log.d("check", "text changed in EditText");
}
}
It would remove previous TextWatcher instances by getting references from tag of the convertView
I struggled with a similar problem with a lot of EditTexts in RecyclerView. I solved it by reflection. Call ReflectionTextWatcher.removeAll(your_edittext) before bind views. This piece of code finds all TextWatchers and removes them from the local EditText's list called "mListeners".
public class ReflectionTextWatcher {
public static void removeAll(EditText editText) {
try {
Field field = findField("mListeners", editText.getClass());
if (field != null) {
field.setAccessible(true);
ArrayList<TextWatcher> list = (ArrayList<TextWatcher>) field.get(editText); //IllegalAccessException
if (list != null) {
list.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static Field findField(String name, Class<?> type) {
for (Field declaredField : type.getDeclaredFields()) {
if (declaredField.getName().equals(name)) {
return declaredField;
}
}
if (type.getSuperclass() != null) {
return findField(name, type.getSuperclass());
}
return null;
}
}
I hope, this will help someone.
Save the current textwatcher in viewholder and you can find the one you want to remove.
It has been long since this question was asked, but someone might find this useful. The problem with TextWatcher in Recyclerview is that we have to make sure it is removed before the view is recycled. Otherwise, we loss the instance of the TextWatcher, and calling removeTextChangedListener(textWatcher) in the OnBindViewHolder() will only remove the current instance of TextWatcher.
The way I solve this problem is to add the TextChangedListener inside a FocusChangedListener:
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus) {
editText.addTextChangedListener(textWatcher)
}
else{
editText.removeTextChangedListener(textWatcher)
}
}
});
This way I am sure when the editText doesn't have focus then the textwatcher is removed, and added again when it has focus. So, when the recyclerview is recycled the editText will have any textChangeListener removed.
As you can see here: CodeSearch of TextView there is no way of removing all listeners. The only way is to provide the watcher you used to register it.
I do not yet fully understand why there are other listeners already registered. However you can subclass the EditText, override the addTextChangedListener(..) and in it keep a copy of all added references yourself and then delegate to the superclass implementation. You then can also provide an additional method that removes all listeners.
Get in touch if you need further explanations.
I had the same problem with xamarin/C# and I wrote for this a class to manage click events inside a ListView where the item view will be "recycled":
public class ViewOnClickEventHandler: Java.Lang.Object
{
private List<EventHandler> EventList { get; set; }
public void SetOnClickEventHandler(View view, EventHandler eventHandler)
{
if (view.Tag != null)
{
ViewOnClickEventHandler holder = ((ViewOnClickEventHandler)view.Tag);
foreach (EventHandler evH in holder.EventList)
view.Click -= evH;
for (int i = 0; i < holder.EventList.Count; i++)
holder.EventList[i] = null;
holder.EventList.Clear();
}
EventList = new List<EventHandler>();
EventList.Add(eventHandler);
view.Click += eventHandler;
view.Tag = this;
}
}
You can use it in your ListView BaseAdapter GetItem method this way:
TextView myTextView = convertView.FindViewById<TextView>(Resource.Id.myTextView);
ViewOnClickEventHandler onClick = new ViewOnClickEventHandler();
onClick.SetOnClickEventHandler(myTextView, new EventHandler(delegate (object sender, EventArgs e)
{
// Do whatever you want with the click event
}));
The ViewOnClickEventHandler class will care about multiple events on your textview. You can also change the class for textchange events. It's the same princip.
I hope this will help.
bye,
nxexo007
I resolved this situation without extend TextView class.
private ArrayList<TextWatcher> mEditTextWatcherList = new ArrayList<>();
private TextWatcher mTextWatcher1;
private TextWathcer mTextWatcher2;
mTextWathcer1 = 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) {}
};
mTextWathcer2 = 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) {}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
setListener(mTextWatcher1);
setListener(mTextWatcher2);
removeListeners();
}
private setListener(TextWatcher listener) {
mEditText.addTextChangedListener(listener);
mEditTextWatcherList.add(listener);
}
private removeListeners() {
for (TextWatcher t : mEditTextWatcherList)
mEditText.removeTextChangedListener(t);
mEditTextWatcherList.clear();
}
I struggled with a similar problem. I solved it by saving references to my textWatchers in an ArrayList:
private final List<TextWatcher> textWatchersForProfileNameTextBox = new ArrayList<>();
public void addTextWatcherToProfileNameTextBox(TextWatcher textWatcher){
textWatchersForProfileNameTextBox.add(textWatcher);
getProfileNameTextView().addTextChangedListener(textWatcher);
}
public void removeAllTextWatchersFromProfileNameTextView(){
while (!textWatchersForProfileNameTextBox.isEmpty())
getProfileNameTextView().removeTextChangedListener(textWatchersForProfileNameTextBox.remove(0));
}
If one, like me, deals with ViewHolder, then simply saving a reference to a text watcher upon its creation will not help. Upon reuse the view will get to some other ViewHolder which would not have a reference to that old text watcher, thus one won't be able to delete it.
Personally i chose to solve problem like #inazaruk, though updated code to Kotlin + renamed class to better reflect it's purpose.
class EditTextWithRemovableTextWatchers(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs) {
private val listeners by lazy { mutableListOf<TextWatcher>() }
override fun addTextChangedListener(watcher: TextWatcher) {
listeners.add(watcher)
super.addTextChangedListener(watcher)
}
override fun removeTextChangedListener(watcher: TextWatcher) {
listeners.remove(watcher)
super.removeTextChangedListener(watcher)
}
fun clearTextChangedListeners() {
for (watcher in listeners) super.removeTextChangedListener(watcher)
listeners.clear()
}
}
What I did to remove text watchers is very simple. I created an array to put my textwatchers:
final TextWatcher[] textWatchers = new TextWatcher[3];
I added them in:
final int CURRENT_PIN_CHECK = 0, NEW_PIN = 1, CONFIRM_PIN_CHECK = 2;
textWatchers[CURRENT_PIN_CHECK] = returnTextWatcherCheckPIN(CURRENT_PIN_CHECK);
textWatchers[NEW_PIN] = returnTextWatcherCheckPIN(NEW_PIN);
textWatchers[CONFIRM_PIN_CHECK] = returnTextWatcherCheckPIN(CONFIRM_PIN_CHECK);
My returnTextWatcherCheckPIN method instantiates a textWatcher with a different checker (switchMethod to check all four editTexts) on afterTextChanged.
Then whenever I remove a text watcher I just referenced the one from the array:
etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);
Check the listeners size of the editText on debug:
It's removed! That solved my problem!
I've run into the issue when using EditText in ViewHolder in RecyclerView item, and it was causing error of infinite loop, when ViewHolder was binding, cause the TextWatcher added in previous bind call was called, hence, never-ending loop..
The only working solution for that was to store TextWatcher's in the list, and then in onBindViewHolder, go trough that list and remove TextWatcher from the EditText.
private val textWatchers: MutableList<TextWatcher> = mutableListOf()
Add TextWatcher to list before assigning it to EditText:
textWatchers.add(textWatcher1)
vh.moneyAmount.editText?.addTextChangedListener(textWatcher1)
Remove them when binding the item, going to trough the entire textWatcherList:
private fun removeTextWatcher(vh: MoneyItemViewHolder) {
textWatchers.forEach { vh.moneyAmount.editText?.removeTextChangedListener(it) }
}
There isn't any other way to remove the TextWatcher's from EditText, than passing the TextWatcher object, hence it needs to be stored somewhere is we plan to remove it later.
Why not attach the TextWatcher reference to the EditText itself with setTag()?
if (etTagValue.getTag(R.id.textWatcherTag) != null) {
etTagValue.removeTextChangedListener((TextWatcher) etTagValue.getTag());
}
etTagValue.setText(myValue);
TextWatcher textWatcher = new DelayedTextWatcher(text -> meta.setDescription(text.toString()));
etTagValue.addTextChangedListener(textWatcher);
etTagValue.setTag(R.id.textWatcherTag, textWatcher);
In ids.xml under /values package:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textWatcherTag" type="id" />
</resources>

How to handle HTML text links in current activity

In my UI I have a list of names which I have displayed using Html.fromHtml() due to the way the names should be highlighted (see pic, the red names).
Under the names I have a ScrollView (see pic, the grey bit). I would like to be able to scroll to a certain part of the scrollview when a name is pressed.
So I sort of have several pieces to solve here:
Make each name clickable individually
Let my current activity handle the click
Not underline the name
OR
4. Solve the text wrapping layout using individual textviews
I know the best thing to do would be to create individual textviews for each name BUT if I do that I lose the text wrapping as seen in the picture.
Thanks for your time.
Edit: I found this Link but it uses an intent so it's not quite the same, I don't want another activity to handle the click, just the current one.
This is how I've added onClick actions to particular words in a string of text.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<TextView
android:id="#+id/mytextview1"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
</LinearLayout>
Main activity:
public class HtmlTextLinkTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.mytextview1);
tv.setText("Whale and Lion");
clickify(tv, "Whale", new ClickSpan.OnClickListener() {
#Override
public void onClick() {
Toast.makeText(HtmlTextLinkTestActivity.this, "Whale was clicked!", Toast.LENGTH_SHORT).show();
}
});
clickify(tv, "Lion", new ClickSpan.OnClickListener() {
#Override
public void onClick() {
Toast.makeText(HtmlTextLinkTestActivity.this, "Lion was clicked!", Toast.LENGTH_SHORT).show();
}
});
}
public static void clickify(TextView view, final String clickableText, final ClickSpan.OnClickListener listener) {
CharSequence text = view.getText();
String string = text.toString();
ClickSpan span = new ClickSpan(listener);
int start = string.indexOf(clickableText);
int end = start + clickableText.length();
if (start == -1) return;
if (text instanceof Spannable) {
((Spannable)text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
SpannableString s = SpannableString.valueOf(text);
s.setSpan(span, start, end, Spanned.SPAN_MARK_MARK);
view.setText(s);
}
MovementMethod m = view.getMovementMethod();
if ((m == null) || !(m instanceof LinkMovementMethod)) {
view.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
class ClickSpan extends ClickableSpan {
private OnClickListener mListener;
public ClickSpan(OnClickListener listener) {
mListener = listener;
}
#Override
public void onClick(View widget) {
if (mListener != null) mListener.onClick();
}
public interface OnClickListener {
void onClick();
}
#Override
public void updateDrawState(TextPaint ds) {
ds.setColor(0xff0000ff); // remove this if you don't want to want to override the textView's color if you specified it in main.xml
}
}

How to remove all listeners added with addTextChangedListener

I have a ListView where each row has an EditText control. I want to add a TextChangedListener to each row; one that contains extra data which says which row the EditText was in. The problem is that as getView gets called, multiple TextWatchers are added; because the convertView already having a TextWatcher (and one that points to a different row).
MyTextWatcher watcher = new MyTextWatcher(currentQuestion);
EditText text = (EditText)convertView.findViewById(R.id.responseText);
text.addTextChangedListener(watcher);
MyTextWatcher is my class that implements TextWatcher; and handles the text events. CurrentQuestion lets me know which row I'm acting upon. When I type in the box; multiple instances of TextWatcher are called.
Is there any way to remove the TextWatchers before adding the new one? I see the removeTextChangedListener method, but that requires a specific TextWatcher to be passed in, and I don't know how to get the pointer to the TextWatcher that is already there.
There is no way to do this using current EditText interface directly. I see two possible solutions:
Redesign your application so you always know what TextWatcher are added to particular EditText instance.
Extend EditText and add possibility to clear all watchers.
Here is an example of second approach - ExtendedEditText:
public class ExtendedEditText extends EditText
{
private ArrayList<TextWatcher> mListeners = null;
public ExtendedEditText(Context ctx)
{
super(ctx);
}
public ExtendedEditText(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
public ExtendedEditText(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
#Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
#Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
}
And here is how you can use ExtendedEditText in xml layouts:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ua.inazaruk.HelloWorld.ExtendedEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="header"
android:gravity="center" />
</LinearLayout>
You can remove TextWatcher from your EditText. First of all I suggest you to move TextWatcher declaration outside the the editText.addTextChangedListener(...):
protected TextWatcher yourTextWatcher = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
// your logic here
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// your logic here
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// your logic here
}
};
After that you will be able to set TextWather little bit simpler:
editText.addTextChangedListener(yourTextWatcher);
Than you can remove TextWatcher like this:
editText.removeTextChangedListener(yourTextWatcher);
and set another if you want.
I also spent a lot of time finding the solution and finally ended up solving with the help of tag like below.
It would remove previous TextWatcher instances by getting references from tag of the convertView.
It perfectly solves the problem.
In your CustomAdapter file, set a new inner class like below:
private static class ViewHolder {
private TextChangedListener textChangedListener;
private EditText productQuantity;
public EditText getProductQuantity() {
return productQuantity;
}
public TextChangedListener getTextChangedListener() {
return textChangedListener;
}
public void setTextChangedListener(TextChangedListener textChangedListener) {
this.textChangedListener = textChangedListener;
}
}
Then in your overrided public View getView(int position, View convertView, ViewGroup parent) method implement the logic like below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
EditText productQuantity;
TextChangedListener textChangedListener;
if(convertView==null) {
LayoutInflater mInflater = (LayoutInflater)
context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.cart_offer_item, parent, false);
productQuantity=(EditText)convertView.findViewById(R.id.productQuantity);
addTextChangedListener(viewHolder, position);
convertView.setTag(viewHolder);
}
else
{
ViewHolder viewHolder=(ViewHolder)convertView.getTag();
productQuantity=viewHolder.getProductQuantity();
removeTextChangedListener(viewHolder);
addTextChangedListener(viewHolder, position);
}
return convertView;
}
private void removeTextChangedListener(ViewHolder viewHolder)
{
TextChangedListener textChangedListener=viewHolder.getTextChangedListener();
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.removeTextChangedListener(textChangedListener);
}
private void addTextChangedListener(ViewHolder viewHolder, int position)
{
TextChangedListener textChangedListener=new TextChangedListener(position);
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.addTextChangedListener(textChangedListener);
viewHolder.setTextChangedListener(textChangedListener);
}
Then implement TextWatcher class as below:
private class TextChangedListener implements TextWatcher
{
private int position;
TextChangedListener(int position)
{
this.position=position;
}
#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) {
Log.d("check", "text changed in EditText");
}
}
It would remove previous TextWatcher instances by getting references from tag of the convertView
I struggled with a similar problem with a lot of EditTexts in RecyclerView. I solved it by reflection. Call ReflectionTextWatcher.removeAll(your_edittext) before bind views. This piece of code finds all TextWatchers and removes them from the local EditText's list called "mListeners".
public class ReflectionTextWatcher {
public static void removeAll(EditText editText) {
try {
Field field = findField("mListeners", editText.getClass());
if (field != null) {
field.setAccessible(true);
ArrayList<TextWatcher> list = (ArrayList<TextWatcher>) field.get(editText); //IllegalAccessException
if (list != null) {
list.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static Field findField(String name, Class<?> type) {
for (Field declaredField : type.getDeclaredFields()) {
if (declaredField.getName().equals(name)) {
return declaredField;
}
}
if (type.getSuperclass() != null) {
return findField(name, type.getSuperclass());
}
return null;
}
}
I hope, this will help someone.
Save the current textwatcher in viewholder and you can find the one you want to remove.
It has been long since this question was asked, but someone might find this useful. The problem with TextWatcher in Recyclerview is that we have to make sure it is removed before the view is recycled. Otherwise, we loss the instance of the TextWatcher, and calling removeTextChangedListener(textWatcher) in the OnBindViewHolder() will only remove the current instance of TextWatcher.
The way I solve this problem is to add the TextChangedListener inside a FocusChangedListener:
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus) {
editText.addTextChangedListener(textWatcher)
}
else{
editText.removeTextChangedListener(textWatcher)
}
}
});
This way I am sure when the editText doesn't have focus then the textwatcher is removed, and added again when it has focus. So, when the recyclerview is recycled the editText will have any textChangeListener removed.
As you can see here: CodeSearch of TextView there is no way of removing all listeners. The only way is to provide the watcher you used to register it.
I do not yet fully understand why there are other listeners already registered. However you can subclass the EditText, override the addTextChangedListener(..) and in it keep a copy of all added references yourself and then delegate to the superclass implementation. You then can also provide an additional method that removes all listeners.
Get in touch if you need further explanations.
I had the same problem with xamarin/C# and I wrote for this a class to manage click events inside a ListView where the item view will be "recycled":
public class ViewOnClickEventHandler: Java.Lang.Object
{
private List<EventHandler> EventList { get; set; }
public void SetOnClickEventHandler(View view, EventHandler eventHandler)
{
if (view.Tag != null)
{
ViewOnClickEventHandler holder = ((ViewOnClickEventHandler)view.Tag);
foreach (EventHandler evH in holder.EventList)
view.Click -= evH;
for (int i = 0; i < holder.EventList.Count; i++)
holder.EventList[i] = null;
holder.EventList.Clear();
}
EventList = new List<EventHandler>();
EventList.Add(eventHandler);
view.Click += eventHandler;
view.Tag = this;
}
}
You can use it in your ListView BaseAdapter GetItem method this way:
TextView myTextView = convertView.FindViewById<TextView>(Resource.Id.myTextView);
ViewOnClickEventHandler onClick = new ViewOnClickEventHandler();
onClick.SetOnClickEventHandler(myTextView, new EventHandler(delegate (object sender, EventArgs e)
{
// Do whatever you want with the click event
}));
The ViewOnClickEventHandler class will care about multiple events on your textview. You can also change the class for textchange events. It's the same princip.
I hope this will help.
bye,
nxexo007
I resolved this situation without extend TextView class.
private ArrayList<TextWatcher> mEditTextWatcherList = new ArrayList<>();
private TextWatcher mTextWatcher1;
private TextWathcer mTextWatcher2;
mTextWathcer1 = 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) {}
};
mTextWathcer2 = 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) {}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
setListener(mTextWatcher1);
setListener(mTextWatcher2);
removeListeners();
}
private setListener(TextWatcher listener) {
mEditText.addTextChangedListener(listener);
mEditTextWatcherList.add(listener);
}
private removeListeners() {
for (TextWatcher t : mEditTextWatcherList)
mEditText.removeTextChangedListener(t);
mEditTextWatcherList.clear();
}
I struggled with a similar problem. I solved it by saving references to my textWatchers in an ArrayList:
private final List<TextWatcher> textWatchersForProfileNameTextBox = new ArrayList<>();
public void addTextWatcherToProfileNameTextBox(TextWatcher textWatcher){
textWatchersForProfileNameTextBox.add(textWatcher);
getProfileNameTextView().addTextChangedListener(textWatcher);
}
public void removeAllTextWatchersFromProfileNameTextView(){
while (!textWatchersForProfileNameTextBox.isEmpty())
getProfileNameTextView().removeTextChangedListener(textWatchersForProfileNameTextBox.remove(0));
}
If one, like me, deals with ViewHolder, then simply saving a reference to a text watcher upon its creation will not help. Upon reuse the view will get to some other ViewHolder which would not have a reference to that old text watcher, thus one won't be able to delete it.
Personally i chose to solve problem like #inazaruk, though updated code to Kotlin + renamed class to better reflect it's purpose.
class EditTextWithRemovableTextWatchers(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs) {
private val listeners by lazy { mutableListOf<TextWatcher>() }
override fun addTextChangedListener(watcher: TextWatcher) {
listeners.add(watcher)
super.addTextChangedListener(watcher)
}
override fun removeTextChangedListener(watcher: TextWatcher) {
listeners.remove(watcher)
super.removeTextChangedListener(watcher)
}
fun clearTextChangedListeners() {
for (watcher in listeners) super.removeTextChangedListener(watcher)
listeners.clear()
}
}
What I did to remove text watchers is very simple. I created an array to put my textwatchers:
final TextWatcher[] textWatchers = new TextWatcher[3];
I added them in:
final int CURRENT_PIN_CHECK = 0, NEW_PIN = 1, CONFIRM_PIN_CHECK = 2;
textWatchers[CURRENT_PIN_CHECK] = returnTextWatcherCheckPIN(CURRENT_PIN_CHECK);
textWatchers[NEW_PIN] = returnTextWatcherCheckPIN(NEW_PIN);
textWatchers[CONFIRM_PIN_CHECK] = returnTextWatcherCheckPIN(CONFIRM_PIN_CHECK);
My returnTextWatcherCheckPIN method instantiates a textWatcher with a different checker (switchMethod to check all four editTexts) on afterTextChanged.
Then whenever I remove a text watcher I just referenced the one from the array:
etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);
Check the listeners size of the editText on debug:
It's removed! That solved my problem!
I've run into the issue when using EditText in ViewHolder in RecyclerView item, and it was causing error of infinite loop, when ViewHolder was binding, cause the TextWatcher added in previous bind call was called, hence, never-ending loop..
The only working solution for that was to store TextWatcher's in the list, and then in onBindViewHolder, go trough that list and remove TextWatcher from the EditText.
private val textWatchers: MutableList<TextWatcher> = mutableListOf()
Add TextWatcher to list before assigning it to EditText:
textWatchers.add(textWatcher1)
vh.moneyAmount.editText?.addTextChangedListener(textWatcher1)
Remove them when binding the item, going to trough the entire textWatcherList:
private fun removeTextWatcher(vh: MoneyItemViewHolder) {
textWatchers.forEach { vh.moneyAmount.editText?.removeTextChangedListener(it) }
}
There isn't any other way to remove the TextWatcher's from EditText, than passing the TextWatcher object, hence it needs to be stored somewhere is we plan to remove it later.
Why not attach the TextWatcher reference to the EditText itself with setTag()?
if (etTagValue.getTag(R.id.textWatcherTag) != null) {
etTagValue.removeTextChangedListener((TextWatcher) etTagValue.getTag());
}
etTagValue.setText(myValue);
TextWatcher textWatcher = new DelayedTextWatcher(text -> meta.setDescription(text.toString()));
etTagValue.addTextChangedListener(textWatcher);
etTagValue.setTag(R.id.textWatcherTag, textWatcher);
In ids.xml under /values package:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textWatcherTag" type="id" />
</resources>

How to clear an EditText on click?

In Android how can I make an EditText clear when it's clicked?
E.g., if I have an EditText with some characters in, such as 'Enter Name', when the user clicks on it these characters disappear.
I'm not sure if you are after this, but try this XML:
android:hint="Enter Name"
It displays that text when the input field is empty, selected or unselected.
Or if you want it to do exactly as you described, assign a onClickListener on the editText and set it empty with setText().
Are you looking for behavior similar to the x that shows up on the right side of text fields on an iphone that clears the text when tapped? It's called clearButtonMode there. Here is how to create that same functionality in an Android EditText view:
String value = "";//any text you are pre-filling in the EditText
final EditText et = new EditText(this);
et.setText(value);
final Drawable x = getResources().getDrawable(R.drawable.presence_offline);//your x image, this one from standard android images looks pretty good actually
x.setBounds(0, 0, x.getIntrinsicWidth(), x.getIntrinsicHeight());
et.setCompoundDrawables(null, null, value.equals("") ? null : x, null);
et.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (et.getCompoundDrawables()[2] == null) {
return false;
}
if (event.getAction() != MotionEvent.ACTION_UP) {
return false;
}
if (event.getX() > et.getWidth() - et.getPaddingRight() - x.getIntrinsicWidth()) {
et.setText("");
et.setCompoundDrawables(null, null, null, null);
}
return false;
}
});
et.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
et.setCompoundDrawables(null, null, et.getText().toString().equals("") ? null : x, null);
}
#Override
public void afterTextChanged(Editable arg0) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
});
after onclick of any action do below step
((EditText) findViewById(R.id.yoursXmlId)).setText("");
or
write this in XML file
<EditText
---------- other stuffs ------
android:hint="Enter Name" />
its works fine for me. Hope to you all.
that is called hint in android
use
android:hint="Enter Name"
#Harris's answer is great, I've implemented it as a separate subclass of EditText, which can make it easier to use if your code already adds TextChangedListeners.
Also, I've tweaked it so that, if you already use any Compound Drawables, it leaves them intact.
Code is here, for anyone who needs it:
package com.companyname.your
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
public class ClearableEditText extends EditText {
public String defaultValue = "";
final Drawable imgX = getResources().getDrawable(android.R.drawable.presence_offline ); // X image
public ClearableEditText(Context context) {
super(context);
init();
}
public ClearableEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public ClearableEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init() {
// Set bounds of our X button
imgX.setBounds(0, 0, imgX.getIntrinsicWidth(), imgX.getIntrinsicHeight());
// There may be initial text in the field, so we may need to display the button
manageClearButton();
this.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
ClearableEditText et = ClearableEditText.this;
// Is there an X showing?
if (et.getCompoundDrawables()[2] == null) return false;
// Only do this for up touches
if (event.getAction() != MotionEvent.ACTION_UP) return false;
// Is touch on our clear button?
if (event.getX() > et.getWidth() - et.getPaddingRight() - imgX.getIntrinsicWidth()) {
et.setText("");
ClearableEditText.this.removeClearButton();
}
return false;
}
});
this.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ClearableEditText.this.manageClearButton();
}
#Override
public void afterTextChanged(Editable arg0) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
});
}
void manageClearButton() {
if (this.getText().toString().equals("") )
removeClearButton();
else
addClearButton();
}
void addClearButton() {
this.setCompoundDrawables(this.getCompoundDrawables()[0],
this.getCompoundDrawables()[1],
imgX,
this.getCompoundDrawables()[3]);
}
void removeClearButton() {
this.setCompoundDrawables(this.getCompoundDrawables()[0],
this.getCompoundDrawables()[1],
null,
this.getCompoundDrawables()[3]);
}
}
If you want to have text in the edit text and remove it like you say, try:
final EditText text_box = (EditText) findViewById(R.id.input_box);
text_box.setOnFocusChangeListener(new OnFocusChangeListener()
{
#Override
public void onFocusChange(View v, boolean hasFocus)
{
if (hasFocus==true)
{
if (text_box.getText().toString().compareTo("Enter Text")==0)
{
text_box.setText("");
}
}
}
});
Be careful when setting text with an onClick listener on the field you are setting the text. I was doing this and setting the text to an empty string. This was causing the pointer to come up to indicate where my cursor was, which will normally go away after a few seconds. When I did not wait for it to go away before leaving my page causing finish() to be called, it would cause a memory leak and crash my app. Took me a while to figure out what was causing the crash on this one..
Anyway, I would recommend using selectAll() in your on click listener rather than setText() if you can. This way, once the text is selected, the user can start typing and all of the previous text will be cleared.
pic of the suspect pointer: http://i.stack.imgur.com/juJnt.png
//To clear When Clear Button is Clicked
firstName = (EditText) findViewById(R.id.firstName);
clear = (Button) findViewById(R.id.clearsearchSubmit);
clear.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (v.getId() == R.id.clearsearchSubmit);
firstName.setText("");
}
});
This will help to clear the wrong keywords that you have typed in so instead of pressing backspace again and again you can simply click the button to clear everything.It Worked For me. Hope It Helps
Code for clearing up the text field when clicked
<EditText android:onClick="TextFieldClicked"/>
public void TextFieldClicked(View view){
if(view.getId()==R.id.editText1);
text.setText("");
}
For me the easiest way...
Create an public EditText, for Example "myEditText1"
public EditText myEditText1;
Then, connect it with the EditText which should get cleared
myEditText1 = (EditText) findViewById(R.id.numberfield);
After that, create an void which reacts to an click to the EditText an let it clear the Text inside it when its Focused, for Example
#OnClick(R.id.numberfield)
void textGone(){
if (myEditText1.isFocused()){
myEditText1.setText("");
}
}
Hope i could help you, Have a nice Day everyone
((EditText) findViewById(R.id.User)).setText("");
((EditText) findViewById(R.id.Password)).setText("");
For kotlin's friends:
edtxt1.onFocusChangeListener = OnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
edtxt1.text = ""
}
}

Ellipsize only a section in a TextView

I was wondering if it is possible to abbreviate only a portion of a string in a TextView. What I would like to do is something like this:
Element with short title (X)
Element with a very lo...(X)
The title should be ellipsized, but the X must be always visible. In my case, is not possible to use more than one TextView. Do you think there is a simple way of doing this?
Thanks!
I really needed a clean solution for a project so after searching around and not finding any solutions I felt I liked, I took some time to write this up.
Here is an implementation of a TextView with enhanced ellipsis control. The way it works is by using Android's Spanned interface. It defines an enum you can use to tag the specific section of text you'd like to be ellipsized if needed.
Limitations:
Does not support ellipsis at MIDDLE. This should be easy to add if it's really needed (I didn't).
This class will always render the text onto one line, as it only supports a single line of text. Others are welcome to extend it if that's needed (but it's a far harder problem).
Here's a sample of the usage:
FooActivity.java
class FooActivity extends Activity {
/**
* You can do this however you'd like, this example uses this simple
* helper function to create a text span tagged for ellipsizing
*/
CharSequence ellipsizeText(String text) {
SpannableString s = new SpannableString(text);
s.setSpan(TrimmedTextView.EllipsizeRange.ELLIPSIS_AT_END, 0, s.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
return s;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.foo_layout);
TextView textView = (TextView) findViewById(R.id.textView4);
SpannableStringBuilder text = new SpannableStringBuilder();
text.append(ellipsizeText("This is a long string of text which has important information "));
text.append("AT THE END");
textView.setText(text);
}
}
res/layouts/foo_layout.xml
<com.example.text.TrimmedTextView
android:id="#+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"/>
That's it
Here's an example of the result:
The Implementation
package com.example.text;
import android.content.Context;
import android.text.Editable;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
public class TrimmedTextView extends TextView {
public static enum EllipsizeRange {
ELLIPSIS_AT_START, ELLIPSIS_AT_END;
}
private CharSequence originalText;
private SpannableStringBuilder builder = new SpannableStringBuilder();
/**
* This allows the cached value of the original unmodified text to be
* invalidated whenever set externally.
*/
private final TextWatcher textCacheInvalidator = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
originalText = null;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
};
public TrimmedTextView(Context context) {
this(context, null, 0);
}
public TrimmedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TrimmedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
addTextChangedListener(textCacheInvalidator);
Log.v("TEXT", "Set!");
}
/**
* Make sure we return the original unmodified text value if it's been
* custom-ellipsized by us.
*/
public CharSequence getText() {
if (originalText == null) {
return super.getText();
}
return originalText;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Layout layout = getLayout();
CharSequence text = layout.getText();
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
int ellipsisStart;
int ellipsisEnd;
TruncateAt where = null;
ellipsisStart = spanned.getSpanStart(EllipsizeRange.ELLIPSIS_AT_START);
if (ellipsisStart >= 0) {
where = TruncateAt.START;
ellipsisEnd = spanned.getSpanEnd(EllipsizeRange.ELLIPSIS_AT_START);
} else {
ellipsisStart = spanned.getSpanStart(EllipsizeRange.ELLIPSIS_AT_END);
if (ellipsisStart >= 0) {
where = TruncateAt.END;
ellipsisEnd = spanned.getSpanEnd(EllipsizeRange.ELLIPSIS_AT_END);
} else {
// No EllipsisRange spans in this text
return;
}
}
Log.v("TEXT", "ellipsisStart: " + ellipsisStart);
Log.v("TEXT", "ellipsisEnd: " + ellipsisEnd);
Log.v("TEXT", "where: " + where);
builder.clear();
builder.append(text, 0, ellipsisStart).append(text, ellipsisEnd, text.length());
float consumed = Layout.getDesiredWidth(builder, layout.getPaint());
CharSequence ellipsisText = text.subSequence(ellipsisStart, ellipsisEnd);
CharSequence ellipsizedText = TextUtils.ellipsize(ellipsisText, layout.getPaint(),
layout.getWidth() - consumed, where);
if (ellipsizedText.length() < ellipsisText.length()) {
builder.clear();
builder.append(text, 0, ellipsisStart).append(ellipsizedText)
.append(text, ellipsisEnd, text.length());
setText(builder);
originalText = text;
requestLayout();
invalidate();
}
}
}
}
You can try using something like this:
myTextView.setEllipsize(TextUtils.TruncateAt.MIDDLE);
It might not give you exactly what you want though, it may do something like this:
Element wi...title (X)
Reference Info
TruncateAt
setEllipsize

Categories

Resources