Multi-line EditText with Done action button

Android

Android Problem Overview


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.

Android Solutions


Solution 1 - Android

Use

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

and in XML:

android:inputType="textMultiLine"

Solution 2 - Android

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.

Solution 3 - Android

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

Solution 4 - Android

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

Solution 5 - Android

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

Solution 6 - Android

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

Solution 7 - Android

This seems to work for me perfectly

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

Solution 8 - Android

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

https://stackoverflow.com/questions/6007204/android-soft-keyboard-action-button/6007433

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

Solution 9 - Android

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

Solution 10 - Android

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.

Solution 11 - Android

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

Solution 12 - Android

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

Solution 13 - Android

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.

Solution 14 - Android

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.

Solution 15 - Android

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: https://stackoverflow.com/questions/5014219/multiline-edittext-with-done-softinput-action-label-on-2-3

Solution 16 - Android

If you use DataBinding, you should create a method that will catch actions. See also https://stackoverflow.com/a/52902266/2914140.

Some file with extension methods, for instance, BindingAdapters.kt:

@BindingAdapter("inputType", "action")
fun EditText.setMultiLineCapSentencesAndDoneAction(inputType: Int, callback: OnActionListener?) {
    setRawInputType(inputType)
    if (callback == null) setOnEditorActionListener(null)
    else setOnEditorActionListener { v, actionId, event ->
        if (actionId == EditorInfo.IME_ACTION_DONE ||
            event?.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN
        ) {
            callback.enterPressed()
            return@setOnEditorActionListener true
        }
        return@setOnEditorActionListener false
    }
}

interface OnActionListener {
    fun enterPressed()
}

Then in XML:

<data>
    <variable
        name="viewModel"
        type="YourViewModel" />

    <import type="android.text.InputType" />
</data>

<EditText
    android:imeOptions="actionDone"
    android:inputType=""
    app:action="@{() -> viewModel.send()}"
    app:inputType="@{InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE}" />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkefsView Question on Stackoverflow
Solution 1 - AndroidalexbtrView Answer on Stackoverflow
Solution 2 - AndroidHYSView Answer on Stackoverflow
Solution 3 - AndroidAnees UView Answer on Stackoverflow
Solution 4 - AndroidOllie CView Answer on Stackoverflow
Solution 5 - AndroidLukapView Answer on Stackoverflow
Solution 6 - AndroidGiboltView Answer on Stackoverflow
Solution 7 - AndroidyonezView Answer on Stackoverflow
Solution 8 - AndroidMartin StoneView Answer on Stackoverflow
Solution 9 - AndroidJean GonçalvesView Answer on Stackoverflow
Solution 10 - AndroidtwallView Answer on Stackoverflow
Solution 11 - AndroidMarcusdevView Answer on Stackoverflow
Solution 12 - AndroidKaushik NPView Answer on Stackoverflow
Solution 13 - AndroidSteve BView Answer on Stackoverflow
Solution 14 - AndroidGabriel MorinView Answer on Stackoverflow
Solution 15 - AndroidvoytezView Answer on Stackoverflow
Solution 16 - AndroidCoolMindView Answer on Stackoverflow