I have an EditText. Now I want to get all changes made by the user to this EditText and work with them before manually inserting them into the EditText. I don't want the user to directly change the text in the EditText. This should only be done by my code (e.g. by using replace() or setText()).
I searched a bit and found an interesting class named InputConnectionWrapper. According to the javadoc it shall act as a proxy for a given InputConnection. So I subclassed it like this:
private class EditTextInputConnection extends InputConnectionWrapper {
public EditTextInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
#Override
public boolean commitText(CharSequence text, int newCursorPosition) {
// some code which takes the input and manipulates it and calls editText.getText().replace() afterwards
return true;
}
}
To initialize the wrapper I overwrote the following method in my EditText-subclass:
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection con = super.onCreateInputConnection(outAttrs);
EditTextInputConnection connectionWrapper = new EditTextInputConnection(con, true);
return connectionWrapper;
}
However, commitText() never gets called. The onCreateInputConnection() gets called and the constructor of EditTextInputConnection also, but never commitText(), altough it should be, when I enter some text into the field. At least, that's how I understand the usage of InputConnectionWrapper. Or am I wrong?
Edit: It seems, that commitText() is only called for special characters like "."," " etc. As I understand the Android sourcecode for all other characters InputConnectionWrapper.sendKeyEvent() should be called, but that's not the case... I'm absolutely stuck at this point. I already tried EditText.onKeyPreIme(), but this only works on hardware keyboards. So that's no alternative... I don't really understand, why Android handles soft keyboards that different from hardware keyboards.
EditText.onTextChanged() gets also fired on non-user input, so this is also not, what I'm looking for.
It turned out, that the above usage of the InputConnectionWrapper was totally correct. However, commitText() gets never called (except for special cases), as there are other methods, which are used during typing. These are mainly setComposingText() and sendKeyEvent(). However, it is also important to overwrite seldom used methods like deleteSurroundingText() or commitText() to make sure to catch every user input.
Blundell suggested on the chat that you use a TextWatcher. Check if this helps you out.
Use a TextWatcher, disconnect it when you're modifying your edittext and reconnect it when done. This way, you won't trigger infinite calls.
Related
I'm using a custom dynamic contentDescription for my textview, so it has been implemented in my java class and not in my xml file.
private void enableButton(TextView textView, boolean enabled) {
if (textView == null) {
return;
}
if (!enabled) {
textView.setContentDescription(textView.getResources().getString(R.string.install_disabled));
} else {
textView.setContentDescription(textView.getResources().getString(R.string.install));
}
textView.setEnabled(enabled);
}
After I'm enabling my textview to be clickable, and when talkback is enabled, focusing on my textview is announcing the state of my textview which is "disabled". Is there a way to not announce that state?
I do not want to set the accessibility to be not important because I still want my dynamic contentDescription to be recited when talkback users focus on the textview.
Suggestion:
I believe the culprit is the "setEnabled" method that is somehow triggering and announcing the state of the textview, but I'm still not able to stop it from reciting that last.
My first answer is: LEAVE IT ALONE! The "disabled" announcement tells a TalkBack user that there is a user interface control there, that under some circumstances can be interacted with, but is not currently active. Given your description, this is exactly what you have. To remove the announcement is actually going to make things WORSE from an accessibility perspective, the explanations for why this is the case are covered in WCAG 1.3.1.
Definitions:
Button = android.widget.Button
button = a user interface component that does something when you click it.
Text = a user interface component that conveys information, but is not active
Long story short, the fact that the control is ever in a state that it can be active and "not disabled" is significant information on its own, and SHOULD be shared with the user. ESPECIALLY since you're using a "TextView" to implement this. This isn't a super uncommon practice, but one of the ways TalkBack calculates roles (button, link, image, text, etc) is by looking at the Class/Type of object. So, when it sees a TextView, it is going to assume Text, unless you inform it otherwise. Now, since you have added click listeners to your TextView (or Gesture Recognizers, or whatever) TalkBack may be able to figure out that the thing you're dealing with is actually a "button", and it may not. REGARDLESS, the fact that this "button" (lower case B!) is not active is important state to share with the user, and communicates to them the fact that they can somehow enable it and come back and interact with it later. This is immensely important information! Imagine if every button/link on a WebPage looked exactly like plane text? How would you know what to interact with?
Now, I will show you the different pieces of this puzzle, as information, but I really do encourage you to leave the announcement alone. This is coming from someone who routinely speaks at Accessibility conferences on Native Android development, PLEASE LEAVE THIS ANNOUNCEMENT IN. To not do so shows a misunderstanding of how users with sight impairments want to perceive controls within your application, and the information that is important to them.
The setEnabled() function on a TextView corresponds directly with the isEnabled() property of AccessibilityNodeInfo.
In order to implement the behavior you want, you want the AccessibilityNodeInfo representation of your TextView to be different from that of the actual representation of your TextView. In order to do this you can use AccessibilityDelegate, I'm actually not sure which callback you want to use. In one of these the node is likely to be "sealed" and in one of them it might not be sealed yet. You obviously want the one where the node is not yet sealed! Regardless the general approach is:
textView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
#Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
// Let the default implementation populate the info.
super.onInitializeAccessibilityNodeInfo(host, info);
// Override this particular property
info.setEnabled(whateverYouThinkItShouldBe);
}
});
Use setClickable(false) to replace setEnabled(false) will solve this problem.
Using two-way Android Databinding, is it possible to have a generic inverse boolean converter? For example, I would like to do something like this:
<Switch android:checked="#={!viewModel.myBoolean}" />
When I run this in Android, the switch just rapidly fires back and forth. I tried to create a two way binding app:inverseChecked following some examples from George Mount, but I was not successful (just kept getting error stating cannot find event 'inverseCheckedAttrChanged' on View type 'android.widget.Switch').
As a comparison, using Aurelia this just works as you would expect for two way binding. In WPF, probably the first converter you make is some sort of InverseBooleanConverter to easily tackle these sorts of things. So, am assuming I am just missing something obvious here.
I actually didn't expect it to work at all. I assume, it's switching back and forth all the time, because the bindings don't apply inverse function of your binding expression.
That said, I tested the behavior with the current data binding library version and checked the generated sources. With the simple example of android:checked these show notes how the inverse should look like and apply it appropriately.
Also George Mount wrote a Blog post about it a short while ago: https://medium.com/google-developers/android-data-binding-inverse-functions-95aab4b11873
If you try to implement an app:inverseChecked, you'd also have to implement a #BindingAdapter("inverseChecked") as setter, #InverseBindingAdapter(attribute="inverseChecked") as getter and #BindingAdapter("inverseCheckedAttrChanged") for setting up the change listener.
The latter could look like the following:
#BindingAdapter("inverseCheckedAttrChanged")
public static void setupInverseCheckedAttrChanged(Switch view, InverseBindingListener listener) {
OnCheckedChangeListener newListener = null;
if (listener != null) {
newListener = (v,b) -> listener.onChange();
}
view.setOnCheckedChangeListener(newListener);
}
I am trying to change the text announced by TalkBack when an ImageView is focused through accessibility.
The Android documentation states that we should create an AccessibilityDelegate, and override onPopulateAccessibilityEvent (I am using the support library because I am also supporting GingerBread)
Thus, my code is the following:
public static void setImageDelegate(View view) {
AccessibilityDelegateCompat delegate = new AccessibilityDelegateCompat() {
#Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
event.getText().add(event.getContentDescription() + ", image");
}
};
ViewCompat.setAccessibilityDelegate(view, delegate);
}
When I call this function on my imageview, the delegate gets set, but the modified text is not being read. It simply reads the original content description. Am I doing something wrong or missing something about the accessibility functions?
Stepping through the code, it seems to be adding the correct text, but still, no change in spoken text.
Note: the above is a contrived example, content description could be used, but I'm trying to figure out why it doesn't work before I try it on custom views.
In ICS and above, TalkBack doesn't use the accessibility event text in most cases. Instead, it checks the text and content description of the AccessibilityNodeInfo exposed by the view. You would need to override onInitializeAccessibilityNodeInfo.
In most cases, though, you would just want to call View.setContentDescription.
In this particular case, you shouldn't set anything since TalkBack handles speaking control types and capabilities. We strongly advise developers against adding descriptions like "button" or "image."
I would like to ask if someone could give me a simple explanation of the KeyEvent.ACTION_MULTIPLE in Android and an example when it is triggered.
Here -> http://developer.android.com/reference/android/view/KeyEvent.html#ACTION_MULTIPLE it says that:
When interacting with an IME, the framework may deliver key events with the special action ACTION_MULTIPLE that either specifies that single repeated key code or a sequence of characters to insert.
What does it mean reapeted key code? That a key is pressed and held down? Sorry but it is not really clear to me cause I am not English and I am new in the Android developing.
Thanks for the attention!
EDIT:
So the event is triggered only when an arrow key of the keyboard is pressed and held down? As the user whose answer was accepted says here -> What triggers (or generates) KeyEvent.ACTION_MULTIPLE?, is it correct?
Your question made me curious :)..so I tried this code,and I was able to repeat this with a few keys.eg.
Backpress:when you press this key continuously,the IME starts deleting one word a ta time instead of one letter.similarly,it is possible determine multiple presses of keys which support such action.
This again depends on the IME.This would also be useful majorly from an IME app's Point.You cannot often replicate it is because long press usually triggers a different character.
Another point would be that KeyCodes are also inputted from the presence of a hardware keyboard.So this can come up and depends how you handle it.
While the http://developer.android.com/reference/android/view/View.OnKeyListener.html says that this action is triggerred only hardware keyboards,it worked with my software keyboard too.ALTHOUGH they are not expected to do so.I guess you can say that this would into picture when you use a hardware keyboard
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText et = (EditText) findViewById(R.id.checkText);
KeyListener listener = et.getKeyListener();
Log.d("tag", listener.toString());
et.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.d("tag", event.getAction()+"event");
return true;
}
});
}
}
In android how do I create a required field validation? like on a page where use can enter some data into some EditText. I wanted to make sure user enter something before continuing, like if the user forgot to enter some data, the app would not crash. I have done other validation like number only using those input-type provided. but so far I research I only found ways to validate content entered but not whether there is something entered.
So I should put something in the onCreate method like
if(EditText text is !=null){ do the stuff} else {error message}
But if I did that, the moment the app is run there will be error displayed.
And how do I write the "text of EditText" in c# I believe is TextBox Text. But I do not know how to do that in java android. I know setText but do not know how to refer to the content without changing it.
To get text user entered in the EditText you can call getText() method. I recommend you to perform validation after user clicks some button. Validating content of EditText inside onCreate() method is useless.
It's always better to tell the user that they need to put the correct information the earliest possible. Why? Because it allows the user to quickly identify the problem and fix it. That being said, I would recommend checking if its null or whatever you want the textfield to contain as soon as it looses focus.
textView.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus)
//We get the text and then convert it to a string and check
if(v.getText().toString() == ""){
//Do what you want in this area
}
}
});
String str = textview.getText().toString().trim()