Editar texto de varias líneas con botón de acción Listo

114

¿Es posible tener un EditTextwidget con android:inputType="textMultiLine"set, yandroid:imeOptions="actionDone" al mismo tiempo?

Me gustaría un cuadro de edición de varias líneas, con el botón de acción en el teclado para estar Listo, no Enter (Retorno de carro), pero no parece estar funcionando.

Gracias por adelantado

kefs
fuente
¿Qué hay de probar un widget compuesto con EditText de varias líneas y un Button combinados?
Subin Sebastian

Respuestas:

194

Utilizar

editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
editText.setRawInputType(InputType.TYPE_CLASS_TEXT);

y en XML:

android:inputType="textMultiLine"
alexbtr
fuente
para mí, el cursor todavía se mostraba cuando el teclado bajaba, necesitaba agregar textField.setCursorVisible(false);dentro de unonEditorActionListener
Fonix
Funciona en 4.2.2 y 7.1.1. Gracias.
tehcpu
1
Funciona muy bien ... Luché por encontrarlo. Esta respuesta debe marcarse como la mejor respuesta
MFAL
7
¿Por qué funciona esto en lugar de configurarlo todo en xml?
Andrew Steinmetz
1
Luché un poco para encontrar esta información. ¡Todavía está funcionando como un encanto!
Costin
51

De la documentación de Android: ' "textMultiLine" Teclado de texto normal que permite a los usuarios ingresar largas cadenas de texto que incluyen saltos de línea (retornos de carro) .Por lo tanto, el atributo textMultiLine no es apropiado si desea tener el botón 'Listo' en el teclado.

Una forma sencilla de obtener un campo de entrada de varias líneas (en este caso 3 líneas) con el botón hecho es usar EditText con

android:lines="3" 
android:scrollHorizontally="false" 

Sin embargo, por alguna razón, esto solo funciona para mí si hago estas configuraciones en el código en lugar del archivo de diseño (en onCreate) por

TextView tv = (TextView)findViewById(R.id.editText);
if (tv != null) {
    tv.setHorizontallyScrolling(false);
    tv.setLines(3);
}

Espero que esto ayude a alguien, ya que tomó bastante tiempo averiguarlo. Si encuentra una manera de hacerlo funcionar desde el manifiesto, háganoslo saber.

HYS
fuente
4
También sugeriría intentarlo en maxLines()lugar de setLines()si desea evitar cambiar la altura delEditText
Daniel Smith
Creo que en la pregunta no se aclara que debes configurar android: imeOptions con un valor como actionSend. Completando esta respuesta, tuve que configurar android: singleLine = "true" incluso si el setMaxLines lo sobrescribe con el código (no hacerlo no le dará una tecla enter con, por ejemplo, "Enviar"). Para capturar la acción <Enter>, verifique la primera respuesta de stackoverflow.com/questions/5014219/… out.
DNax
Lo siento, la mejor alternativa que encontré para capturar el <Enter> es la respuesta de @earlcasper aquí stackoverflow.com/questions/1489852/…
DNax
1
Esto funcionó perfectamente para mí (usando setMaxLines(3)) ¡Muchas gracias!
laurencevs
1
Funcionó como un encanto: solo agregue android: imeOptions = "actionDone" android: inputType = "text" a su XML y elimine android: lines = "3" android: scrollHorizontally = "false" de XML
HannahCarney
24

¡Ejemplo de trabajo! Cree la clase EditText personalizada a continuación que admita esta función y use la clase en el archivo xml. Código de trabajo:

package com.example;

import android.content.Context;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;

public class ActionEditText extends EditText
{
   public ActionEditText(Context context)
   {
       super(context);
   }

   public ActionEditText(Context context, AttributeSet attrs)
   {
       super(context, attrs);
   }

   public ActionEditText(Context context, AttributeSet attrs, int defStyle)
   {
       super(context, attrs, defStyle);
   }

   @Override
   public InputConnection onCreateInputConnection(EditorInfo outAttrs)
   {
       InputConnection conn = super.onCreateInputConnection(outAttrs);
       outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
       return conn;
   }
}

<com.example.ActionEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:imeOptions="actionDone"
       android:inputType="textAutoCorrect|textCapSentences|textMultiLine" />
Anees U
fuente
Esto funcionó para mí, mi requisito era hacer un texto de edición que debería ser de varias líneas y también debería tener el siguiente botón en las teclas de función ............... así que muchas gracias por lo que hice estaba en editar texto agregué 2 líneas android: inputType = "textMultiLine" android: imeOptions = "actionNext" Y en la clase personalizada anterior lo hice: InputConnection conn = super.onCreateInputConnection (outAttrs); //outAttrs.imeOptions & = ~ EditorInfo.IME_FLAG_NO_ENTER_ACTION; outAttrs.imeOptions & = EditorInfo.IME_ACTION_NEXT; return conn;
yash
Trabajos. Probado en Andorid 6.0.1.
AmiguelS
9

