Home » Android » android – Multi-line EditText with Done action button

android – Multi-line EditText with Done action button

Posted by: admin March 11, 2020 Leave a comment

Questions:

Is it possible to have an EditText widget with android:inputType="textMultiLine" set, and android:imeOptions="actionDone" at the same time?

I’d like a multi-line edit box, with the action button on the keyboard to be Done, not Enter (Carriage Return), but it doesn’t seem to be working..

Thanks in advance

How to&Answers:

Use

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

and in XML:

android:inputType="textMultiLine"

Answer:

From the android documentation: ‘“textMultiLine”
Normal text keyboard that allow users to input long strings of text that include line breaks (carriage returns).
‘ Therefore the textMultiLine attribute is not appropriate if you want to have the ‘Done’ button in the keyboard.

A simple way to get a multi-line (in this case 3 lines) input field with the done button is to use EditText with

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

However, for some reason this only works for me if I do these settings in the code instead of the layout file (in onCreate) by

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

I hope this helps someone, as it took quite a while to figure out. If you find a way to make it work from the manifest, please let us know.

Answer:

Working Example!
Create the below custom EditText class that supports this feature and use the class in the xml file. Working code:

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" />

Answer:

I think this is the way to do you thing. Having android:inputType="textMultiLine", android:imeOptions="actionDone" makes enter key functionality ambiguous. Just keep in mind that you can use android:lines="10" and maybe remove android:inputType="textMultiLine", but depends what you want to achieve sometimes you just need the android:inputType="textMultiLine" and there is no replacement for it.

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

Answer:

To do this in Kotlin (and also optionally apply other configurations like textCapSentences you can use this extension function:

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

Answer:

This seems to work for me perfectly

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

Answer:

Short answer: No, I believe it’s not possible prior to API level 11 (3.0).

The same issue cropped up here (discussed in the comments to the accepted answer):

Android Soft keyboard action button

From the final comment:

Looking at a few apps on my phone, it seems common to have the multiline box last, with a visible “Done” or “Send” button below it (e.g. Email app).

Answer:

A simple way to work around this situation:

  • keep this attributes on the EditText:

    android:inputType="textMultiLine" 
    android:scrollHorizontally="false"
    
  • then add this code to only hide the keyboard when ENTER is pressed:

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

Answer:

If it is not about the look of the on-screen keyboard, you could simply put a input listener on the keyboard and fire the “done”-status if the user inputs a newline.

Answer:

if you use the input option textImeMultiline with imeoptions flagnext and actionnext you get a next button instead of the cariage return

Answer:

Reuseable Kotlin Solution

Setting these values in code was the only thing that worked for me

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

I require this frequently, so made this to keep the code clean:

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

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

If you want to be add an optional custom action on ‘Done’, try this:

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

Need to easily control the keyboard in callback? Read this post

Then add hideKeyboard() call in EditText.multilineDone

Answer:

I’m on 4.x and tried calling setHorizontallyScrolling() (with or without setLine() or setMaxLines()), as well as many different XML configurations to get the Done button to show. None of them worked. The bottom line is that if your EditText is multi-line, Android will always want to show the carriage return instead of the “Done” button, unless you put in some hack around this.

The least complication solution I found that doesn’t involve remapping the behavior of the carriage return is here: https://stackoverflow.com/a/12570003/3268329. This solution will nullify Android relentless desire to force setting of the IME_FLAG_NO_ENTER_ACTION flag for multi-line views, which causes the Done button to disappear.

Answer:

While none of the other solutions ever worked for me, the following worked beautifully and saved me days and days of more googling, with a few twists of my own of course. Unfortunately don’t remember where I got the code from exactly and so cannot give the author the credit he/she so deserves.

In your Java code :

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

Answer:

I struggled as well for quite some time, but i finally found a solution!

Just create a custom EditText class as such :

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

This class removes lineBreaks (\n), wraps the text as textMultiline would do, AND allows you to replace the Enter button by a ImeAction ;).

You just need to call it in your XML instead of the classic EditText class.

To explain the logic here :

  • Set the EditText as a singleLine to be able to show a ImeAction button instead of Enter.
  • Remove the horizontal scrolling to make the text go to the next line when reaching the end of the view.
  • Watch the layout changes with the onGlobalLayoutListener, and set it’s “line” parameter to the “lineCount” of the current text held by the editText. This is what refreshes its height.

Answer:

Working solution is here, create your custom EditTextView (just extend a textview) and override onInputConnection wit a piece of code youll find in accepted answer here: Multiline EditText with Done SoftInput Action Label on 2.3