Comment laisser EditText prendre autant d'espace qu'il en a besoin, pour être scrollable avec du contenu au-dessus?
arrière-plan
j'ai une mise en page qui a quelques vues en haut, qui devrait être scrollable avec un texte D'édition en dessous d'eux.
L'EditText prend le reste de l'espace, tout l'espace dont il a besoin.
voici un exemple de la mise en page POC qui le démontre (utilisé seulement 2 édits ici):
<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>
j'ai mis un cadre de fond pour avoir une indication visuelle de la taille du texte édité.
le problème
j'ai trouvé tellement de solutions à ce que j'ai écrit, mais aucune ne gère vraiment le défilement.
Ce que je suis toujours la voir, au moins une de ces questions:
- impossible de faire défiler toute la page (seul EditText peut être scrollable, ce que j'essaie d'éviter), donc je ne peux plus accéder aux vues du haut.
- quand j'entre le texte, le caret pourrait aller en dehors de la zone visible
- comme je tape de plus en plus de lignes, il ne fait pas défiler la page entière. Seulement dans l'EditText lui-même.
Ce que j'ai essayé
j'ai essayé ces solutions:
- Tous de ici , ici , ici , ici . Peut-être plus, mais je n'ai pas gardé assez de trace...
- j'ai essayé diverses valeurs
windowSoftInputMode
dans le manifeste, et j'ai essayé de mettreisNestedScrollingEnabled
dans le NestedScrollView. - a essayé différentes configurations dans le XML, pour laisser le texte D'édition prendre autant d'espace qu'il a besoin, pour l'empêcher d'être scrollable à l'intérieur.
la question
Comment puis-je faire le fond Édittext pour prendre autant d'espace qu'il a besoin, et encore être en mesure de faire défiler entier NestedScrollView, sans problème d'édition ?
EDIT: depuis l'application originale est un peu plus complexe, ayant quelques vues en bas (à l'intérieur de ce qui est comme une barre d'outils) qui AUTO-masquent quand vous n'êtes pas en focus sur le texte éditorial en bas , cela a fait la réponse que j'ai trouvé ne pas fonctionner.
aussi, j'ai accidentellement accordé la prime à la mauvaise réponse, donc voici une nouvelle prime, sur le POC plus complexe. La question reste la même. La fenêtre NestedScrollView doit rester au même endroit, sans défilement lorsque vous vous focalisez sur le texte édité en bas de page.
<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)
}
}
}
4 réponses
vous pouvez obtenir le résultat escompté en modifiant la disposition.xml et MainActivity.
Modifier layout_height
et ajouter layout_weight
pour ConstraintLayout
comme suit:
<?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>
manifeste.le xml est:
<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>
Enregistrer la valeur courante de scrollX & scroll Y pour NestedScrollView
et ajuster NestedScrollView
comme suit:
activité principale.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 ne pas faire défiler quand il n'était pas nécessaire. Voir la capture d'écran:
j'ai trouvé un moyen de contourner ce problème, en enveloppant le texte édité en bas de page avec une mise en page qui lui permettra de se concentrer.
Ne nécessite pas beaucoup de code à tous les
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>
activité principale.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)
}
}
}
}
manifeste
<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>
vous devriez être en mesure d'atteindre ce que vous voulez en calculant le minLines
basé sur la hauteur de l'écran. Voir l'exemple d'activité et de mise en page ci-dessous.
vous aurez besoin de taper un peu de texte pour utiliser les lignes minimales et grandir au-delà de la hauteur de l'écran pour démarrer le comportement de défilement, mais vous pouvez contourner cela en ajoutant quelques lignes constantes au minLines
calcul
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);
}
}
voici le plan
<?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>
MODIFIER
j'ai recréé votre solution et vous pouvez corriger le problème de focus en réglant cet écouteur sur votre texte D'édition. Il fonctionne en écrasant l'action de défilement lorsque le texte D'édition obtient la mise au point, de faire défiler assez pour rendre le curseur visible.
contentEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if(hasFocus){
nestedScrollView.scrollTo(0, 0);
}
}
});
EDIT 2
j'ai mis à jour ma réponse pour refléter les changements avec le nouveau bounty, ça devrait être assez proche de ce dont tu as besoin, si j'ai bien compris la question.
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);
}
});
}
}
Voici la nouvelle mise en page
<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>
je réalise que NestedScrollView
ne ferait pas défiler jusqu'à ce que son contenu sorte de l'écran. Je fais un hack et mets quelques lignes vides après le texte entré pour m'assurer que le contenu ira en dehors de l'écran courant. Après le focus des pertes EditText, je supprime les lignes vides.
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();
}
}
}