Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to let EditText take as much space it needs, to be scrollable together with content above it?

Tags:

Background

I have a layout that has some views at the top, which should be scrollable together with an EditText below them.

The EditText takes the rest of the space, as much space as it needs.

Here's a sample POC layout that demonstrate it (used just 2 EditTexts here) :

<android.support.v4.widget.NestedScrollView android:id="@+id/nestedScrollView"     xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"     android:layout_height="match_parent" android:fillViewport="true">      <LinearLayout         android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">          <EditText             android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content"             android:ellipsize="end" android:hint="title" android:imeOptions="actionNext|flagNoExtractUi"             android:inputType="text|textAutoCorrect|textCapSentences" android:maxLines="1"             android:nextFocusDown="@id/contentEditText" android:nextFocusForward="@id/contentEditText"             android:scrollHorizontally="true" android:textColor="#2a2f3b" android:textColorHint="#a3a3a3"             android:textSize="21sp"/>          <EditText             android:id="@+id/contentEditText" android:layout_width="match_parent" android:layout_height="match_parent"             android:gravity="top" android:hint="content" android:background="@android:drawable/alert_light_frame"             android:imeOptions="actionDone|flagNoEnterAction|flagNoExtractUi" android:textSize="18sp"             android:inputType="textMultiLine|textAutoCorrect|textCapSentences"/>      </LinearLayout>  </android.support.v4.widget.NestedScrollView> 

I've set a background frame to have a visual indication of how large the EditText is.

The problem

I've found so many solutions to what I wrote, but none of them actually handles the scrolling well.

What I'm always seeing, is at least one of those issues:

  1. Unable to scroll entire page (only EditText might be scrollable, which I'm trying to avoid), so can't get to the views at the top anymore.
  2. When I enter text, the caret might go outside of the visible area
  3. As I type more and more lines, it doesn't scroll the entire page. Only in the EditText itself.

What I've tried

I've tried those solutions:

  1. All from here, here, here , here. Maybe more, but I didn't keep enough track...
  2. I tried various windowSoftInputMode values in the manifest, and tried to set isNestedScrollingEnabled in the NestedScrollView.
  3. Tried various configurations in the XML, to let the EditText take as much space as it needs, to prevent it from being scrollable within it.

The question

How can I make the bottom EditText to take as much space as it needs, and still be able to scroll entire NestedScrollView, without an issue in editing ?


EDIT: since the original app is a bit more complex, having some views at the bottom (inside what is like a toolbar) that auto-hide when you are not in focus on the bottom EditText , this made the answer I've found not to work.

Also, I've accidentally granted the bounty to the wrong answer, so here's a new bounty, on the more complex POC. The question stays the same. The NestedScrollView should remain on the same place, without scrolling when focusing on the bottom EditText.

<LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"     android:layout_height="match_parent" android:orientation="vertical">      <View         android:layout_width="0dp" android:layout_height="0dp" android:focusable="true"         android:focusableInTouchMode="true"/>      <android.support.v4.widget.NestedScrollView         android:id="@+id/nestedScrollView" android:layout_width="match_parent" android:layout_height="0px"         android:layout_weight="1" android:fillViewport="true">          <LinearLayout             android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">              <EditText                 android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content"                 android:ellipsize="end" android:hint="title" android:imeOptions="actionNext|flagNoExtractUi"                 android:inputType="text|textAutoCorrect|textCapSentences" android:maxLines="1"                 android:nextFocusDown="@id/contentEditText" android:nextFocusForward="@id/contentEditText"                 android:scrollHorizontally="true" android:textColor="#2a2f3b" android:textColorHint="#a3a3a3"                 android:textSize="21sp"/>              <android.support.constraint.ConstraintLayout                 android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"                 android:background="@android:drawable/alert_light_frame" android:clickable="true"                 android:focusable="false">                  <EditText                     android:id="@+id/contentEditText" android:layout_width="match_parent"                     android:layout_height="wrap_content" android:background="@null" android:gravity="top"                     android:hint="content" android:imeOptions="actionDone|flagNoEnterAction|flagNoExtractUi"                     android:inputType="textMultiLine|textAutoCorrect|textCapSentences" android:textSize="18sp"/>             </android.support.constraint.ConstraintLayout>          </LinearLayout>      </android.support.v4.widget.NestedScrollView>      <LinearLayout         android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true"         android:orientation="vertical">          <LinearLayout             android:id="@+id/autoHideLayout" android:layout_width="match_parent" android:layout_height="wrap_content"             android:orientation="horizontal" android:visibility="gone" tools:visibility="visible">              <Button                 android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button"/>              <Button                 android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button2"/>          </LinearLayout>     </LinearLayout> </LinearLayout>   class MainActivity : AppCompatActivity() {      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)         setContentView(R.layout.activity_main)         container.setOnClickListener {             contentEditText.requestFocus()             contentEditText.setSelection(contentEditText.length())         }         contentEditText.setOnFocusChangeListener { view, hasFocus ->             autoHideLayout.visibility = if (hasFocus) View.VISIBLE else View.GONE             if (hasFocus)                 nestedScrollView.scrollTo(0, 0)         }     } } 
like image 825
android developer Avatar asked Mar 21 '18 13:03

android developer


People also ask

How do I scroll up layout when clicking on EditText?

try this android:windowSoftInputMode="adjustResize" in your activity in manifest file.

How do you make EditText read only?

The most reliable way to do this is using UI. setReadOnly(myEditText, true) from this library. There are a few properties that have to be set, which you can check out in the source code.

What is EditText control?

Advertisements. A EditText is an overlay over TextView that configures itself to be editable. It is the predefined subclass of TextView that includes rich editing capabilities.

Which method is used to set the EditText?

In android, we can set the text of EditText control either while declaring it in Layout file or by using setText() method in Activity file.


2 Answers

I got some workaround for this, by wrapping the bottom EditText with a layout that will grant it focus.

Doesn't require much code at all

activity_main.xml

<android.support.v4.widget.NestedScrollView     android:id="@+id/nestedScrollView" xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true">      <LinearLayout         android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">          <EditText             android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content"             android:ellipsize="end" android:hint="title" android:imeOptions="actionNext|flagNoExtractUi"             android:inputType="text|textAutoCorrect|textCapSentences" android:maxLines="1"             android:nextFocusDown="@id/contentEditText" android:nextFocusForward="@id/contentEditText"             android:scrollHorizontally="true" android:textColor="#2a2f3b" android:textColorHint="#a3a3a3"             android:textSize="21sp"/>          <android.support.constraint.ConstraintLayout             android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"             android:background="@android:drawable/alert_light_frame" android:clickable="true" android:focusable="false">              <EditText                 android:id="@+id/contentEditText" android:layout_width="match_parent"                 android:layout_height="wrap_content" android:background="@null" android:gravity="top"                 android:hint="content" android:imeOptions="actionDone|flagNoEnterAction|flagNoExtractUi"                 android:inputType="textMultiLine|textAutoCorrect|textCapSentences" android:textSize="18sp"/>         </android.support.constraint.ConstraintLayout>      </LinearLayout>  </android.support.v4.widget.NestedScrollView> 

MainActivity.kt

class MainActivity : AppCompatActivity() {      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)         setContentView(R.layout.activity_main)         container.setOnClickListener {             contentEditText.requestFocus()             contentEditText.setSelection(contentEditText.length())         }         contentEditText.setOnFocusChangeListener { view, hasFocus ->             if (hasFocus) {                 nestedScrollView.scrollTo(0, 0)             }         }     } } 

