25

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)
        }
    }
}
android developer
  • 106,412
  • 122
  • 641
  • 1,128
  • you want whole screen to scroll instead of just edit text scroll ? – Vivek Mishra Mar 23 '18 at 17:00
  • are you try to achieve this https://youtu.be/jnO1KQHo3gU – DimDim Mar 23 '18 at 19:55
  • @VivekMishra Yes. Exactly – android developer Mar 23 '18 at 20:14
  • @DimDim Seems right. Supposing it has no weird issues, that's exactly what I need. – android developer Mar 23 '18 at 20:14
  • When you say `The EditText takes the rest of the space, as much space as it needs`, do you mean that it should fill the screen, and then grow if it needs to? Or do you just mean that it should be allowed to grow as necessary, even if it starts off smaller than filling the screen? – Ben P. Mar 26 '18 at 21:12
  • It should be like an email or note editor app. There are a few views at the top, and an EditText that takes the rest of the space, and gets larger as needed. You can click anywhere on its empty space of it to grant it focus. – android developer Mar 26 '18 at 22:22
  • For me it only happens if SoftKeyboard is visible. Otherwise it works fine. Is it the same case for you? `android:windowSoftInputMode="adjustResize"` setting this in manifest has solved the problem. – Sagar Mar 28 '18 at 01:20
  • can you post the image with the question? so that it can be more clear... – RBK Apr 13 '18 at 07:17
  • @RBK Picture of what? It's just one EditText below another... – android developer Apr 13 '18 at 19:37
  • @Sagar Still has this issue. If I put multiple lines on the second EditText, and focus on it from the first one on its first row, it still scrolls. – android developer Apr 13 '18 at 20:16
  • Its bit unclear. Do you want it to scroll or want it to be un scrollable? – Sagar Apr 13 '18 at 23:30
  • @Sagar It shouldn't scroll on this case, because there is no need for it, as there is enough space. See video: https://uploadfiles.io/vgbac – android developer Apr 14 '18 at 11:27
  • @androiddeveloper If I understand correctly, you want it to scroll only if there is a need to scroll. Otherwise it should avoid scrolling as much as possible. Is that correct? – Sagar Apr 14 '18 at 12:53

4 Answers4

15

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>
android developer
  • 106,412
  • 122
  • 641
  • 1,128
  • @DaveS Your workaround for my solution doesn't seem to work. See video here: https://ufile.io/vgbac . Also, I think that even what I wrote isn't always working well. Sometimes, it goes to the end of the EditText, even though I've clicked on a specific point in it. I will untick my answer because it's not good enough, and update to show that it has this issue too. – android developer Mar 27 '18 at 07:14
  • What's wrong with the listener behavior? It shouldn't scroll noticeably unless you are past the bottom of the screen, that way the title text isn't hidden unless it's needed, like you asked. – Dave S Mar 27 '18 at 07:18
  • @DaveS It shouldn't scroll at all if I'm at the top row. The title text do get hidden, just not entirely. – android developer Mar 27 '18 at 11:02
  • @DaveS This causes the keyboard to dissappear in most cases, when I focus on the bottom editText. – android developer Mar 28 '18 at 22:04
  • @DaveS However, removing the part that I do show the keyboard fixes it. So I will give you the bounty, and change my answer to be without the issue. – android developer Mar 28 '18 at 22:48
  • Puzzled, You use `MainActivity.kt`, In your answer but do not use the `kotlin` **tag** in your question. – Jon Goodwin Mar 29 '18 at 23:00
  • @DaveS Now it has 2 issues: when having a lot of lines, it scrolls a lot. When having few, it has a jumping behavior (scrolls a bit in one direction and then the opposite) – android developer Mar 29 '18 at 23:07
  • Ok, I fixed an issue with the minLines, repeatedly calculating and it clears up the jumping behavior. Not sure what the other issue is but give it a try. – Dave S Mar 29 '18 at 23:55
  • @DaveS Both issues still exist. Here's a video: https://ufile.io/83ubo . The jumping in the beginning, and the scrolling in the rest. – android developer Mar 30 '18 at 06:14
  • I don't have the jumping issue without a title bar, and the scroll on focus looks like it needs a custom scroll view to disable. Personally I think that behavior is consistent with the user experience and not worth "fixing" but I have a few things I can try tomorrow. – Dave S Mar 30 '18 at 06:20
  • I haven't been able to fix it with listeners. I think it will require a custom scroll view and it's getting a bit large and complex for this question. – Dave S Apr 06 '18 at 15:33
14

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>
Dave S
  • 3,210
  • 1
  • 16
  • 30
  • Whenever I focus on the bottom EditText, it also scrolls, hiding the top one (and other views if I add them), even though there is plenty of space. See here: https://ufile.io/i02oe . I also don't want to make the bottom EditText take so much more space than it needs. – android developer Mar 24 '18 at 21:28
  • So to clarify you don't want the edittext to fill the remaining space? You want it to just take as much space as it needs? (Wrap_content) – Dave S Mar 24 '18 at 22:03
  • The focus issue should be fixable with something like this. I can try to add it on Monday. https://stackoverflow.com/a/6486348/2680506 – Dave S Mar 24 '18 at 22:10
  • I don't want it to take too much space. It's ok to take the remaining, but it should take more only if it indeed have content that needs more lines. I've put the background for it for a reason - to see its edges (in the normal app I don't have it). When the keyboard isn't shown, it shouldn't show that I can scroll, because there wasn't even a single character entered yet. About the solution that was offered for the focus, it didn't work (tried on both EditTexts). – android developer Mar 24 '18 at 22:33
  • Just try the layout I've created. You will see. The bottom EditText takes there the remaining space. Not more than it. – android developer Mar 24 '18 at 22:59
  • Match parent is the wrong attribute if you want it to be a dynamic size. Needs to be wrap content. My oncreate code should only make it take the remaining space, but when the keyboard appears it will cause it to scroll because then it will be bigger than the smaller screen. – Dave S Mar 25 '18 at 00:37
3

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: screen shot

Sagar
  • 20,467
  • 4
  • 50
  • 56
  • I still see the same issue. Having multiple lines on the second EditText, and then focusing on the first line- will cause a scroll to occur. Unless you've edited the code too, this still isn't a solution to the issue, sadly. Here's a video showing the issue: https://ufile.io/tzz09 – android developer Apr 14 '18 at 16:57
  • You've removed important part of my code, for "autoHideLayout" being automatically shown and hidden. Also you've removed the part of being able to click in the entire space for the EditText. Restoring this code, it still has the same issue. BTW, I think you mean "MainActivity.java" . – android developer Apr 15 '18 at 06:35
  • I have updated the answer. You can check now. sorry for missing the detail – Sagar Apr 16 '18 at 01:50
  • You still removed a part of the code, of pressing on the container. And the issue still exists: https://ufile.io/ilxyq , just scroll a tiny bit and you will notice it jumps when focusing on the second EditText. – android developer Apr 16 '18 at 05:26
  • You can add it. I forgot to paste the code. For me its quite smooth on my emulator running 8.0. Which OS version are you using? I will test on it and update accordingly – Sagar Apr 16 '18 at 05:53
  • Occurs on Android P, Android 6.0.1, and Android 4.4. Tried on emulator too. – android developer Apr 16 '18 at 17:27
  • Updated the code. I have tested the usecase you are trying to perform in video on Samsung Galaxy A5. It works as expected for me. – Sagar Apr 17 '18 at 01:20
  • I think you got it this time. Sadly the bounty was already auto-granted (to you), so I can't grant it. The only thing I've noticed missing in your code (and mine too) is to force show the keyboard when clicking on the container. But that's easy to add :`((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)` . All I can do is accept the answer and give +1 for it, so that's what I do now. – android developer Apr 17 '18 at 06:46
  • @androiddeveloper Good to know. Yeah and unfortunate I cannot get the whole bounty. But happy that it was useful for you.Thanks for responding to the post. – Sagar Apr 17 '18 at 06:56
1

I realize that NestedScrollView would not scroll until it's content goes out of the screen. I make a hack and put some empty rows after the entered text to ensure that the content will go outside the current screen. After the EditText losses focus I remove the empty rows.

public class MainActivity extends AppCompatActivity {
        String EMPTY_SPACES = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
        EditText mEditText;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            mEditText = findViewById(R.id.contentEditText);

            mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View view, boolean hasFocus) {
                    if (!hasFocus) {
                        // code to execute when EditText loses focus
                        mEditText.setText(mEditText.getText().toString().trim());
                    }
                }
            });

            mEditText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    int charsThatGuaranteesTextIsOutOfScreen = 400;
                    if (mEditText.hasFocus() && charSequence.length() < charsThatGuaranteesTextIsOutOfScreen) {
                        mEditText.setText(String.format(Locale.getDefault(), "%1$s%2$s", charSequence, EMPTY_SPACES));
                        mEditText.setSelection(i2);
                    }
                }

                @Override
                public void afterTextChanged(Editable editable) {

                }
            });
        }

        @Override
        public void onBackPressed() {
            if (mEditText.hasFocus()) {
                mEditText.clearFocus();
            } else {
                super.onBackPressed();
            }
        }
    }
DimDim
  • 291
  • 1
  • 17
  • Scrolling doesn't seem to work on this. See video: https://ufile.io/qb6zq . – android developer Mar 23 '18 at 20:57
  • Maybe post full project on the website I've put? I will try the exact same thing you've tried. – android developer Mar 23 '18 at 20:58
  • Hmmm I think `NestedScrollView` would not scroll until it's content goes out of the screen ... my video was shot when the `NestedScrollView` was more then one screen and scrolling effect was as expected – DimDim Mar 23 '18 at 21:23
  • This causes weird editing issues when focusing. Sometimes when I type right after focusing, the text isn't entered, and sometimes the caret moves up for some reason. Also, it puts junk spaces. See here: https://ufile.io/d5v6s . – android developer Mar 24 '18 at 21:23
  • Definitely it is not the best solution I only demonstrate the problem. I am going to make better way to force the `NestedScrollView` scrolling – DimDim Mar 25 '18 at 10:02
  • I got an alternative solution here: https://stackoverflow.com/a/49473337/878126 . But it's also not perfect. Focusing on a multi-line content will scroll it even if you focus on first line. – android developer Mar 25 '18 at 10:48