Wie kann ich eine ListView in eine ScrollView einfügen, ohne dass sie zusammenbricht?


351

Ich habe nach Lösungen für dieses Problem gesucht und die einzige Antwort, die ich finden kann, scheint zu sein: " Fügen Sie keine ListView in eine ScrollView ein ". Ich habe jedoch noch keine wirkliche Erklärung dafür, warum . Der einzige Grund, den ich zu finden scheint, ist, dass Google nicht der Meinung ist, dass Sie das tun sollten. Nun, das tue ich, also habe ich es getan.

Die Frage ist also, wie Sie eine ListView in eine ScrollView einfügen können, ohne dass sie auf ihre minimale Höhe reduziert wird.


11
Denn wenn ein Gerät einen Touchscreen verwendet, gibt es keine gute Möglichkeit, zwischen zwei verschachtelten scrollbaren Containern zu unterscheiden. Es funktioniert auf herkömmlichen Desktops, auf denen Sie mithilfe der Bildlaufleiste einen Container scrollen.
Romain Guy

57
Sicher gibt es. Wenn sie sich im Inneren berühren, scrollen Sie das. Wenn der innere zu groß ist, ist das schlechtes UI-Design. Das heißt nicht, dass es im Allgemeinen eine schlechte Idee ist.
DougW

5
Ich bin gerade auf diese für meine Tablet-App gestoßen. Während es auf dem Smartphone nicht allzu schlimm zu sein scheint, ist dies auf einem Tablet schrecklich, auf dem Sie leicht entscheiden können, ob der Benutzer durch die äußere oder innere ScrollView scrollen möchte. @ DougW: Schreckliches Design, ich stimme zu.
Quallen

4
@Romain - Dies ist ein ziemlich alter Beitrag, daher frage ich mich, ob die Bedenken hier bereits behoben wurden oder ob es immer noch keine gute Möglichkeit gibt, eine ListView in einer ScrollView zu verwenden (oder eine Alternative, die keine manuelle Codierung beinhaltet alle Schönheiten einer ListView in ein LinearLayout)?
Jim

3
@Jim: "oder eine Alternative, bei der nicht alle Funktionen einer ListView manuell in ein LinearLayout codiert werden" - setzen Sie alles in das ListView. ListViewkann mehrere Zeilenstile sowie nicht auswählbare Zeilen haben.
CommonsWare

Antworten:


196

Die Verwendung von a ListView, damit es nicht gescrollt wird, ist extrem teuer und widerspricht dem gesamten Zweck von ListView. Sie sollten dies NICHT tun. Verwenden Sie LinearLayoutstattdessen einfach ein .


69
Die Quelle wäre ich, da ich seit 2 oder 3 Jahren für ListView verantwortlich bin :) ListView leistet eine Menge zusätzlicher Arbeit, um die Verwendung von Adaptern zu optimieren. Wenn Sie versuchen, das Problem zu umgehen, erledigt ListView immer noch viel Arbeit, die ein LinearLayout nicht leisten müsste. Ich werde nicht auf die Details eingehen, da hier nicht genügend Platz vorhanden ist, um die Implementierung von ListView zu erklären. Dies löst auch nicht das Verhalten von ListView in Bezug auf Berührungsereignisse. Es gibt auch keine Garantie dafür, dass Ihr Hack, wenn er heute funktioniert, auch in einer zukünftigen Version funktioniert. Die Verwendung eines LinearLayout wäre nicht wirklich viel Arbeit.
Romain Guy

7
Nun, das ist eine vernünftige Antwort. Wenn ich Feedback geben könnte, hätte ich eine viel bedeutendere iPhone-Erfahrung, und ich kann Ihnen sagen, dass die Dokumentation von Apple in Bezug auf Leistungsmerkmale und Anwendungsfälle (oder Anti-Patterns) wie diese weitaus besser geschrieben ist. Insgesamt ist die Android-Dokumentation weitaus verteilter und weniger fokussiert. Ich verstehe, dass es einige Gründe dafür gibt, aber das ist eine lange Diskussion. Wenn Sie sich also gezwungen fühlen, darüber zu plaudern, lassen Sie es mich wissen.
DougW

6
Sie können leicht eine statische Methode schreiben, die aus einem Adapter ein LinearLayout erstellt.
Romain Guy

125
Dies ist KEINE Antwort für How can I put a ListView into a ScrollView without it collapsing?... Sie können sagen, dass Sie es nicht tun sollten, aber auch eine Antwort geben, wie es geht, das ist eine gute Antwort. Sie wissen, dass eine ListView neben dem Scrollen noch einige andere coole Funktionen bietet, die LinearLayout nicht bietet.
Jorge Fuentes González

44
Was ist, wenn Sie eine Region haben, die eine ListView + andere Ansichten enthält, und Sie möchten, dass alles als eine gescrollt wird? Dies scheint ein vernünftiger Anwendungsfall für das Platzieren einer ListView in einer ScrollView zu sein. Das Ersetzen der ListView durch ein LinearLayout ist keine Lösung, da Sie dann keine Adapter verwenden können.
Barry Fruitman

262

Hier ist meine Lösung. Ich bin ziemlich neu in der Android-Plattform, und ich bin sicher, dass dies ein bisschen hackisch ist, insbesondere in dem Teil, in dem es darum geht, .measure direkt aufzurufen und die LayoutParams.heightEigenschaft direkt festzulegen , aber es funktioniert.

Alles, was Sie tun müssen, ist anzurufen, Utility.setListViewHeightBasedOnChildren(yourListView)und die Größe wird so angepasst, dass sie genau der Höhe der Elemente entspricht.

public class Utility {
    public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();

        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            if (listItem instanceof ViewGroup) {
                listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
             }

             listItem.measure(0, 0);
             totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }
}

66
Sie haben gerade ein sehr teures LinearLayout neu erstellt :)
Romain Guy

82
Außer a LinearLayouthat nicht alle Feinheiten von a ListView- es hat keine Trennwände, Kopf- / Fußzeilenansichten, eine Listenauswahl, die den Farben des UI-Themas des Telefons entspricht usw. Ehrlich gesagt gibt es keinen Grund, warum Sie nicht scrollen können den inneren Container bis zum Ende und lassen Sie dann keine Berührungsereignisse mehr abfangen, sodass der äußere Container einen Bildlauf durchführt. Jeder Desktop-Browser tut dies, wenn Sie Ihr Scrollrad verwenden. Android selbst kann verschachteltes Scrollen verarbeiten, wenn Sie über den Trackball navigieren. Warum also nicht per Touch?
Neil Traft

29
listItem.measure(0,0)löst eine NPE aus, wenn listItem eine ViewGroup-Instanz ist. Ich habe listItem.measureif (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
vorhin

4
Fix für ListViews mit anderen Auffüllungen als 0:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
Paul

8
Leider ist diese Methode etwas fehlerhaft, wenn die ListViewDose Gegenstände mit variabler Höhe enthalten kann. Der Aufruf von gibt getMeasuredHeight()nur die angestrebte Mindesthöhe im Gegensatz zur tatsächlichen Höhe zurück. Ich habe versucht, getHeight()stattdessen zu verwenden, aber dies gibt 0 zurück. Hat jemand einen Einblick, wie man das angeht?
Matt Huggins

89

Dies wird auf jeden Fall Arbeit ............
Sie müssen nur Ihre ersetzen <ScrollView ></ScrollView>in das Layout mit dieser XML - Datei Custom ScrollViewwie <com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >

package com.tmd.utils;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class VerticalScrollview extends ScrollView{

    public VerticalScrollview(Context context) {
        super(context);
    }

     public VerticalScrollview(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_MOVE:
                    return false; // redirect MotionEvents to ourself

            case MotionEvent.ACTION_CANCEL:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
                    super.onTouchEvent(ev);
                    break;

            case MotionEvent.ACTION_UP:
                    Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
                    return false;

            default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
        }

        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent(ev);
        Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
         return true;
    }
}

4
Das ist sehr gut. Es ist auch möglich, ein Google Maps-Fragment in die ScrollView einzufügen, und es funktioniert weiterhin beim Vergrößern / Verkleinern. Vielen Dank.
Magnus Johansson

Hat jemand irgendwelche Nachteile bei diesem Ansatz bemerkt? Es ist so einfach, fürchte ich: o
Marija Milosevic

1
Schöne Lösung, sollte die akzeptierte Antwort +1 sein! Ich habe es mit der Antwort unten (von djunod) gemischt, und das funktioniert großartig!
Tanou

1
Dies ist die einzige Korrektur, die das Scrollen UND KLICKEN auf Kinder ermöglicht, die gleichzeitig für mich arbeiten.
Vielen

25

Wenn wir nicht ListViewin ein setzen ScrollView, können wir es ListViewals verwenden ScrollView. Dinge, die ListViewdrin sein müssen, können in die ListView. Andere Layouts oben und unten ListViewkönnen durch Hinzufügen von Layouts zu Kopf- und Fußzeile von eingefügt werden ListView. Das ListViewGanze gibt Ihnen also eine Erfahrung des Scrollens.


3
Dies ist die eleganteste und angemessenste Antwort imho. Weitere Informationen zu diesem Ansatz finden Sie in dieser Antwort: stackoverflow.com/a/20475821/1221537
adamdport

21

Es gibt viele Situationen, in denen es sehr sinnvoll ist, ListViews in einer ScrollView zu haben.

Hier ist Code, der auf DougWs Vorschlag basiert ... arbeitet in einem Fragment, benötigt weniger Speicher.

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        return;
    }
    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
    int totalHeight = 0;
    View view = null;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        view = listAdapter.getView(i, view, listView);
        if (i == 0) {
            view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, LayoutParams.WRAP_CONTENT));
        }
        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

Rufen Sie setListViewHeightBasedOnChildren (Listenansicht) für jede eingebettete Listenansicht auf.


Es funktioniert nicht, wenn Listenelemente eine variable Größe haben, dh eine Textansicht, die sich über mehrere Zeilen erstreckt. Irgendeine Lösungsmöglichkeit?
Khobaib

Überprüfen Sie meinen Kommentar hier - es gibt die perfekte Lösung - stackoverflow.com/a/23315696/1433187
Khobaib

hat bei mir mit API 19 funktioniert, aber nicht mit API 23: Hier wird viel Leerraum unter der listView hinzugefügt.
Lucker10

17

ListView ist bereits in der Lage, sich selbst so zu messen, dass es groß genug ist, um alle Elemente anzuzeigen. Dies ist jedoch nicht der Fall, wenn Sie einfach wrap_content (MeasureSpec.UNSPECIFIED) angeben. Dies geschieht, wenn mit MeasureSpec.AT_MOST eine Höhe angegeben wird. Mit diesem Wissen können Sie eine sehr einfache Unterklasse erstellen, um dieses Problem zu lösen, die weitaus besser funktioniert als alle oben genannten Lösungen. Sie sollten für diese Unterklasse weiterhin wrap_content verwenden.

public class ListViewForEmbeddingInScrollView extends ListView {
    public ListViewForEmbeddingInScrollView(Context context) {
        super(context);
    }

    public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 4, MeasureSpec.AT_MOST));
    }
}

Wenn Sie die heightMeasureSpec so bearbeiten, dass sie AT_MOST mit einer sehr großen Größe (Integer.MAX_VALUE >> 4) ist, misst die ListView alle untergeordneten Elemente bis zur angegebenen (sehr großen) Höhe und stellt ihre Höhe entsprechend ein.

Dies funktioniert aus mehreren Gründen besser als die anderen Lösungen:

  1. Es misst alles richtig (Polsterung, Trennwände)
  2. Es misst die ListView während des Messdurchlaufs
  3. Aufgrund von # 2 werden Änderungen in der Breite oder Anzahl der Elemente ohne zusätzlichen Code korrekt behandelt

Auf der anderen Seite könnte man argumentieren, dass dies auf undokumentiertem Verhalten im SDK beruht, das sich ändern könnte. Auf der anderen Seite könnten Sie argumentieren, dass wrap_content auf diese Weise wirklich mit ListView funktionieren sollte und dass das aktuelle Verhalten von wrap_content nur fehlerhaft ist.

Wenn Sie befürchten, dass sich das Verhalten in Zukunft ändern könnte, sollten Sie einfach die Funktion onMeasure und die zugehörigen Funktionen aus ListView.java in Ihre eigene Unterklasse kopieren und dann den Pfad AT_MOST durch onMeasure auch für UNSPECIFIED ausführen.

Übrigens glaube ich, dass dies ein absolut gültiger Ansatz ist, wenn Sie mit einer kleinen Anzahl von Listenelementen arbeiten. Es mag im Vergleich zu LinearLayout ineffizient sein, aber wenn die Anzahl der Elemente gering ist, ist die Verwendung von LinearLayout eine unnötige Optimierung und daher unnötige Komplexität.


Funktioniert auch mit Elementen mit variabler Höhe in der Listenansicht!
Denny

@ Jason Y was bedeutet das, Integer.MAX_VALUE >> 4 bedeutet? Was ist 4 da?
KJEjava48

@ Jason Y für mich hat es erst funktioniert, nachdem ich Integer.MAX_VALUE >> 2 in Integer.MAX_VALUE >> 21 geändert habe. Warum ist es so? Es funktioniert auch mit ScrollView, nicht mit NestedScrollView.
KJEjava48

13

Es gibt eine eingebaute Einstellung dafür. In der ScrollView:

android:fillViewport="true"

In Java

mScrollView.setFillViewport(true);

Romain Guy erklärt es hier ausführlich: http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/


2
Das funktioniert für ein LinearLayout, wie er demonstriert. Es funktioniert nicht für eine ListView. Aufgrund des Datums dieses Beitrags stelle ich mir vor, dass er ihn geschrieben hat, um ein Beispiel für seine Alternative zu liefern, aber dies beantwortet die Frage nicht.
DougW

Dies ist eine schnelle Lösung

10

Sie erstellen eine benutzerdefinierte Listenansicht, die nicht scrollbar ist

public class NonScrollListView extends ListView {

    public NonScrollListView(Context context) {
        super(context);
    }
    public NonScrollListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
                    Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = getMeasuredHeight();    
    }
}

In Ihrer Layout-Ressourcendatei

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <!-- com.Example Changed with your Package name -->

    <com.Example.NonScrollListView
        android:id="@+id/lv_nonscroll_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </com.Example.NonScrollListView>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/lv_nonscroll_list" >

        <!-- Your another layout in scroll view -->

    </RelativeLayout>
</RelativeLayout>

In der Java-Datei

Erstellen Sie ein Objekt Ihrer customListview anstelle von ListView wie folgt: NonScrollListView non_scroll_list = (NonScrollListView) findViewById (R.id.lv_nonscroll_list);


8

Wir konnten nicht zwei gleichzeitig scrollen. Wir haben die Gesamtlänge von ListView und erweitern die Listenansicht um die Gesamthöhe. Dann können wir ListView direkt in ScrollView oder mit LinearLayout hinzufügen, da ScrollView direkt ein Kind hat. Kopieren Sie die Methode setListViewHeightBasedOnChildren (lv) in Ihren Code und erweitern Sie die Listenansicht. Anschließend können Sie die Listenansicht in der Bildlaufansicht verwenden. \ layout xml Datei

<?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="match_parent"
android:orientation="vertical" >
 <ScrollView

        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
         android:background="#1D1D1D"
        android:orientation="vertical"
        android:scrollbars="none" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#1D1D1D"
            android:orientation="vertical" >

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="First ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/first_listview"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
               android:listSelector="#ff0000"
                android:scrollbars="none" />

               <TextView
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:background="#333"
                android:gravity="center_vertical"
                android:paddingLeft="8dip"
                android:text="Second ListView"
                android:textColor="#C7C7C7"
                android:textSize="20sp" />

            <ListView
                android:id="@+id/secondList"
                android:layout_width="260dp"
                android:layout_height="wrap_content"
                android:divider="#00000000"
                android:listSelector="#ffcc00"
                android:scrollbars="none" />
  </LinearLayout>
  </ScrollView>

   </LinearLayout>

onCreate-Methode in der Aktivitätsklasse:

 import java.util.ArrayList;
  import android.app.Activity;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
  import android.widget.ListView;

   public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.listview_inside_scrollview);
    ListView list_first=(ListView) findViewById(R.id.first_listview);
    ListView list_second=(ListView) findViewById(R.id.secondList);
    ArrayList<String> list=new ArrayList<String>();
    for(int x=0;x<30;x++)
    {
        list.add("Item "+x);
    }

       ArrayAdapter<String> adapter=new ArrayAdapter<String>(getApplicationContext(), 
          android.R.layout.simple_list_item_1,list);               
      list_first.setAdapter(adapter);

     setListViewHeightBasedOnChildren(list_first);

      list_second.setAdapter(adapter);

    setListViewHeightBasedOnChildren(list_second);
   }



   public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        // pre-condition
        return;
    }

    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
      }

Gute Lösung! Es funktioniert nach Bedarf. Vielen Dank. In meinem Fall habe ich eine ganze Reihe von Ansichten vor der Liste und die perfekte Lösung besteht darin, nur eine vertikale Schriftrolle in der Ansicht zu haben.
Sprichwort

4

Dies ist eine Kombination der Antworten von DougW, Good Guy Greg und Paul. Ich fand, dass alles benötigt wurde, wenn versucht wurde, dies mit einem benutzerdefinierten Listenansichtsadapter und nicht standardmäßigen Listenelementen zu verwenden, da sonst die Listenansicht die Anwendung zum Absturz brachte (auch mit der Antwort von Nex abstürzte):

public void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            return;
        }

        int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            if (listItem instanceof ViewGroup)
                listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }

Wo soll man diese Methode aufrufen?
Dhrupal

Nach dem Erstellen der Listenansicht und dem Einstellen des Adapters.
Verlassener Wagen

Dies wird auf langen Listen zu langsam.
Cakan

@cakan Es wurde auch 2 Jahre zuvor als Problemumgehung geschrieben, um die Verwendung bestimmter UI-Komponenten zusammen zu ermöglichen, die eigentlich nicht kombiniert werden sollen. Sie fügen in modernem Android unnötig viel Overhead hinzu, sodass zu erwarten ist, dass sich die Liste verlangsamt, wenn sie groß wird.
Verlassener Wagen

3

Sie sollten eine ListView nicht in eine ScrollView einfügen, da dies bereits eine ListView ist ein Scroll. Das wäre also so, als würde man eine ScrollView in eine ScrollView einfügen.

Was versuchst du zu erreichen?


4
Ich versuche, eine ListView in einer ScrollView zu haben. Ich möchte nicht, dass meine ListView scrollbar ist, aber es gibt keine einfache Eigenschaft, um myListView.scrollEnabled = false festzulegen.
DougW

Wie Romain Guy sagte, ist dies nicht der Zweck einer ListView. Eine ListView ist eine Ansicht, die für die Anzeige einer langen Liste von Elementen optimiert ist, mit einer Menge komplizierter Logik zum verzögerten Laden von Ansichten, Wiederverwenden von Ansichten usw. Nichts davon ist sinnvoll, wenn Sie der ListView anweisen, nicht zu scrollen. Wenn Sie nur eine Reihe von Elementen in einer vertikalen Spalte haben möchten, verwenden Sie ein LinearLayout.
Cheryl Simon

9
Das Problem ist, dass es eine Reihe von Lösungen gibt, die ListView bietet, einschließlich der von Ihnen erwähnten. Ein Großteil der Funktionen, die Adapter vereinfachen, betrifft die Datenverwaltung und -steuerung, nicht die Optimierung der Benutzeroberfläche. IMO sollte es eine einfache ListView geben, und eine komplexere, die die gesamte Wiederverwendung von Listenelementen und so weiter erledigt.
DougW

2
Absolut einverstanden. Beachten Sie, dass es einfach ist, einen vorhandenen Adapter mit einem LinearLayout zu verwenden, aber ich möchte all die schönen Dinge, die ListView bietet, wie Teiler, und dass ich keine Lust habe, sie manuell zu implementieren.
Artem Russakovskii

1
@ArtemRussakovskii Können Sie bitte erklären, wie Sie ListView auf einfache Weise durch LinearLayout durch einen vorhandenen Adapter ersetzen können?
happyhardik

3

Ich habe @ DougW's Utilityin C # konvertiert (in Xamarin verwendet). Das Folgende funktioniert gut für Elemente mit fester Höhe in der Liste und ist meistens in Ordnung oder zumindest ein guter Anfang, wenn nur einige der Elemente etwas größer als das Standardelement sind.

// You will need to put this Utility class into a code file including various
// libraries, I found that I needed at least System, Linq, Android.Views and 
// Android.Widget.
using System;
using System.Linq;
using Android.Views;
using Android.Widget;

namespace UtilityNamespace  // whatever you like, obviously!
{
    public class Utility
    {
        public static void setListViewHeightBasedOnChildren (ListView listView)
        {
            if (listView.Adapter == null) {
                // pre-condition
                return;
            }

            int totalHeight = listView.PaddingTop + listView.PaddingBottom;
            for (int i = 0; i < listView.Count; i++) {
                View listItem = listView.Adapter.GetView (i, null, listView);
                if (listItem.GetType () == typeof(ViewGroup)) {
                    listItem.LayoutParameters = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
                }
                listItem.Measure (0, 0);
                totalHeight += listItem.MeasuredHeight;
            }

            listView.LayoutParameters.Height = totalHeight + (listView.DividerHeight * (listView.Count - 1));
        }
    }
}

Danke @DougW, das hat mich aus einer schwierigen Situation gebracht, als ich mit OtherPeople'sCode arbeiten musste. :-)


Eine Frage: Wann haben Sie die setListViewHeightBasedOnChildren()Methode aufgerufen ? Weil es bei mir nicht immer funktioniert.
Alexandre D.

@AlexandreD Sie rufen das Utility.setListViewHeightBasedOnChildren (yourListView) auf, wenn Sie die Liste der Elemente erstellt haben. dh stellen Sie sicher, dass Sie zuerst Ihre Liste erstellt haben! Ich hatte festgestellt, dass ich eine Liste erstellte und meine Elemente sogar in Console.WriteLine anzeigte ... aber irgendwie befanden sich die Elemente im falschen Bereich. Nachdem ich das herausgefunden und sichergestellt hatte, dass ich den richtigen Umfang für meine Liste hatte, funktionierte dies wie ein Zauber.
Phil Ryan

Tatsache ist, dass meine Listen in meinem ViewModel erstellt werden. Wie kann ich warten, bis meine Listen in meiner Ansicht erstellt wurden, bevor ich Utility.setListViewHeightBasedOnChildren (yourListView) aufrufe?
Alexandre D.

3

Dies ist das einzige, was für mich funktioniert hat:

ab Lollipop können Sie verwenden

yourtListView.setNestedScrollingEnabled(true);

Wenn Sie das verschachtelte Scrollen für diese Ansicht aktivieren oder deaktivieren, müssen Sie die RecyclerView verwenden, wenn Sie eine Abwärtskompatibilität mit einer älteren Version des Betriebssystems benötigen.


Dies hatte keine Auswirkungen auf meine Listenansichten zu API 22 in einem DialogFragment in einer AppCompat-Aktivität. Sie brechen immer noch zusammen. Die Antwort von @Phil Ryan funktionierte mit ein wenig Modifikation.
Hakan Çelik MK1

3

Vorher war es nicht möglich. Mit der Veröffentlichung neuer Appcompat-Bibliotheken und Design-Bibliotheken kann dies jedoch erreicht werden.

Sie müssen nur NestedScrollView verwenden https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html verwenden

Ich weiß nicht, ob es mit Listview funktioniert oder nicht, aber mit RecyclerView.

Code-Auszug:

<android.support.v4.widget.NestedScrollView 
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</android.support.v4.widget.NestedScrollView>

Cool, ich habe es nicht ausprobiert, daher kann ich nicht bestätigen, dass eine interne ListView nicht zusammenbricht, aber es sieht so aus, als ob dies die Absicht sein könnte. Tief traurig, Romain Guy nicht im
Schuldprotokoll

1
Dies funktioniert mit recyclerView, jedoch nicht mit listView. Thanx
IgniteCoders

2

Hey, ich hatte ein ähnliches Problem. Ich wollte eine Listenansicht anzeigen, die nicht gescrollt wurde, und stellte fest, dass das Bearbeiten der Parameter funktioniert, aber ineffizient ist und sich auf verschiedenen Geräten unterschiedlich verhält. Daher ist dies ein Teil meines Zeitplancodes, der dies tatsächlich sehr effizient ausführt .

db = new dbhelper(this);

 cursor = db.dbCursor();
int count = cursor.getCount();
if (count > 0)
{    
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.layoutId);
startManagingCursor(YOUR_CURSOR);

YOUR_ADAPTER(**or SimpleCursorAdapter **) adapter = new YOUR_ADAPTER(this,
    R.layout.itemLayout, cursor, arrayOrWhatever, R.id.textViewId,
    this.getApplication());

int i;
for (i = 0; i < count; i++){
  View listItem = adapter.getView(i,null,null);
  linearLayout.addView(listItem);
   }
}

Hinweis: Wenn Sie dies verwenden, notifyDataSetChanged();funktioniert es nicht wie beabsichtigt, da die Ansichten nicht neu gezeichnet werden. Tun Sie dies, wenn Sie eine Lösung benötigen

adapter.registerDataSetObserver(new DataSetObserver() {

            @Override
            public void onChanged() {
                super.onChanged();
                removeAndRedrawViews();

            }

        });

2

Bei der Verwendung einer ListView in einer ScrollView treten zwei Probleme auf.

1- ListView muss vollständig auf die untergeordnete Größe erweitert werden. Diese ListView löst dieses Problem:

public class ListViewExpanded extends ListView
{
    public ListViewExpanded(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        setDividerHeight(0);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST));
    }
}

Die Teilerhöhe muss 0 sein. Verwenden Sie stattdessen eine Auffüllung in Zeilen.

2- Die ListView verwendet Berührungsereignisse, sodass ScrollView nicht wie gewohnt gescrollt werden kann. Diese ScrollView behebt dieses Problem:

public class ScrollViewInterceptor extends ScrollView
{
    float startY;

    public ScrollViewInterceptor(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e)
    {
        onTouchEvent(e);
        if (e.getAction() == MotionEvent.ACTION_DOWN) startY = e.getY();
        return (e.getAction() == MotionEvent.ACTION_MOVE) && (Math.abs(startY - e.getY()) > 50);
    }
}

Dies ist der beste Weg, den ich gefunden habe, um den Trick zu machen!


2

Eine Lösung, die ich verwende, besteht darin, den gesamten Inhalt der ScrollView (was über und unter der listView sein sollte) als headerView und footerView in der ListView hinzuzufügen.

So funktioniert es wie, auch die Konvertierungsansicht wird so geändert, wie sie sein sollte.


1

Dank Vinays Code ist hier mein Code, wenn Sie keine Listenansicht in einer Bildlaufansicht haben können, aber so etwas brauchen

LayoutInflater li = LayoutInflater.from(this);

                RelativeLayout parent = (RelativeLayout) this.findViewById(R.id.relativeLayoutCliente);

                int recent = 0;

                for(Contatto contatto : contatti)
                {
                    View inflated_layout = li.inflate(R.layout.header_listview_contatti, layout, false);


                    inflated_layout.setId(contatto.getId());
                    ((TextView)inflated_layout.findViewById(R.id.textViewDescrizione)).setText(contatto.getDescrizione());
                    ((TextView)inflated_layout.findViewById(R.id.textViewIndirizzo)).setText(contatto.getIndirizzo());
                    ((TextView)inflated_layout.findViewById(R.id.textViewTelefono)).setText(contatto.getTelefono());
                    ((TextView)inflated_layout.findViewById(R.id.textViewMobile)).setText(contatto.getMobile());
                    ((TextView)inflated_layout.findViewById(R.id.textViewFax)).setText(contatto.getFax());
                    ((TextView)inflated_layout.findViewById(R.id.textViewEmail)).setText(contatto.getEmail());



                    RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

                    if (recent == 0)
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, R.id.headerListViewContatti);
                    }
                    else
                    {
                        relativeParams.addRule(RelativeLayout.BELOW, recent);
                    }
                    recent = inflated_layout.getId();

                    inflated_layout.setLayoutParams(relativeParams);
                    //inflated_layout.setLayoutParams( new RelativeLayout.LayoutParams(source));

                    parent.addView(inflated_layout);
                }

Das relativeLayout bleibt in einer ScrollView, sodass alles scrollbar wird :)


1

Hier ist kleine Änderung auf @djunod ‚s Antwort , die ich brauche , um es perfekt funktioniert:

public static void setListViewHeightBasedOnChildren(ListView listView)
{
    ListAdapter listAdapter = listView.getAdapter();
    if(listAdapter == null) return;
    if(listAdapter.getCount() <= 1) return;

    int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
    int totalHeight = 0;
    View view = null;
    for(int i = 0; i < listAdapter.getCount(); i++)
    {
        view = listAdapter.getView(i, view, listView);
        view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
        totalHeight += view.getMeasuredHeight();
    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

: Hallo, Ihre Antwort funktioniert meistens, aber nicht, wenn die Listenansicht eine Kopf- und eine Fußansicht hat. kannst du es checken?
hguser

Ich brauche eine Lösung, wenn Listenelemente eine variable Größe haben, dh eine Textansicht, die sich über mehrere Zeilen erstreckt. Irgendein Vorschlag?
Khobaib

1

versuche das, das funktioniert bei mir, ich habe vergessen, wo ich es gefunden habe, irgendwo im Stapelüberlauf, ich bin nicht hier, um zu erklären, warum es nicht funktioniert, aber das ist die Antwort :).

    final ListView AturIsiPulsaDataIsiPulsa = (ListView) findViewById(R.id.listAturIsiPulsaDataIsiPulsa);
    AturIsiPulsaDataIsiPulsa.setOnTouchListener(new ListView.OnTouchListener() 
    {
        @Override
        public boolean onTouch(View v, MotionEvent event) 
        {
            int action = event.getAction();
            switch (action) 
            {
                case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(true);
                break;

                case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                v.getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }

            // Handle ListView touch events.
            v.onTouchEvent(event);
            return true;
        }
    });
    AturIsiPulsaDataIsiPulsa.setClickable(true);
    AturIsiPulsaDataIsiPulsa.setAdapter(AturIsiPulsaDataIsiPulsaAdapter);

EDIT !, Ich habe endlich herausgefunden, woher ich den Code habe. Hier ! : ListView in ScrollView scrollt unter Android nicht


Hier erfahren Sie, wie Sie verhindern können, dass die Interaktion mit der Bildlaufansicht verloren geht. Es ging jedoch darum, die Höhe der Listenansicht beizubehalten.
Verlassener Wagen

1

Obwohl die vorgeschlagenen setListViewHeightBasedOnChildren () -Methoden in den meisten Fällen funktionieren, habe ich in einigen Fällen, insbesondere bei vielen Elementen, festgestellt, dass die letzten Elemente nicht angezeigt werden. Daher habe ich beschlossen, eine einfache Version des ListView- Verhaltens nachzuahmen, um Adaptercode wiederzuverwenden. Hier ist die ListView-Alternative:

import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListAdapter;

public class StretchedListView extends LinearLayout {

private final DataSetObserver dataSetObserver;
private ListAdapter adapter;
private OnItemClickListener onItemClickListener;

public StretchedListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setOrientation(LinearLayout.VERTICAL);
    this.dataSetObserver = new DataSetObserver() {
        @Override
        public void onChanged() {
            syncDataFromAdapter();
            super.onChanged();
        }

        @Override
        public void onInvalidated() {
            syncDataFromAdapter();
            super.onInvalidated();
        }
    };
}

public void setAdapter(ListAdapter adapter) {
    ensureDataSetObserverIsUnregistered();

    this.adapter = adapter;
    if (this.adapter != null) {
        this.adapter.registerDataSetObserver(dataSetObserver);
    }
    syncDataFromAdapter();
}

protected void ensureDataSetObserverIsUnregistered() {
    if (this.adapter != null) {
        this.adapter.unregisterDataSetObserver(dataSetObserver);
    }
}

public Object getItemAtPosition(int position) {
    return adapter != null ? adapter.getItem(position) : null;
}

public void setSelection(int i) {
    getChildAt(i).setSelected(true);
}

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    this.onItemClickListener = onItemClickListener;
}

public ListAdapter getAdapter() {
    return adapter;
}

public int getCount() {
    return adapter != null ? adapter.getCount() : 0;
}

private void syncDataFromAdapter() {
    removeAllViews();
    if (adapter != null) {
        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View view = adapter.getView(i, null, this);
            boolean enabled = adapter.isEnabled(i);
            if (enabled) {
                final int position = i;
                final long id = adapter.getItemId(position);
                view.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        if (onItemClickListener != null) {
                            onItemClickListener.onItemClick(null, v, position, id);
                        }
                    }
                });
            }
            addView(view);

        }
    }
}
}

1

Alle diese Antworten sind falsch !!! Wenn Sie versuchen, eine Listenansicht in eine Bildlaufansicht einzufügen, sollten Sie Ihr Design überdenken. Sie versuchen, eine ScrollView in eine ScrollView einzufügen. Ein Eingriff in die Liste beeinträchtigt die Leistung der Liste. Es wurde von Android so entworfen.

Wenn Sie wirklich möchten, dass sich die Liste im selben Bildlauf wie die anderen Elemente befindet, müssen Sie nur die anderen Elemente mit einer einfachen switch-Anweisung in Ihrem Adapter oben in die Liste einfügen:

class MyAdapter extends ArrayAdapter{

    public MyAdapter(Context context, int resource, List objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
         ViewItem viewType = getItem(position);

        switch(viewType.type){
            case TEXTVIEW:
                convertView = layouteInflater.inflate(R.layout.textView1, parent, false);

                break;
            case LISTITEM:
                convertView = layouteInflater.inflate(R.layout.listItem, parent, false);

                break;            }


        return convertView;
    }


}

Der Listenadapter kann alles verarbeiten, da er nur das Renderbare wiedergibt.


0

Dieses ganze Problem würde einfach verschwinden, wenn LinearLayout eine setAdapter-Methode hätte, denn wenn Sie dann jemandem sagten, er solle sie verwenden, wäre die Alternative trivial.

Wenn Sie tatsächlich eine scrollende ListView in einer anderen scrollenden Ansicht wünschen, hilft dies nicht, aber ansonsten gibt Ihnen dies zumindest eine Idee.

Sie müssen einen benutzerdefinierten Adapter erstellen, um den gesamten Inhalt zu kombinieren, über den Sie scrollen möchten, und den ListView-Adapter darauf einstellen.

Ich habe keinen Beispielcode zur Hand, aber wenn Sie so etwas wollen.

<ListView/>

(other content)

<ListView/>

Dann müssen Sie einen Adapter erstellen, der den gesamten Inhalt darstellt. Die ListView / Adapter sind intelligent genug, um auch verschiedene Typen zu verarbeiten, aber Sie müssen den Adapter selbst schreiben.

Die Android UI API ist einfach nicht so ausgereift wie so ziemlich alles andere da draußen, daher hat sie nicht die gleichen Feinheiten wie andere Plattformen. Wenn Sie etwas auf Android tun, müssen Sie sich in einer Android (Unix) -Mentalität befinden, in der Sie erwarten, dass Sie, um etwas zu tun, wahrscheinlich die Funktionalität kleinerer Teile zusammenstellen und eine Menge Ihres eigenen Codes schreiben müssen, um es zu erreichen Arbeit.


0

Wenn wir ListViewhineinlegen ScrollView, entstehen zwei Probleme. Einer ScrollViewmisst seine Kinder im UNSPECIFIED-Modus, ListViewstellt also seine eigene Höhe ein, um nur einen Gegenstand aufzunehmen (ich weiß nicht warum), ein anderer ScrollViewfängt das Berührungsereignis so abListView scrollt nicht.

Aber wir können Platz ListViewinnen ScrollViewmit etwas Abhilfe. Dieser Beitrag von mir erklärt die Problemumgehung. Durch diese Problemumgehung können wir auch die ListViewRecyclingfunktion beibehalten .


0

Anstatt die Listenansicht in die Bildlaufansicht einzufügen, platzieren Sie den Rest des Inhalts zwischen der Listenansicht und dem Öffnen der Bildlaufansicht als separate Ansicht und legen Sie diese Ansicht als Kopfzeile der Listenansicht fest. So erhalten Sie schließlich nur die Listenansicht, die Scroll übernimmt.



-1

Hier ist meine Version des Codes, der die Gesamthöhe der Listenansicht berechnet. Dieser funktioniert für mich:

   public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null || listAdapter.getCount() < 2) {
        // pre-condition
        return;
    }

    int totalHeight = 0;
    int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(BCTDApp.getDisplaySize().width, View.MeasureSpec.AT_MOST);
    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        if (listItem instanceof ViewGroup) listItem.setLayoutParams(lp);
        listItem.measure(widthMeasureSpec, heightMeasureSpec);
        totalHeight += listItem.getMeasuredHeight();
    }

    totalHeight += listView.getPaddingTop() + listView.getPaddingBottom();
    totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight;
    listView.setLayoutParams(params);
    listView.requestLayout();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.