Para hacer esto en Kotlin (y también opcionalmente aplicar otras configuraciones como textCapSentencespuedes usar esta función de extensión:

// To use this, do NOT set inputType on the EditText in the layout
fun EditText.setMultiLineCapSentencesAndDoneAction() {
    imeOptions = EditorInfo.IME_ACTION_DONE
    setRawInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or InputType.TYPE_TEXT_FLAG_MULTI_LINE)
}

Uso:

myEditText.setMultiLineCapSentencesAndDoneAction()
Ollie C
fuente
1
¡Funciona a partir de junio de 2019! Gracias :)
Rohan Taneja
La clave para mí fue usar en setRawInputTypelugar desetInputType
Tamoxin
9

Creo que esta es la forma de hacer las cosas. Tener android:inputType="textMultiLine", android:imeOptions="actionDone"hace que la funcionalidad de la tecla Enter sea ambigua. Solo tenga en cuenta que puede usar android:lines="10"y tal vez eliminar android:inputType="textMultiLine", pero depende de lo que quiera lograr, a veces solo necesita android:inputType="textMultiLine"y no hay reemplazo para él.

EditText ed=new EditText(this);
ed.setOnKeyListener(new OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if(keyCode == KeyEvent.KEYCODE_ENTER){
                //do your stuff here
            }
            return false;
        }
});
Lukap
fuente
El uso de una cantidad fija de números máximos con android: lines = "10" funcionó para mí, obteniendo el botón ok en el teclado. Buen truco si uno sabe cuántas líneas habrá, por ejemplo, con un conjunto pequeño de maxLength.
sunadorer
Quiero deshabilitar el código de clave # 66, ¿es posible? ¿Cómo puedo hacer eso?
Adil Malik
1
¿Por qué keyCode == 66 en lugar de keyCode == EditorInfo.IME_ACTION_GO?
tse
5

Esto parece funcionarme perfectamente

int lineNum = 2;
mEditText.setHorizontallyScrolling(false);
mEditText.setLines(3);
yonez
fuente
4

Solución Kotlin reutilizable

Establecer estos valores en el código fue lo único que funcionó para mí

edittext.inputType = EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
edittext.setHorizontallyScrolling(false)
edittext.maxLines = Integer.MAX_VALUE // Or your preferred fixed value

Necesito esto con frecuencia, así que hice esto para mantener el código limpio:

fun EditText.multilineIme(action: Int) {
    imeOptions = action
    inputType = EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
    setHorizontallyScrolling(false)
    maxLines = Integer.MAX_VALUE
}

// Then just call
edittext.multilineIme(EditorInfo.IME_ACTION_DONE)

Si desea agregar una acción personalizada opcional en 'Listo', intente esto:

fun EditText.multilineDone(callback: (() -> Unit)? = null) {
    val action = EditorInfo.IME_ACTION_DONE
    multilineIme(action)
    setOnEditorActionListener { _, actionId, _ ->
            if (action == actionId) {
                callback?.invoke()
                true
            }
            false
        }
    }
}

// Then you can call
edittext.multilineDone { closeKeyboard() }

// or just
edittext.multilineDone()

¿Necesita controlar fácilmente el teclado en la devolución de llamada? Leer esta publicación

Luego agrega hideKeyboard()llamarEditText.multilineDone

Gibolt
fuente
1
☝️Esta es una gran respuesta
Michael
3

Respuesta corta: No, creo que no es posible antes del nivel de API 11 (3.0).

El mismo problema surgió aquí (discutido en los comentarios a la respuesta aceptada):

Botón de acción del teclado suave de Android

Del comentario final:

Mirando algunas aplicaciones en mi teléfono, parece común tener el cuadro de varias líneas al final, con un botón visible "Listo" o "Enviar" debajo (por ejemplo, aplicación de correo electrónico).

Martin Stone
fuente
3

Una forma sencilla de solucionar esta situación:

  • mantenga estos atributos en EditText:

    android:inputType="textMultiLine" 
    android:scrollHorizontally="false"
  • luego agregue este código para ocultar el teclado solo cuando se presiona ENTER:

    editText.setOnEditorActionListener(new OnEditorActionListener() 
    {
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) 
        {
            editText.setSelection(0);
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);      
            return true;
         } 
         else 
         {
            return false;
         }
         }
    });
Jean Gonçalves
fuente
2

Si no se trata del aspecto del teclado en pantalla, simplemente puede poner un oyente de entrada en el teclado y activar el estado "hecho" si el usuario ingresa una nueva línea.

twall
fuente
2

si usa la opción de entrada textImeMultiline con imeoptions flagnext y actionnext, obtendrá un botón siguiente en lugar del retorno de cariage

Marcusdev
fuente
2

Si bien ninguna de las otras soluciones funcionó para mí, lo siguiente funcionó perfección y me ahorró días y días de más búsquedas en Google, con algunos giros propios, por supuesto. Desafortunadamente, no recuerdo de dónde obtuve el código exactamente y, por lo tanto, no puedo darle al autor el crédito que se merece.

En su código Java:

////////////Code to Hide SoftKeyboard on Enter (DONE) Press///////////////
editText.setRawInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD|InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
editText.setImeActionLabel("DONE",EditorInfo.IME_ACTION_DONE);              //Set Return Carriage as "DONE"
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);

editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) 
    {
                if (event == null) {
                    if (actionId == EditorInfo.IME_ACTION_DONE) {
                        // Capture soft enters in a singleLine EditText that is the last EditText
                        // This one is useful for the new list case, when there are no existing ListItems
                        editText.clearFocus();
                        InputMethodManager inputMethodManager = (InputMethodManager)  getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
                        inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0);
                    }

                    else if (actionId == EditorInfo.IME_ACTION_NEXT) {
                        // Capture soft enters in other singleLine EditTexts
                    } else if (actionId == EditorInfo.IME_ACTION_GO) {
                    } else {
                        // Let the system handle all other null KeyEvents
                        return false;
                    }
                } 
        else if (actionId == EditorInfo.IME_NULL) {
                    // Capture most soft enters in multi-line EditTexts and all hard enters;
                    // They supply a zero actionId and a valid keyEvent rather than
                    // a non-zero actionId and a null event like the previous cases.
                    if (event.getAction() == KeyEvent.ACTION_DOWN) {
                        // We capture the event when the key is first pressed.
                    } else {
                        // We consume the event when the key is released.
                        return true;
                    }
                } 
        else {
                    // We let the system handle it when the listener is triggered by something that
                    // wasn't an enter.
                    return false;
                }
                return true;
        }
});
NP Kaushik
fuente
1

Estoy en 4.xy intenté llamar a setHorizontallyScrolling () (con o sin setLine () o setMaxLines ()), así como muchas configuraciones XML diferentes para que se muestre el botón Listo. Ninguno de ellos funcionó. La conclusión es que si tu EditText es de varias líneas, Android siempre querrá mostrar el retorno de carro en lugar del botón "Listo", a menos que pongas algún truco alrededor de esto.

La solución de menor complicación que encontré que no implica reasignar el comportamiento del retorno de carro está aquí: https://stackoverflow.com/a/12570003/3268329 . Esta solución anulará el implacable deseo de Android de forzar la configuración del indicador IME_FLAG_NO_ENTER_ACTION para vistas de varias líneas, lo que hace que el botón Listo desaparezca.

Steve B
fuente
0

Yo también luché durante bastante tiempo, ¡pero finalmente encontré una solución!

Simplemente cree una clase EditText personalizada como tal:

public class EditTextImeMultiline extends EditText {

    public void init() {
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                for (int i = s.length(); i > 0; i--)
                    if (s.subSequence(i - 1, i).toString().equals("\n"))
                        s.replace(i - 1, i, "");
            }
        });
        setSingleLine();
        setHorizontallyScrolling(false);
        this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                EditTextImeMultiline.this.setLines(EditTextImeMultiline.this.getLineCount());
            }
        });
    }

    public EditTextImeMultiline(Context context) {
        super(context);
        init();
    }

    public EditTextImeMultiline(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public EditTextImeMultiline(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public EditTextImeMultiline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }
}

Esta clase elimina lineBreaks (\ n), envuelve el texto como lo haría textMultiline, Y le permite reemplazar el botón Enter por un ImeAction;).

Solo necesita llamarlo en su XML en lugar de la clásica clase EditText.

Para explicar la lógica aquí:

  • Configure EditText como singleLine para poder mostrar un botón ImeAction en lugar de Enter.
  • Elimine el desplazamiento horizontal para que el texto pase a la siguiente línea cuando llegue al final de la vista.
  • Observe los cambios de diseño con el onGlobalLayoutListener y establezca su parámetro "line" en el "lineCount" del texto actual contenido en editText. Esto es lo que refresca su altura.
Gabriel Morin
fuente