I have a form with multiple EditText items each wrapped in a TextInputLayout. When clicking the Submit button, I check to see if the EditText is empty. If it is, I call an error on that field, e.g. ageWrapper.setError("Age required"). This shows an error message under the field, and also changes the hint of the EditText to red. This occurs for all input fields on the page and the errors are working fine.
The problem occurs after I clear the error. I first leave the field blank and submit - error shows up and things are displayed red. I then type something into the field, and submit again. Now, the error goes away as it should (ageWrapper.setErrorEnabled(false)), but the hint remains in red rather than changing back to its normal, non-error, colour.
This should not be happening because the error has been cleared by typing something into the field, and yet the hint is still red implying an error, even though the error message itself (under the field) disappears.
When I click the field again, or any other field on the page, the red hint text then changes back to its normal colour.
Clearly the error display is working - it displays and goes away as it should. But why does the hint text remain red after Ive cleared the error by typing something into the field, and only changes back to its regular colour after a click to the field itself (or another field)? How do I get rid of this behaviour?
In my mind, if both fields are empty, both will display red and show error messages. If I then fill in one of the fields, setErrorEnabled(false) should be set on that field and the hint text should go back to normal but it doesnt.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/new_layout_padding">
<android.support.design.widget.TextInputLayout
android:id="#+id/input_condition_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/input_subnum_wrapper">
<EditText
android:id="#+id/input_condition"
android:layout_width="match_parent"
android:layout_height="#dimen/new_form_element_height"
android:hint="#string/input_text_condition"
android:inputType="number" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="#+id/input_age_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/input_condition_wrapper">
<EditText
android:id="#+id/input_age"
android:layout_width="match_parent"
android:layout_height="#dimen/new_form_element_height"
android:hint="#string/input_text_age"
android:inputType="number" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="#+id/input_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:ems="5"
android:text="#string/input_button_save" />
</RelativeLayout>
Java class for the fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new, container, false);
//Get all text fields
conditionWrapper = (TextInputLayout) view.findViewById(R.id.input_condition_wrapper);
ageWrapper = (TextInputLayout) view.findViewById(R.id.input_age_wrapper);
//Listener for create button
createButton = (Button) view.findViewById(R.id.input_submit);
createButton.setOnClickListener(this);
// Inflate the layout for this fragment
return view;
}
//Create/submit button click
#Override
public void onClick(View v) {
//Get input values
String condition = conditionWrapper.getEditText().getText().toString();
String age = ageWrapper.getEditText().getText().toString();
//If all the validation passes, submit the form. Else, show errors
if (!isEmpty(condition) & !isEmpty(age)) {
//Submit form data
} else {
if (isEmpty(condition)) {
conditionWrapper.setError("Condition required");
} else {
conditionWrapper.setErrorEnabled(false);
}
if (isEmpty(age)) {
ageWrapper.setError("Age required");
} else {
ageWrapper.setErrorEnabled(false);
}
}
}
//Check if a string is empty
public boolean isEmpty(String string) {
if (string.equals("")) {
return true;
} else {
return false;
}
}
change your code to
if (isEmpty(condition)) {
conditionWrapper.setError("Condition required");
} else {
conditionWrapper.setError(null);
}
Use doOnTextChanged listner on edit text and check whenever user write something is there error enable if then make it null and change box color
binding.loginEmailValue.doOnTextChanged { text, start, before, count ->
if(binding.loginEmailContainer.error.toString().isNotEmpty()){
binding.loginEmailContainer.error = null
}
}
Related
I need to set prompt for my TextView, if String is too long to fit inside TV (i set max length 20) then it displays only part of it with "..." at the end. I want to display prompt with full String when i click on TV. Is it possible? And if yes then how to do it?
Inside activity:
textCompany.setText(testDb.getCompanyName(id));
textCompany.setEllipsize(null);
And XML:
<TextView
android:id="#+id/textCompany"
android:layout_width="match_parent"
android:layout_below="#+id/textId"
android:layout_height="wrap_content"
android:maxLength="20"
android:ellipsize="end"
android:gravity="end"
android:layout_marginEnd="20dp"
android:textSize="17sp"
android:text="verylongstringjusttotestifthisworksandletshopeitwill" />
You can do that with a simple onClickListener. First, you check what the length of the text is, then if it's more than 20 characters, you take the first 20 and add three dots to the end and display that. At the same time, you save the full text to a temporary variable, and you display it when someone clicks your TextView.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView textView = findViewById(R.id.textCompany);
String shortMessage;
final String message = textView.getText().toString();
if(message.length() >= 20){
shortMessage = message.substring(0,19)+"...";
textView.setText(shortMessage);
}
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
});
}
Note: make sure you make your textView clickable by adding the following to your xml:
android:clickable="true"
The behavior you mentioned is due to
android:ellipsize="end"
It displays the end of text with "Some text..." if it does not fit in width.
You can programmatically change the ellipsize value to none to display the full text on Click.
myTextView.setEllipsize(null);
I have a dropdown spinner which is showed when click on a button looks like this:
<Button
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Spinner
android:id="#+id/spinMenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:spinnerMode="dropdown"
android:visibility="invisible" />
<ListView
android:id="#+id/lvWall"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Here is snippet showing dropdown popup:
findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
spinMenu.performClick();
}
});
My spinner can show dropdown popup correctly. The problem is my layout has a listview which getting data from web service in background. When data is loading completely, all list items will be showed or refreshed, and the spinner's dropdown popup is dismiss (I even don't touch anything on screen). I think the problem is window has changed focus on other view. So how can I prevent it?
Update:
Here is my list after load data from background, it's very simple:
List<Feed> data = result;
FeedAdapter adapter = new FeedAdapter (this, data);
ListView lvWall = (ListView)findViewById(R.id.lvWall);
lvWall.setAdapter(adapter);
And data for spinner:
List<String> list = getMenus();
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinMenu.setAdapter(dataAdapter);
If I understand correctly, you have a Spinner view which you set as invisible with the only purpose of showing the popup menu, but not the Spinner view itself. In that case, the problem is probably related to this snippet in Spinner.java, more precisely in DropdownPopup.show():
public void show(int textDirection, int textAlignment) {
...
super.show();
...
// Make sure we hide if our anchor goes away.
// TODO: This might be appropriate to push all the way down to PopupWindow,
// but it may have other side effects to investigate first. (Text editing handles, etc.)
final ViewTreeObserver vto = getViewTreeObserver();
if (vto != null) {
final OnGlobalLayoutListener layoutListener = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!Spinner.this.isVisibleToUser()) {
dismiss();
} else {
computeContentWidth();
...
What does this mean? Basically that the Spinner is set up with a ViewTreeObserver to be notified whenever a layout pass changes the views in the screen. And if the Spinner is not visible after that happens, the popup is dismissed. Loading the ListView evidently causes a change in the view hierarchy, and it's being fired when the data arrives from the server.
For general usage this is completely logical: if the Spinner is hidden, or it goes off screen, or something like that, it would be reasonable to make the popup go away. However, it's interferring with what you're attempting to do. It would be nice if you could somehow override isVisibleToUser(), but unfortunately it's marked as #hide, so that's not possible.
Might I suggest a workaround, like setting the Spinner visible but really small? Like, with a height of 1px? I believe that should be enough to fool this method.
Another option, and probably a more sensible one, would be to forgo the Spinner altogether and use a PopupMenu instead. You can anchor it to the Button, load it dynamically, and show it when the button is pressed. The visual effect should be the same.
If you think the problem is due to the change of focus . You can set it with multiple ways.
first create a focuschangeListener and onfocuschange do whatever you like
yourView.setOnFocusChangeListener(testListener);
#Override
public void onFocusChange(View arg0,
boolean isFocused)
{
if(isFocused)
{
//do your work here
}
else
{
}
}
And second way to prevent view from focus..
<!-- Dummy item to prevent AutoCompleteTextView from receiving focus -->
<LinearLayout
android:focusable="true" android:focusableInTouchMode="true"
android:layout_width="0px" android:layout_height="0px"/>
<!-- :nextFocusUp and :nextFocusLeft have been set to the id of this component
to prevent the dummy from receiving focus again -->
<AutoCompleteTextView android:id="#+id/autotext"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:nextFocusUp="#id/autotext" android:nextFocusLeft="#id/autotext"/>
I use the FormEditText widget for a form (obviously). Thing is: After being tested not valid once, the warning red exclamation mark won't disappear even after entering a correct input and the field.testValidity() returning true. I also haven't found a method of FormEditText to manually clear it.
Anyone else experienced this?
This is the method I use when the send button is tapped from the action bar:
public boolean checkFields() {
FormEditText[] allFields = { firstname, lastname, street, zip, city,
email, phone };
boolean allValid = true;
FormEditText firstFailedField = null;
for (FormEditText field : allFields) {
if (!field.testValidity()) {
Log.d("FOR", "failed field: " + field.getHint());
allValid = false;
if (firstFailedField == null) {
firstFailedField = field;
}
}
}
if (allValid) {
} else {
firstFailedField.requestFocus();
}
return allValid;
}
And this is part of my layout:
<com.andreabaccega.widget.FormEditText
android:id="#+id/firstname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="Vorname"
android:inputType="textPersonName"
android:singleLine="true"
whatever:customRegexp="[a-zA-Z]{2,}"
whatever:emptyErrorString="Bitte Vornamen angeben"
whatever:testErrorString="Bitte Vornamen angeben"
whatever:testType="regexp" >
</com.andreabaccega.widget.FormEditText>
<com.andreabaccega.widget.FormEditText
android:id="#+id/lastname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="Nachname"
android:inputType="textPersonName"
android:singleLine="true"
whatever:customRegexp="[a-zA-Z]{2,}"
whatever:emptyErrorString="Bitte Nachnamen angeben"
whatever:testErrorString="Bitte Nachnamen angeben"
whatever:testType="regexp" >
</com.andreabaccega.widget.FormEditText>
I entered Kim in the firstname field, pressed send. Theres a warning on the lastname field. Enter Jong in the lastname field, but the exclamation mark won't hide. And the field does test valid!
As suggested I'll post my comment as an answer:
You can try field.setError(null): in this way you can hide the error from an EditText. It should work for FormEditText since FormEditText extends EditText.
I'm creating a compose screen for my app. I have a ScrollView which contains a RelativeView which in turn contains two things: the EditText where the user types a message, and an ImageView whose visibility is toggled on and off depending on whether an image is attached to the status or not. Here's that part of my layout XML.
<!-- #dimen/bigGap = 8dp -->
<ScrollView android:id="#+id/parentScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:layout_marginTop="#dimen/bigGap"
android:layout_marginRight="#dimen/bigGap"
android:layout_marginLeft="#dimen/bigGap"
android:layout_marginBottom="#dimen/bigGap"
android:layout_above="#+id/footer"
android:background="#006400"
> <!-- green background color -->
<RelativeLayout android:id="#+id/parentLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFD700"> <!-- yellow background color -->
<EditText android:id="#+id/postText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#dddddd"
android:inputType="textMultiLine"
android:gravity="top|left"
/> <!-- gray background color -->
<ImageView android:id="#+id/postImage"
android:layout_width="#dimen/thumbnailSize"
android:layout_height="#dimen/thumbnailSize"
android:visibility="gone"
android:layout_below="#id/postText"
/>
</RelativeLayout>
</ScrollView>
Because my EditText's height is wrap_content, the whole thing starts off with a single line of gray background (the EditText) on top of a yellow background (the RelativeLayout, which fully covers the green background of the ScrollView). However, I'll later change all the views' backgrounds to white (to make them look like a single component) and it will be counter-intuitive for the user to be able to tap only that single line of EditText to make the keyboard pop up.
What I want to do is to redirect the click and long click actions of the RelativeLayout to the click and long click actions of the EditText, but my code below doesn't work. Help?
final EditText editText = (EditText) findViewById(R.id.postText);
RelativeLayout rl = (RelativeLayout) findViewById(R.id.parentLinearLayout);
rl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Logger.d("onClick invoked!");
editText.performClick();
}
});
rl.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Logger.d("onLongClick invoked!");
return editText.performLongClick();
}
});
My intention here is so that when the RelativeLayout is clicked, the keyboard pops up (as it does when done to an EditText) and when long-pressed, display the cut/copy/paste/text selection options (same behavior with EditText).
From the description, What you actually want is to open the keyboard. So your title for the question suggests a solution, not the actual problem.
call this from your click listener (or immediately when you show the page):
((InputMethodManager) myActivity
.getSystemService(Context.INPUT_METHOD_SERVICE))
.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
Edit:
You must also call editText.requestFocus(); (#Phil is right), but from my experience it's not enough to open the keyboard, so you'll need also the ugly code above.
What you want is for your EditText to gain focus (which in-turn causes the keyboard to show):
rl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Logger.d("onClick invoked!");
editText.requestFocus();
}
});
rl.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Logger.d("onLongClick invoked!");
editText.requestFocus();
return true;
}
});
Is there a way to allow the user to select / copy text in a TextView? I need the same functionality of EditText where you can long-press the control and get the popup options of select all / copy, but I need the control to look like a TextView.
Tried a few things like making an EditText use the editable="none" option or inputType="none", but those still retain the framed background of an EditText, which I don't want,
Thanks
------- Update ----------------------
This is 99% there, all I'd want is for the selection hilight to be visible (the orange stuff). Other than that it's good, could live with this though:
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:editable="false"
style="?android:attr/textViewStyle"
android:textColor="#color/white"
android:textAppearance="#android:style/TextAppearance.Medium"
android:cursorVisible="false"
android:background="#null" />
I guess it's being caused because of cursorVisible="false" but without that the cursor is present even without any selection being made.
android:textIsSelectable works (at least in ICS - I haven't yet checked in earlier versions)
<TextView
android:id="#+id/deviceIdTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="" />
Text View needs to be enabled, focusable, longClickable and textIsSelectable
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:id="#+id/pwTextView"
android:enabled="true"
android:textIsSelectable="true"
android:focusable="true"
android:longClickable="true" />
I think I have a better solution.
Just call
registerForContextMenu(yourTextView);
and your TextView will be registered for receiving context menu events.
Then override onCreateContextMenu in your Activity
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
//user has long pressed your TextView
menu.add(0, v.getId(), 0, "text that you want to show in the context menu - I use simply Copy");
//cast the received View to TextView so that you can get its text
TextView yourTextView = (TextView) v;
//place your TextView's text in clipboard
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(yourTextView.getText());
}
Hope this helps you and anyone else looking for a way to copy text from a TextView
textview1.setTextIsSelectable(true);
This will enable user to select and copy text on long clicking or as we do usually
Using Kotlin Programmatically (Manual Copy)
button.setTextIsSelectable(true)
Or, add a Kotlin property extension
var TextView.selectable
get() = isTextSelectable
set(value) = setTextIsSelectable(value)
Then call
textview.selectable = true
// or
if (textview.selectable) { ...
Using Kotlin Programmatically (Auto-Copy)
If you want to auto-copy when user long-presses you view, this is the base code required:
myView.setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", myString)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
You may want to add a Toast to confirm it happened
Or, add a Kotlin extension function
myView.copyOnHold() // pass custom string to not use view contents
fun TextView.copyOnHold(customText: String? = null) {
setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", customText ?: text)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
}
Using Xml (Manual Copy)
Add this to your <TextView>
android:textIsSelectable="true"
NOTE: All of these require android:enabled="true" and android:focusable="true", which are the default values for a TextView.
I'm trying to implement the same, and your question helped me to set my editext layout correctly. So Thanks! :)
Then I realized, that the highlight will actually be visible if the cursor is on.
But I just like you do not want to see a cursor before long clicking on the text, so I hide the cursor in the layout.xml file just like you, and added an eventlistener for long click and display the cursor only when a selection starts.
So add the listener in your Activity in the onCreate section:
public TextView htmltextview;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
htmltextview.setOnLongClickListener(new OnLongClickListener(){
public boolean onLongClick(View v) {
htmltextview.setCursorVisible(true);
return false;
}
});
}
And voilá, no cursor at the beginning, and if you long-click, the cursor appears with the selection boundaries.
I hope I could help.
Cheers,
fm
I was also trying to do something similar but still needed a custom approach with manipulation of highlighting of text in TextView. I triggered highlight and copying on LongClick action.
This is how I managed using SpannableString:
SpannableString highlightString = new SpannableString(textView.getText());
highlightString.setSpan(new BackgroundColorSpan(ContextCompat.getColor(getActivity(), R.color.gray))
, 0, textView.getText().length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(highlightString);
copyToClipboard(urlToShare);
and the copy function:
public void copyToClipboard(String copyText) {
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("url", copyText);
clipboard.setPrimaryClip(clip);
Toast toast = Toast.makeText(getActivity(), "Link is copied", Toast.LENGTH_SHORT);
toast.show();
}
I hope it's helpful for someone who ends up on this question :)
I have found it doesn't work the first time I double click, but it works there after ( at least in android 11). This told me it needed to get focus. So, in the onCreate event, I first made the text view selectable, then I requested the focus to shift to the text view. Now I'm not saying the text view can lose focus and the first attempted selection will work. Not guaranteed. What is guaranteed is once it has focus, it'll work every time until it loses focus again. Don't forget about androids animations. So allow at least a half second for the non overridable animation to play out when the keyboard is hiding.
// In onCreate
TextView1.setTextIsSelectable( true );
// Allow animations to play out.
timer = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView1.requestFocus();
}
});
}
};
_timer.schedule(timer, (int)(1000));
}
Thanks a lot gilbot for your explanation. I just want to add another thing.
Selected text background color follows your app theme's colorAccent
For example check the image below
Here AppTheme is my application theme.
<item name="colorAccent">#color/cold</item>
and the colorAccent value will be the selected text background color.
Just use this simple library:
GitHub: Selectable TextView