android - sobre - ¿Cómo permitir que EditText ocupe todo el espacio que necesita para poder desplazarse junto con el contenido que se encuentra arriba?
manual de programacion android (4)
Debería poder lograr lo que desea calculando los minLines
según la altura de la pantalla. Vea el ejemplo de actividad y diseño a continuación.
Necesitará escribir un poco de texto para usar las líneas mínimas y crecer más allá de la altura de la pantalla para iniciar el comportamiento de desplazamiento, pero puede minLines
agregando algunas líneas constantes al cálculo de 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);
}
}
Aquí está el diseño
<?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>
EDITAR
Reconstruí su solución y puede corregir el problema de enfoque configurando este oyente en su EditText. Funciona al anular la acción de desplazamiento cuando el Texto de edición recibe el foco, para solo desplazarse lo suficiente como para que el cursor sea visible.
contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus){
nestedScrollView.scrollTo(0, 0);
}
}
});
Editar 2
He actualizado mi respuesta para reflejar los cambios con la nueva recompensa, esto debería ser bastante parecido a lo que necesita, si he entendido la pregunta correctamente.
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);
}
});
}
}
Aquí está el nuevo diseño
<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>
Fondo
Tengo un diseño que tiene algunas vistas en la parte superior, que debe ser desplazable junto con un texto de edición debajo de ellos.
El EditText toma el resto del espacio, tanto espacio como necesita.
Aquí hay un ejemplo de diseño de POC que lo demuestra (usamos solo 2 EditTexts aquí):
<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>
He establecido un marco de fondo para tener una indicación visual de qué tan grande es el EditText.
El problema
He encontrado tantas soluciones a lo que escribí, pero ninguna de ellas maneja bien el desplazamiento.
Lo que siempre estoy viendo, es al menos uno de esos problemas:
- No se puede desplazar la página completa (solo EditText puede ser desplazable, lo que estoy tratando de evitar), por lo que ya no puedo acceder a las vistas en la parte superior.
- Cuando ingreso texto, el cursor puede salir del área visible
- A medida que escribo más y más líneas, no se desplaza por toda la página. Solo en el EditText mismo.
Lo que he intentado
He intentado esas soluciones:
- Todo desde here , here , here , here . Tal vez más, pero no hice suficiente seguimiento ...
- Intenté varios valores
windowSoftInputMode
en el manifiesto, y traté de establecerisNestedScrollingEnabled
en el NestedScrollView. - Probé varias configuraciones en el XML, para permitir que EditText ocupe todo el espacio que necesita, para evitar que se pueda desplazar dentro de él.
La pregunta
¿Cómo puedo hacer que el EditText inferior ocupe todo el espacio que necesita y pueda seguir desplazando NestedScrollView completo sin problemas en la edición?
EDITAR: ya que la aplicación original es un poco más compleja, con algunas vistas en la parte inferior (dentro de lo que es una barra de herramientas) que se ocultan automáticamente cuando no está enfocado en el texto de edición inferior, esto hizo que la respuesta no la encontré trabajar.
Además, accidentalmente he otorgado la recompensa a la respuesta incorrecta, así que aquí hay una nueva recompensa, en el POC más complejo. La pregunta sigue siendo la misma. El NestedScrollView debe permanecer en el mismo lugar, sin desplazarse cuando se enfoca en el texto de edición inferior.
<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)
}
}
}
Me doy cuenta de que NestedScrollView
no se desplazará hasta que su contenido salga de la pantalla. Hago un truco y coloco algunas filas vacías después del texto ingresado para garantizar que el contenido salga de la pantalla actual. Después de que el foco de las pérdidas de EditText quito las filas vacías.
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();
}
}
}
Su resultado esperado se puede lograr cambiando el layout.xml y MainActivity. Cambie layout_height
y agregando layout_weight
para ConstraintLayout
siguiente manera:
<?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 es:
<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>
Registre el valor actual de scrollX y scroll Y para NestedScrollView
y ajuste NestedScrollView
siguiente manera:
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);
}
}
});
}
}
No se desplazó cuando no era necesario. Vea la captura de pantalla:
Tengo algunas soluciones para esto, envolviendo el EditText inferior con un diseño que le otorgará enfoque.
No requiere mucho código
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)
}
}
}
}
manifiesto
<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>