I got a RecyclerView with multiple EditText-fields. When I try to edit one of the EditText-fields and click enter on the virtual keyboard, the focus shifts down to the next EditText-field, something I don't want to happen. I want to submit the changes I made in the first EditText-field and then close the keyboard. I managed to turn off this focus-shifting by adding the following to my .xml file:
android:focusable="true"
android:focusableInTouchMode="true"
But the problem still persists, now the changes just never get submitted as my listener never gets called. If I remove all items except from one in my RecyclerView everything works like I want. How can I make that happen with more items in myRecyclerView too?
My bind function inside my UserCardItem.kt file;
override fun bind(viewHolder: ViewHolder, position: Int) {
...
viewHolder.itemView.creditcard_nickname.setOnEditorActionListener{ _, actionId, _ ->
if(actionId == EditorInfo.IME_ACTION_DONE){
saveNickname(viewHolder)
true
} else {
false
}
}
private fun saveNickname(viewHolder : ViewHolder){
val nickname = viewHolder.itemView.creditcard_nickname.text.toString()
userCreditcard.nickname = nickname
UserCardStore().updateNickname(userCreditcard)
}
Add android:imeOptions="actionDone" to your EditText in your layout XML.
Related
i've a problem in my Android app.
I have a fragment that is generated as a list of editable textview. Every textview has a setOnFocusChangeListener that calls an API on server.
FocusChangedListener works correctly on last textview, and after that my textview remain focused as you can see in the figure below.
Problem
Now i have to change menu (fragment) by clicking on the right menu button.
When button is clicked and the new menu is about to be loaded, my textview loses focus and calls the API, but i don't want my app do that. If I click menu button is because i've to change it and i expect that my old fragment disappear or basically don't execute the FocusChangedListener.
Any ideas?
I made a little trick to solve the problem.
I just added this code inside OnCreate of my DrawerMenu and then i set a Global Variable "drawerOpened" to check every time if my Menu is open or not.
// Initialize the action bar drawer toggle instance
val drawerToggle:ActionBarDrawerToggle = object : ActionBarDrawerToggle(
this,
drawer_layout,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
){
override fun onDrawerClosed(view:View){
super.onDrawerClosed(view)
GlobalVar.drawerOpened = false
}
override fun onDrawerOpened(drawerView: View){
super.onDrawerOpened(drawerView)
GlobalVar.drawerOpened = true
currentFocus?.clearFocus() //clear focus - any view having focus
}
}
Then in my HomeFragment i did this:
// if GlobalVar.drawerOpened is false then setOnFocus
if (field.validatefield || field.nextpage != 0)
{
textView.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus && !GlobalVar.drawerOpened)
{
if ((field.mandatory && !textView.text.toString().isNullOrEmpty()) || (!field.mandatory)) {
if (field.validatefield) {
validateField(field.fieldcode, textView.text.toString(), field.nextpage)
} else {
_goToNextPageNo = field.nextpage
goToNextPageIfExist()
}
}
}
}
}
This is not a complete solution, but it works fine.
I assume that after a click, the application switches to touch mode and your EditText loses focus.
It happens at the system level.
A focus can have only one view at a time.
https://android-developers.googleblog.com/2008/12/touch-mode.html
I'm trying to play around with some Kotlin and Anko (more familiar with iOS) and taking from their example, there is this code:
internal open class TextListWithCheckboxItem(val text: String = "") : ListItem {
protected inline fun createTextView(ui: AnkoContext<ListItemAdapter>, init: TextView.() -> Unit) = ui.apply {
textView {
id = android.R.id.text1
text = "Text list item" // default text (for the preview)
isClickable = true
setOnClickListener {
Log.d("test", "message")
}
init()
}
checkBox {
id = View.generateViewId()
setOnClickListener {
Log.d("hi", "bye")
}
init()
}
}.view
My row appears how I want with a checkbox and textview. But I want to bind an action to the row selection not the checkbox selection. Putting a log message in both, I see that I get a log message when the row is selected which flips the checkbox. It does not, however, log my "test:message" from the textView click handler. Is there a way to get around this?
Apparently your issue has been addressed here. As the checkbox is consuming all the focus of ListItem you should set the CheckBox's focusable flag to false:
checkBox {
focusable = View.NOT_FOCUSABLE
}
Unfortunately setFocusable call requires at least API 26, but you could define view .xml and inflate the view manually as described here:
<CheckBox
...
android:focusable="false" />
Alternatively you could try setting a onTouchListener returning false which means the touch event will be passed to underlying views.
Let me know if it works ;)
I am trying to test an EditText that already contains some text using Espresso. The problem is that when I use typeText(), the cursor is placed at an arbitrary position within the text. I tried performing click() before using typeTextIntoFocusedView but the cursor is sometimes placed at the beginning of the EditText. I want to know is it possible to set the cursor at the end of the EditText before typing text into it?
A better way would be to use Espresso the way it's meant to be used: with actions on view matchers.
Example in Kotlin:
class SetEditTextSelectionAction(private val selection: Int) : ViewAction {
override fun getConstraints(): Matcher<View> {
return allOf(isDisplayed(), isAssignableFrom(EditText::class.java))
}
override fun getDescription(): String {
return "set selection to $selection"
}
override fun perform(uiController: UiController, view: View) {
(view as EditText).setSelection(selection)
}
}
Example usage:
onView(withId(R.id.my_text_view).perform(SetEditTextSelectionAction(selection))
An extra advantage over manually doing findViewById() is that you can combine this with matchers like withSubString("my text") if you don't have the ID of the view.
By the way: to change this into setting selection at the end of text you can simply remove the selection: Int constructor argument and change setSelection(selection) to setSelection(view.text.lastIndex).
The only way I have found to do this is to get a reference to the EditText itself and use EditText#setSelection(). For example, to move the cursor to the end of the current text:
val activity = activityRule.activity
val tv = activity.findViewById<EditText>(R.id.edittext)
activity.runOnUiThread { tv.setSelection(tv.text.length) }
I've had success by inserting the KeyCodes for "Home" and "End". These work just like on your desktop keyboard, by moving the cursor to either the beginning or end of the EditText. For example:
onView(withId(R.id.myView))
.perform(pressKey(KeyEvent.KEYCODE_MOVE_HOME))
To move to the end, you can use KeyEvent.KEYCODE_MOVE_END, and you can move left or right using KeyEvent.KEYCODE_DPAD_LEFT and KeyEvent.KEYCODE_DPAD_RIGHT.
I wanted to post my answer since I just had this problem and none of the other answers solved my problem.
I used a GeneralClickAction to click on the right side of the edit text which put the cursor at the end of the EditText where I wanted it. After that I used the TypeTextAction and disabled the tapToFocus behavior by passing in false to the constructor:
onView(withId(R.id.edit_text))
.perform(
new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0, null),
new TypeTextAction(text, false)
);
I have a listener that listens and makes focused items little bit bigger with animation.
private fun focus() {
itemView?.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
val anim : Animation = AnimationUtils.loadAnimation(itemView.context, R.anim.scale_in)
itemView.startAnimation(anim)
anim.fillAfter = true
} else {
val anim : Animation = AnimationUtils.loadAnimation(itemView.context, R.anim.scale_out)
itemView.startAnimation(anim)
anim.fillAfter = true
}
}
}
Besides this listener I also made custom function, that when focused item is clicked, it actually changes size back to normal
fun customFunction(): Unit = with(itemView) {
val anim : Animation = AnimationUtils.loadAnimation(itemView.context, R.anim.scale_out)
itemView.startAnimation(anim)
anim.fillAfter = true
}
PROBLEM: focus() and customFunction() functions work alright. Problem is, when I hit enter on focused element (customFunction() triggers) and element changes it size to normal - which is okay. But the moment I navigate to other element, the previous one scales out twice. How do I need to modify my onFocusListener to know that I shouldn't scale out twice if I've triggered customFunction() by clicking some item. Any idea is welcomed.
if i understand right your question you may have to try : itemView. setOnFocusChangeListener(null); in your customFunction()
I am working on Android Smart TV application:
In a view there is a custom keyboard and an EditText.
When application launches focus goes to the keyboard.
Desired:
When the user types with keyboard (clicking with a remote) the cursor should also blink inside the editText.
How can I show this effect inside the EditText?
This happens if you set a background for the field. If you want to solve this, set the cursorDrawable to #null.
You should add textCursorDrawable with cursorVisible.
Reference to a drawable that will be drawn under the insertion cursor.
android:cursorVisible="true"
android:textCursorDrawable="#null"
You could try something like this:
editText.setText(text);
editText.setPressed(true);
editText.setSelection(editText.getText().length()); // moves the cursor to the end of the text
However, there are 2 problems with this approach:
The cursor will not blink. The logic for the blinking is in the Editor class and cannot be overridden. It requires that the EditText is focused, and only 1 View can be focused at once within a Window - in your case that will be one of the keyboard buttons.
/**
* #return True when the TextView isFocused and has a valid zero-length selection (cursor).
*/
private boolean shouldBlink() {
if (!isCursorVisible() || !mTextView.isFocused()) return false;
...
}
The cursor will not always be visible. The blinking of the cursor is based on the System time - it is visible for half a second, and hidden for the next half a second. The cursor will only be visible if the code I suggested above is called at a point in time when the cursor would be visible according to the System time.
This is why the native keyboard/IME works the way it does. It is a separate Window that allows the EditText to maintain focus and have the blinking cursor functionality, while the user is tapping on Views in a different Window (the keyboard/IME).
That being said, there is a workaround for the problems above - make sure to set shouldBlink to false when you no longer need it though, it's a guaranteed memory leak or crash otherwise:
private void blink() {
if (shouldBlink) {
editText.setText(editText.getText());
editText.setPressed(true);
editText.setSelection(editText.getText().length());
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (shouldBlink) {
blink();
}
}
}, 500);
}
}
You can do this..I hope/think that u have a layout for the buttons u have created, by this u can set a Focus Listener for that layout and inside the onFocusChange method you can check if(layout.hasFocus()) and do this...
For example if your editText is named as et, u can set this to it:
et.setActivated(true);
et.setPressed(true);
I have a small example code for you having two edit text
et2.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(et2.hasFocus()){
//et1.setCursorVisible(true);
et1.setActivated(true);
et1.setPressed(true);
}
}
});
In your layout xml file add the following line in your edit text:
<requestFocus/>
This will place the cursor in your editText widget.
Hope it helps.
simply add
editText.requestFocus();
There is a couple of ways doing it:
1) XML
android:cursorVisible="true"
2) Java
mEditText.setOnClickListener(editTextClickListener);
OnClickListener editTextClickListener = new OnClickListener() {
public void onClick(View v) {
if (v.getId() == mEditText.getId()) {
mEditText.setCursorVisible(true);
}
}
};
or
if (mEditText.hasFocus()){
mEditText.setCursorVisible(true);
}
I know this is necro, but this was much better than the solutions above. Just extend EditText and add:
#Override
public boolean isCursorVisible() {
return true;
}
#Override
public boolean isFocused() {
return true;
}
And in your XML:
<com.foo.MyEditText
...
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:cursorVisible="true"/>
Now the EditText thinks it is focused and the cursor is visible, but it actually can't be focused.
private void setFocusCursor(){
mBinding.replyConversationsFooter.footerEditText.setFocusable(true);
`mBinding.replyConversationsFooter.footerEditText.setFocusableInTouchMode(true);`
`mBinding.replyConversationsFooter.footerEditText.requestFocus();`
}
Just call this function in oncreateView() and there you go.
We can only set one and only focus on a window.So doing this will help you solve your problem.
You can use the following code in your Activity:
//Get the EditText using
EditText et = (EditText)findViewById(R.id.editText);
//Set setCursorVisible to true
et.setCursorVisible(true);
You can explicitly put caret to last position in text:
int pos = editText.getText().length();
editText.setSelection(pos);
This will always focus on first character on edittext.
android:focusable="true"
Tested on API 27, 28 emulator.
Remove a background attribute, add focusable:
<EditText
...
android:focusable="true"
android:focusableInTouchMode="true"
/>
In code write: edit.requestFocus(); Though an underline will be visible.
In order to remove an underline, see https://stackoverflow.com/a/52052087/2914140:
edit.getBackground().mutate().setColorFilter(ContextCompat.getColor(getContext(), R.color.white), PorterDuff.Mode.SRC_ATOP);
To change a color of the cursor see https://stackoverflow.com/a/49462015/2914140:
add android:textCursorDrawable="#drawable/shape_cursor", while shape_cursor is:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="1dp"
android:height="25dp"
/>
<solid android:color="#color/gray" />
</shape>
It works on API 27, 28 emulator, but on a real device (API 21) cursor disappears. I tried many variants from here and there, but nothing helped.
Then I noticed that when EditText contains at least one symbol, it shows cursor. I added a TextWatcher to add a space when nothing entered.
private lateinit var someText: String
...
edit.requestFocus()
edit.setText(" ")
edit.addTextChangedListener(YourTextWatcher())
private inner class YourTextWatcher : TextWatcher {
override fun afterTextChanged(s: Editable?) {
someText = s.toString().trim()
if (someText.isEmpty()) {
// To not fall into infinite loop.
if (s?.length != 1) {
edit.setText(" ")
}
} else {
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
}
Also you can add paddings in order to tap inside EditText, if it is small.
I did like this:
var msgEditText = dialog.findViewById(R.id.msg1textView) as EditText
msgEditText.isActivated = true
msgEditText.isPressed = true
msgEditText.requestFocus()
msgEditText.setSelection(view.getText().length)