How to let EditText take as much space it needs, to be scrollable together with content above it?
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>
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>
Your expected result can be achieved by changing the layout.xml and MainActivity.Change layout_height
and adding layout_weight
for ConstraintLayout
as follows:
<?xml version="1.0" encoding="utf-8"?><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="0dp" android:layout_weight="1" android:background="@android:drawable/alert_light_frame" android:clickable="true" android:focusable="false" android:nestedScrollingEnabled="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="visible" 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>
Manifest.xml is:
<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.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity></application>
Record the current value of scrollX & scroll Y for NestedScrollView
and adjust NestedScrollView
as follows:
MainActivity.java
public class MainActivity extends AppCompatActivity { private int scrollX; private int scrollY; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final EditText editText = findViewById(R.id.contentEditText); final LinearLayout autoHideLayout = findViewById(R.id.autoHideLayout); ConstraintLayout container = findViewById(R.id.container); container.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { autoHideLayout.setVisibility(View.VISIBLE); editText.requestFocus(); editText.setSelection(editText.length()); } }); final NestedScrollView nestedScrollView = findViewById(R.id.nestedScrollView); editText.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { scrollX = nestedScrollView.getScrollX(); scrollY = nestedScrollView.getScrollY(); autoHideLayout.setVisibility(View.VISIBLE); return false; } }); editText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) nestedScrollView.scrollTo(scrollX, scrollY); if (!hasFocus) { autoHideLayout.setVisibility(View.GONE); } } }); }}
It didn't scroll when it was not necessary. See the screenshot: