Modifier le texte multiligne avec le bouton d'action Terminé

114

Est-il possible d'avoir un EditTextwidget avec android:inputType="textMultiLine"set, et android:imeOptions="actionDone"en même temps?

Je voudrais une zone d'édition multiligne, avec le bouton d'action sur le clavier pour être terminé, pas Entrée (retour chariot), mais cela ne semble pas fonctionner.

Merci d'avance

kefs
la source
Pourquoi ne pas essayer un widget composé avec EditText multiligne et un bouton combinés?
Subin Sebastian le

Réponses:

195

Utilisation

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

et en XML:

android:inputType="textMultiLine"
alexbtr
la source
pour moi, le curseur s'afficherait toujours lorsque le clavier descendait, je devais ajouter à l' textField.setCursorVisible(false);intérieur d'unonEditorActionListener
Fonix
Fonctionne sur 4.2.2 et 7.1.1. Merci.
tehcpu
1
Fonctionne très bien ... J'ai eu du mal à le trouver. Cette réponse doit être marquée comme meilleure réponse
MFAL
7
pourquoi cela fonctionne-t-il plutôt que de tout configurer en xml?
Andrew Steinmetz
1
J'ai eu du mal à trouver cette information. Cela fonctionne toujours comme un charme!
Costin le
51

De la documentation Android: ' "textMultiLine" Clavier de texte normal qui permet aux utilisateurs de saisir de longues chaînes de texte comprenant des sauts de ligne (retours chariot) . 'Par conséquent, l'attribut textMultiLine n'est pas approprié si vous voulez avoir le bouton' Terminé 'sur le clavier.

Un moyen simple d'obtenir un champ de saisie multiligne (dans ce cas 3 lignes) avec le bouton Terminé consiste à utiliser EditText avec

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

Cependant, pour une raison quelconque, cela ne fonctionne pour moi que si je fais ces paramètres dans le code au lieu du fichier de mise en page (dans onCreate) par

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

J'espère que cela aide quelqu'un, car il a fallu un certain temps pour le comprendre. Si vous trouvez un moyen de le faire fonctionner à partir du manifeste, veuillez nous en informer.

HYS
la source
4
Je suggérerais également d'essayer maxLines()au lieu de setLines()si vous voulez éviter de changer la hauteur duEditText
Daniel Smith
Je pense que dans la question, il n'est pas précisé que vous devez définir android: imeOptions avec une valeur telle que actionSend. Pour compléter cette réponse, j'ai dû définir android: singleLine = "true" même si setMaxLines l'écrase par code (ne pas le faire ne vous donnera pas une touche d'entrée avec par exemple "Envoyer"). Pour capturer l'action <Entrée>, vérifiez la première réponse de stackoverflow.com/questions/5014219/... out.
DNax
Désolé, la meilleure alternative que j'ai trouvée pour capturer le <Entrée> est la réponse @earlcasper ici stackoverflow.com/questions/1489852/…
DNax
1
Cela a parfaitement fonctionné pour moi (en utilisant setMaxLines(3)) Merci beaucoup!
laurencevs
1
A fonctionné comme un charme: ajoutez simplement android: imeOptions = "actionDone" android: inputType = "text" à votre XML et supprimez android: lines = "3" android: scrollHorizontally = "false" de XML
HannahCarney
24

Exemple de travail! Créez la classe EditText personnalisée ci-dessous qui prend en charge cette fonctionnalité et utilisez la classe dans le fichier xml. Code de travail:

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
la source
Cela a fonctionné pour moi, mon exigence était de créer un texte d'édition qui devrait être multi-lignes ainsi que le bouton suivant sur les touches programmables ............... alors merci beaucoup pour ce que j'ai fait était dans le texte d'édition j'ai ajouté 2 lignes android: inputType = "textMultiLine" android: imeOptions = "actionNext" Et dans la classe personnalisée ci-dessus j'ai fait: InputConnection conn = super.onCreateInputConnection (outAttrs); //outAttrs.imeOptions & = ~ EditorInfo.IME_FLAG_NO_ENTER_ACTION; outAttrs.imeOptions & = EditorInfo.IME_ACTION_NEXT; return conn;
yash
Travaux. Testé sur Andorid 6.0.1.
AmiguelS
9

Pour ce faire dans Kotlin (et également éventuellement appliquer d'autres configurations comme textCapSentencesvous pouvez utiliser cette fonction d'extension:

// 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)
}

Usage:

myEditText.setMultiLineCapSentencesAndDoneAction()
Ollie C
la source
1
Fonctionne à partir de juin 2019! Merci :)
Rohan Taneja
La clé pour moi était d'utiliser à la setRawInputTypeplace desetInputType
Tamoxin
9

