如何让 EditText 占用它需要的 space ,以便与上面的内容一起滚动?
How to let EditText take as much space it needs, to be scrollable together with content above it?
背景
我的布局在顶部有一些视图,它们应该可以与它们下方的 EditText 一起滚动。
EditText 占用 space 的其余部分,根据需要使用 space。
这是一个演示它的示例 POC 布局(此处仅使用 2 个 EditText):
<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>
我设置了一个背景框来直观地指示 EditText 的大小。
问题
我发现了很多针对我所写内容的解决方案,但其中 none 实际上可以很好地处理滚动。
我经常看到的至少是以下问题之一:
- 无法滚动整个页面(只有 EditText 可能是可滚动的,我试图避免这种情况),因此无法再访问顶部的视图。
- 当我输入文本时,插入符号可能会超出可见区域
- 随着我键入越来越多的行,它不会滚动整个页面。仅在 EditText 本身中。
我试过的
我已经尝试过这些解决方案:
- 全部来自here, here, here , here。也许更多,但我没有保持足够的跟踪...
- 我在清单中尝试了各种
windowSoftInputMode
值,并尝试在 NestedScrollView 中设置 isNestedScrollingEnabled
。
- 尝试了 XML 中的各种配置,让 EditText 根据需要使用尽可能多的 space,以防止它在其中滚动。
问题
我怎样才能让底部的 EditText 根据需要使用尽可能多的 space,并且仍然能够滚动整个 NestedScrollView,而不会出现编辑问题?
编辑:由于原始应用程序有点复杂,底部有一些视图(在类似工具栏的内部),当您不关注底部 EditText 时会自动隐藏,这就是答案我发现不行。
此外,我不小心将赏金授予了错误的答案,所以这里有一个新的赏金,用于更复杂的 POC。问题保持不变。 NestedScrollView 应该保持在同一个地方,在关注底部 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)
}
}
}
我意识到 NestedScrollView
在其内容离开屏幕之前不会滚动。我做了一个 hack,在输入的文本之后放置了一些空行,以确保内容会超出当前屏幕。在 EditText 失去焦点后,我删除了空行。
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();
}
}
}
根据屏幕的高度计算minLines
应该可以达到你想要的效果。请参阅下面的示例 activity 和布局。
您需要键入相当多的文本才能用完最小行数并超出屏幕高度才能开始滚动行为,但您可以通过向 minLines
计算
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);
}
}
布局如下
<?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>
编辑
我重新创建了您的解决方案,您可以通过将此侦听器设置为您的 EditText 来更正焦点问题。它的工作原理是在“编辑文本”获得焦点时覆盖滚动操作,只滚动到足以使光标可见的程度。
contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus){
nestedScrollView.scrollTo(0, 0);
}
}
});
编辑 2
我已经更新了我的答案以反映新赏金的变化,如果我正确理解了这个问题,这应该非常接近您的需要。
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);
}
});
}
}
这是新布局
<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>
我找到了一些解决方法,方法是将底部的 EditText 包裹在一个可以赋予它焦点的布局中。
根本不需要太多代码
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 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>
您可以通过更改 layout.xml 和 MainActivity 来实现您的预期结果。
更改 layout_height
并为 ConstraintLayout
添加 layout_weight
,如下所示:
<?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 是:
<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>
记录NestedScrollView
的scrollX&scroll Y的当前值,调整NestedScrollView
如下:
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);
}
}
});
}
}
不需要的时候不滚动。看截图:
背景
我的布局在顶部有一些视图,它们应该可以与它们下方的 EditText 一起滚动。
EditText 占用 space 的其余部分,根据需要使用 space。
这是一个演示它的示例 POC 布局(此处仅使用 2 个 EditText):
<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>
我设置了一个背景框来直观地指示 EditText 的大小。
问题
我发现了很多针对我所写内容的解决方案,但其中 none 实际上可以很好地处理滚动。
我经常看到的至少是以下问题之一:
- 无法滚动整个页面(只有 EditText 可能是可滚动的,我试图避免这种情况),因此无法再访问顶部的视图。
- 当我输入文本时,插入符号可能会超出可见区域
- 随着我键入越来越多的行,它不会滚动整个页面。仅在 EditText 本身中。
我试过的
我已经尝试过这些解决方案:
- 全部来自here, here, here , here。也许更多,但我没有保持足够的跟踪...
- 我在清单中尝试了各种
windowSoftInputMode
值,并尝试在 NestedScrollView 中设置isNestedScrollingEnabled
。 - 尝试了 XML 中的各种配置,让 EditText 根据需要使用尽可能多的 space,以防止它在其中滚动。
问题
我怎样才能让底部的 EditText 根据需要使用尽可能多的 space,并且仍然能够滚动整个 NestedScrollView,而不会出现编辑问题?
编辑:由于原始应用程序有点复杂,底部有一些视图(在类似工具栏的内部),当您不关注底部 EditText 时会自动隐藏,这就是答案我发现不行。
此外,我不小心将赏金授予了错误的答案,所以这里有一个新的赏金,用于更复杂的 POC。问题保持不变。 NestedScrollView 应该保持在同一个地方,在关注底部 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)
}
}
}
我意识到 NestedScrollView
在其内容离开屏幕之前不会滚动。我做了一个 hack,在输入的文本之后放置了一些空行,以确保内容会超出当前屏幕。在 EditText 失去焦点后,我删除了空行。
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();
}
}
}
根据屏幕的高度计算minLines
应该可以达到你想要的效果。请参阅下面的示例 activity 和布局。
您需要键入相当多的文本才能用完最小行数并超出屏幕高度才能开始滚动行为,但您可以通过向 minLines
计算
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);
}
}
布局如下
<?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>
编辑
我重新创建了您的解决方案,您可以通过将此侦听器设置为您的 EditText 来更正焦点问题。它的工作原理是在“编辑文本”获得焦点时覆盖滚动操作,只滚动到足以使光标可见的程度。
contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus){
nestedScrollView.scrollTo(0, 0);
}
}
});
编辑 2
我已经更新了我的答案以反映新赏金的变化,如果我正确理解了这个问题,这应该非常接近您的需要。
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);
}
});
}
}
这是新布局
<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>
我找到了一些解决方法,方法是将底部的 EditText 包裹在一个可以赋予它焦点的布局中。
根本不需要太多代码
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 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>
您可以通过更改 layout.xml 和 MainActivity 来实现您的预期结果。
更改 layout_height
并为 ConstraintLayout
添加 layout_weight
,如下所示:
<?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 是:
<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>
记录NestedScrollView
的scrollX&scroll Y的当前值,调整NestedScrollView
如下:
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);
}
}
});
}
}
不需要的时候不滚动。看截图: