284 votes

Vue de liste Android dans une vue de défilement

J'ai un modèle Android qui a un scrollView avec un certain nombre d'éléments à l'intérieur. Au bas de la scrollView J'ai un listView qui est ensuite alimenté par un adaptateur.

Le problème que je rencontre, c'est qu'Android exclut l'élément listView de la scrollView comme le scrollView a déjà une fonction de défilement. Je veux que le listView doit être aussi longue que le contenu et pour que la vue de défilement principale puisse défiler.

Comment puis-je obtenir ce comportement ?

Voici ma maquette principale :

<ScrollView
    android:id="@+id/scrollView1"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="2"
    android:fillViewport="true"
    android:gravity="top" >

    <LinearLayout
        android:id="@+id/foodItemActvity_linearLayout_fragments"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
    </LinearLayout>

</ScrollView>

J'ajoute ensuite de manière programmatique mes composants à la couche linéaire avec l'id : foodItemActvity_linearLayout_fragments . Voici l'une des vues qui est chargée dans ce modèle linéaire. C'est celle qui me pose problème avec les défilements.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
       android:id="@+id/fragment_dds_review_textView_label"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Reviews:"
       android:textAppearance="?android:attr/textAppearanceMedium" />

   <ListView
       android:id="@+id/fragment_dds_review_listView"
       android:layout_width="match_parent"
       android:layout_height="wrap_content">
   </ListView>
</LinearLayout>

Mon adaptateur remplit alors cette vue en liste.

Voici une image de la visionneuse de hiérarchie Android lorsque je clique sur le scrollView maître :

Android List View inside a scroll view

Comme vous pouvez le voir, il exclut la liste des commentaires.

Je devrais pouvoir faire défiler la page vers le bas et voir 8 critiques, mais au lieu de cela, il ne me montre que ces 3, et je peux faire défiler sur la petite partie où se trouvent les critiques. Je veux un défilement global de la page

0 votes

0 votes

J'obtiens une solution sur : androidhub4you.com/2012/12/

0 votes

C'est ici. Vous pouvez trouver des informations descriptives complètes : stackoverflow.com/questions/20116381/

576voto

Arshu Points 2160

Pour que toute vue enfant puisse défiler à l'intérieur d'une ScrollView. Tout comme ListView, RecyclerView, etc. Il suffit de remplacer ScrollView con androidx.core.widget.NestedScrollView dans votre xml actuel et la magie opère.

Vous trouverez ci-dessous un exemple de code xml :

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp"
        android:paddingBottom="20dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Recycler View inside a Scroll View"
            android:textColor="@color/black"
            android:textSize="@dimen/_20sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="Below is a Recycler View as an example."
            android:textSize="16sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            app:layout_constraintTop_toBottomOf="@id/et_damaged_qty" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="This textview automatically goes below the Recycler View."
            android:textSize="16sp" />
    </androidx.appcompat.widget.LinearLayoutCompat>
</androidx.core.widget.NestedScrollView>

Vous pouvez maintenant vous débarrasser de tous les affreux bidouillages que vous avez faits pour contourner le défilement imbriqué.

0 votes

Merci ! Cela a fonctionné pour moi mais j'ai dû faire quelques ajustements parce que les éléments de la liste étaient des vues complexes et que j'utilisais ExpandableListView : view = listAdapter.getView(0, view, listView) ; int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(ViewGroup. LayoutParams.MATCH_PARENT, View.MeasureSpec.EXACTEMENT) ; int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.EXACTEMENT) ; view.measure(widthMeasureSpec, heightMeasureSpec) ;

0 votes

Sous Android 2.3, j'obtiens ce comportement : Après avoir fait défiler la liste dans le ScrollView, vous devez appuyer une fois sur le ListView avant de pouvoir la faire défiler à nouveau. Sinon, cela fonctionne bien. Je n'ai pas encore testé dans d'autres versions d'Android. Quelqu'un sait-il comment contourner ce comportement ? (Je veux pouvoir recommencer immédiatement à faire défiler dans la ListView au lieu de devoir d'abord la tapoter).

0 votes

@Nathan : Je vais examiner la question et je posterai une solution si je la trouve au plus tôt.

223voto

ericosg Points 2158

La réponse est simple et je suis surpris qu'elle n'ait pas encore trouvé de réponse ici.

Utilisez un Header View ou/et Footer View sur la liste elle-même. Ne mélangez pas un ScrollView avec un ListView ou tout ce qui peut défiler. Il est destiné à être utilisé avec des en-têtes et des pieds de page :)

En gros, prenez tout le contenu au-dessus de votre ListView, mettez-le dans un autre fichier .xml en tant que layout, puis dans le code gonflez-le et ajoutez-le à la liste en tant que vue d'en-tête.

c'est-à-dire

View header = getLayoutInflater().inflate(R.layout.header, null);
View footer = getLayoutInflater().inflate(R.layout.footer, null);
listView.addHeaderView(header);
listView.addFooterView(footer);

2 votes

@ericosg, Mais je ne veux pas cela, c'est pourquoi je cherche une solution.

13 votes

Si vous ajoutez une vue d'en-tête, la position int param dans onListItemClick sera +1. Vous devez donc le gérer. (le premier élément de la liste aura la position 1, et non 0).

0 votes

J'aimerais qu'il y ait un en-tête pour le ViewPager.

10voto

Su Zhenpeng Points 51
    public static void setListViewHeightBasedOnChildren(ListView listView) {
    // 获取ListView对应的Adapter
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        return;
    }

    int totalHeight = 0;
    for (int i = 0, len = listAdapter.getCount(); i < len; i++) { // listAdapter.getCount()返回数据项的数目
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0); // 计算子项View 的宽高
        totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    // listView.getDividerHeight()获取子项间分隔符占用的高度
    // params.height最后得到整个ListView完整显示需要的高度
    listView.setLayoutParams(params);
}

vous pouvez utiliser ce code pour listview dans scrollview

0 votes

Cela va-t-il désactiver le défilement intégré des listes ?

0 votes

Cela fonctionnera également pour ListView à l'intérieur de RecyclerView pour montrer plus que le premier élément.

0 votes

Merci, cette réponse a été très utile. Mon problème était que j'avais un ListView à l'intérieur d'un ScrollView, et lorsque je mettais à jour les éléments à l'intérieur du ListView, le ScrollView avait toujours la même hauteur. En d'autres termes, l'utilisateur pouvait faire défiler la liste vers le bas même si le ListView comportait moins de rangées.

5voto

Alexander Zhak Points 2818

Je le laisse ici au cas où quelqu'un serait confronté au même problème. Je devais placer une ListView à l'intérieur d'une ScrollView. Le ListView avec en-tête n'était pas une option pour un certain nombre de raisons. Je n'avais pas non plus la possibilité d'utiliser LinearLayout au lieu de ListView. J'ai donc suivi la solution acceptée, mais cela n'a pas fonctionné parce que les éléments de la liste avaient une mise en page complexe avec plusieurs rangées et que chaque élément du ListView avait une hauteur variable. La hauteur n'était pas mesurée correctement. La solution était de mesurer chaque élément dans la méthode getView() de l'adaptateur ListView.

@Override
public View getView(int position, View view, ViewGroup parent) {
    ViewHolder holder;
    if (view == null) {
        . . .
        view.setTag(holder);
    } else holder = (ViewHolder)view.getTag();
    . . .

    // measure ListView item (to solve 'ListView inside ScrollView' problem)
    view.measure(View.MeasureSpec.makeMeasureSpec(
                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    return view;
}

2voto

Rajnish Mishra Points 408

Mise à jour

<ScrollView
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:fillViewport="true"
android:gravity="top" >

<LinearLayout
    android:id="@+id/foodItemActvity_linearLayout_fragments"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
</LinearLayout>

à

<ScrollView
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="2"
android:fillViewport="true"
android:gravity="top" >

<LinearLayout
    android:id="@+id/foodItemActvity_linearLayout_fragments"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
</LinearLayout>

Le problème ici est que vous essayez de définir la hauteur à 0dp (fixe).

0 votes

C'est pour que les poids aient un effet sur elle.

0 votes

Souhaitez-vous que l'image du produit et la vue du texte à gauche sur le côté supérieur puissent défiler ? Si c'est le cas, vous devez placer ces deux éléments dans votre vue défilante, car je constate qu'ils ne le sont pas actuellement.

0 votes

Ils se trouvent actuellement à l'intérieur de la vue de défilement, car je les ajoute par programme à la disposition linéaire qui se trouve à l'intérieur de la vue de défilement. Si ma logique est correcte, les enfants de la mise en page linéaire devraient également se trouver à l'intérieur du scrollview.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X