Je pense que c'est la façon de faire votre chose. Avoir android:inputType="textMultiLine", android:imeOptions="actionDone"rend la fonctionnalité de clé d'entrée ambiguë. Gardez simplement à l'esprit que vous pouvez utiliser android:lines="10"et peut-être supprimer android:inputType="textMultiLine", mais cela dépend de ce que vous voulez réaliser, parfois vous avez juste besoin du android:inputType="textMultiLine"et il n'y a pas de remplacement.

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
la source
Utiliser un nombre fixe de nombres maximum avec android: lines = "10" a fonctionné pour moi, obtenir le bouton ok sur le clavier. Bon truc si on sait combien de lignes il y aura, par exemple avec un petit ensemble maxLength.
sunadorer
Je veux désactiver le keyCode # 66 est-ce possible? Comment puis je faire ça?
Adil Malik
1
pourquoi keyCode == 66 au lieu de keyCode == EditorInfo.IME_ACTION_GO?
tse
5

Cela semble fonctionner parfaitement pour moi

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

Solution Kotlin réutilisable

Définir ces valeurs dans le code était la seule chose qui fonctionnait pour moi

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

J'en ai besoin fréquemment, alors faites ceci pour garder le code propre:

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 vous souhaitez ajouter une action personnalisée facultative sur "Terminé", essayez ceci:

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()

Besoin de contrôler facilement le clavier en rappel? Lisez cet article

Ensuite, ajoutez un hideKeyboard()appelEditText.multilineDone

Gibolt
la source
1
☝️C'est une excellente réponse
Michael
3

Réponse courte: Non, je pense que ce n'est pas possible avant le niveau d'API 11 (3.0).

Le même problème a surgi ici (discuté dans les commentaires de la réponse acceptée):

Bouton d'action du clavier logiciel Android

Du commentaire final:

En regardant quelques applications sur mon téléphone, il semble courant d'avoir la boîte multiligne en dernier, avec un bouton "Terminé" ou "Envoyer" visible en dessous (par exemple, application de messagerie).

Martin Stone
la source
3

Une façon simple de contourner cette situation:

  • conservez ces attributs sur EditText:

    android:inputType="textMultiLine" 
    android:scrollHorizontally="false"
  • puis ajoutez ce code pour masquer uniquement le clavier lorsque vous appuyez sur ENTRÉE:

    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
la source
2

S'il ne s'agit pas de l'apparence du clavier à l'écran, vous pouvez simplement mettre un écouteur d'entrée sur le clavier et déclencher l'état «terminé» si l'utilisateur entre une nouvelle ligne.

twall
la source
2

si vous utilisez l'option d'entrée textImeMultiline avec imeoptions flagnext et actionnext, vous obtenez un bouton suivant au lieu du retour de cariage

Marcusdev
la source
2

Bien qu'aucune des autres solutions n'ait jamais fonctionné pour moi, ce qui suit a fonctionné à merveille et m'a sauvé des jours et des jours de recherche sur Google, avec quelques rebondissements bien sûr. Malheureusement, je ne me souviens pas d'où j'ai obtenu le code exactement et je ne peux donc pas donner à l'auteur le crédit qu'il mérite.

Dans votre code 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;
        }
});
PN de Kaushik
la source
1

Je suis sur 4.x et j'ai essayé d'appeler setHorizontallyScrolling () (avec ou sans setLine () ou setMaxLines ()), ainsi que de nombreuses configurations XML différentes pour afficher le bouton Done. Aucun d'eux n'a fonctionné. L'essentiel est que si votre EditText est multi-lignes, Android voudra toujours afficher le retour chariot au lieu du bouton "Terminé", à moins que vous n'ayez mis un hack autour de cela.

La solution la moins compliquée que j'ai trouvée qui n'implique pas de remapper le comportement du retour chariot est ici: https://stackoverflow.com/a/12570003/3268329 . Cette solution annulera le désir incessant d'Android de forcer le paramétrage de l'indicateur IME_FLAG_NO_ENTER_ACTION pour les vues multilignes, ce qui fera disparaître le bouton Terminé.

Steve B
la source
0

J'ai aussi eu du mal pendant un certain temps, mais j'ai finalement trouvé une solution!

Créez simplement une classe EditText personnalisée en tant que telle:

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();
    }
}

Cette classe supprime lineBreaks (\ n), encapsule le texte comme le ferait textMultiline, ET vous permet de remplacer le bouton Entrée par un ImeAction;).

Il vous suffit de l'appeler dans votre XML au lieu de la classe classique EditText.

Pour expliquer la logique ici:

  • Définissez EditText comme une seule ligne pour pouvoir afficher un bouton ImeAction au lieu de Enter.
  • Supprimez le défilement horizontal pour faire passer le texte à la ligne suivante lorsqu'il atteint la fin de la vue.
  • Regardez les changements de mise en page avec le onGlobalLayoutListener, et définissez son paramètre "line" sur le "lineCount" du texte actuel détenu par editText. C'est ce qui rafraîchit sa hauteur.
Gabriel Morin
la source