manifest

<manifest package="com.example.user.myapplication" xmlns:android="http://schemas.android.com/apk/res/android">      <application         android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"         android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">         <activity android:name=".MainActivity" android:windowSoftInputMode="adjustResize">             <intent-filter>                 <action android:name="android.intent.action.MAIN"/>                  <category android:name="android.intent.category.LAUNCHER"/>             </intent-filter>         </activity>     </application>  </manifest> 
like image 183
android developer Avatar answered Oct 17 '22 06:10

android developer


You should be able to achieve what you want by calculating the minLines based on the height of the screen. See the example activity and layout below.

You'll need to type quite a bit of text to use up the minimum lines and grow beyond the height of the screen to start the scrolling behavior, but you can circumvent this by adding a few constant lines to the minLines calculation

public class ScrollingActivity extends AppCompatActivity {     EditText editText2;      @Override     protected void onCreate(Bundle savedInstanceState)     {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_scrolling);          editText2 = findViewById(R.id.editText2);          int minHeight = getResources().getDisplayMetrics().heightPixels - editText2.getTop();         float lineHeight = editText2.getPaint().getFontMetrics().bottom - editText2.getPaint().getFontMetrics().top;         int minLines = (int)(minHeight/lineHeight);         editText2.setMinLines(minLines);     } } 

Here is the layout

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout     android:id="@+id/parentLayout"     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     tools:context="com.my.package.ScrollingActivity">     <ScrollView         android:layout_width="match_parent"         android:layout_height="match_parent"         android:background="#000000">         <android.support.constraint.ConstraintLayout             android:id="@+id/scrollingLayout"             android:layout_width="match_parent"             android:layout_height="wrap_content">             <EditText                 android:id="@+id/editText1"                 android:layout_margin="15dp"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 app:layout_constraintTop_toTopOf="parent"                 android:background="#FFFFFF"/>             <EditText                 android:id="@+id/editText2"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 app:layout_constraintTop_toBottomOf="@id/editText1"                 app:layout_constraintBottom_toBottomOf="parent"                 android:layout_margin="15dp"                 android:background="#FFFFFF"/>         </android.support.constraint.ConstraintLayout>     </ScrollView> </android.support.constraint.ConstraintLayout> 

EDIT

I recreated your solution and you can correct the focus issue by setting this listener to your EditText. It works by overriding the scroll action when the Edit Text gets the focus, to only scroll enough to make the cursor visible.

contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {     @Override     public void onFocusChange(View view, boolean hasFocus) {         if(hasFocus){             nestedScrollView.scrollTo(0, 0);         }     } }); 

EDIT 2

I've updated my answer to reflect the changes with the new bounty, this should be pretty close to what you need, if I've understood the question properly.

public class ScrollingActivity extends AppCompatActivity {     ConstraintLayout parentLayout;     EditText contentEditText;     NestedScrollView nestedScrollView;     LinearLayout autoHideLayout;      boolean preventScroll = true;      @Override     protected void onCreate(Bundle savedInstanceState)     {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_scrolling);          contentEditText = findViewById(R.id.contentEditText);         nestedScrollView = findViewById(R.id.nestedScrollView);         autoHideLayout = findViewById(R.id.autoHideLayout);         parentLayout = findViewById(R.id.parentLayout);         nestedScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);          getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);           parentLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {             @Override             public void onGlobalLayout() {                 int minHeight = autoHideLayout.getTop() - contentEditText.getTop();                 float lineHeight = contentEditText.getPaint().getFontMetrics().bottom - contentEditText.getPaint().getFontMetrics().top;                 int minLines = (int)(minHeight/lineHeight);                 if(minLines != contentEditText.getMinLines()){                     contentEditText.setMinLines(minLines);                 }             }         });           contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {             @Override             public void onFocusChange(View view, boolean hasFocus) {                 ViewGroup.LayoutParams layoutParams = autoHideLayout.getLayoutParams();                 if(hasFocus){                     nestedScrollView.scrollTo(0,0);                     layoutParams.height = ConstraintLayout.LayoutParams.WRAP_CONTENT;                 } else{                     layoutParams.height = 0;                 }                 autoHideLayout.setLayoutParams(layoutParams);             }         });     } } 

Here is the new layout

    <android.support.constraint.ConstraintLayout     android:id="@+id/parentLayout"     xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"     android:layout_height="match_parent" android:orientation="vertical"     android:animateLayoutChanges="true">      <android.support.v4.widget.NestedScrollView         android:id="@+id/nestedScrollView"         android:layout_width="0dp"         android:layout_height="0dp"         android:focusable="false"         android:focusableInTouchMode="false"         app:layout_constraintLeft_toLeftOf="parent"         app:layout_constraintRight_toRightOf="parent"         app:layout_constraintTop_toTopOf="parent"         app:layout_constraintBottom_toTopOf="@id/autoHideLayout"         >          <LinearLayout             android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">              <EditText                 android:id="@+id/titleEditText" android:layout_width="match_parent" android:layout_height="wrap_content"                 android:ellipsize="end" android:hint="title" android:imeOptions="actionNext|flagNoExtractUi"                 android:inputType="text|textAutoCorrect|textCapSentences" android:maxLines="1"                 android:nextFocusDown="@id/contentEditText" android:nextFocusForward="@id/contentEditText"                 android:scrollHorizontally="true" android:textColor="#2a2f3b" android:textColorHint="#a3a3a3"                 android:background="@android:drawable/alert_light_frame"                 android:textSize="21sp"/>              <EditText                 android:id="@+id/contentEditText" android:layout_width="match_parent"                 android:layout_height="wrap_content" android:background="@android:drawable/alert_light_frame" android:gravity="top"                 android:hint="content" android:imeOptions="actionDone|flagNoEnterAction|flagNoExtractUi"                 android:inputType="textMultiLine|textAutoCorrect|textCapSentences" android:textSize="18sp"/>          </LinearLayout>      </android.support.v4.widget.NestedScrollView>      <LinearLayout         android:id="@+id/autoHideLayout" android:layout_width="0dp" android:layout_height="0dp"         app:layout_constraintLeft_toLeftOf="parent"         app:layout_constraintRight_toRightOf="parent"         android:orientation="horizontal" android:visibility="visible" tools:visibility="visible"         app:layout_constraintBottom_toBottomOf="parent">          <Button             android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button"/>          <Button             android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button2"/>      </LinearLayout> </android.support.constraint.ConstraintLayout> 
like image 39
Dave S Avatar answered Oct 17 '22 07:10

Dave S