Как я могу поместить ListView в ScrollView без его разрушения? - PullRequest
345 голосов
/ 16 августа 2010

Я искал решения этой проблемы, и единственный ответ, который я могу найти, - « не помещайте ListView в ScrollView ». Я до сих пор не вижу реального объяснения почему . Кажется, единственная причина, по которой я могу найти это, заключается в том, что Google не считает, что вам следует этого делать. Ну, я так и сделал.

Итак, вопрос в том, как поместить ListView в ScrollView, не сворачивая его до минимальной высоты?

Ответы [ 27 ]

258 голосов
/ 16 августа 2010

Вот мое решение. Я довольно новичок в платформе Android, и я уверен, что это немного глупо, особенно в части, касающейся прямого вызова .measure и прямой установки свойства LayoutParams.height, но это работает.

Все, что вам нужно сделать, это позвонить по номеру Utility.setListViewHeightBasedOnChildren(yourListView), и его размер будет изменен, чтобы точно соответствовать высоте его предметов.

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);
    }
}
193 голосов
/ 16 августа 2010

Использование ListView для предотвращения прокрутки очень дорого и противоречит цели ListView.Вы должны НЕ сделать это.Просто используйте LinearLayout вместо.

89 голосов
/ 10 декабря 2012

Это определенно сработает ............
Вам нужно просто заменить ваш <ScrollView ></ScrollView> в XML-файле макета на этот Custom ScrollView как <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;
    }
}
24 голосов
/ 31 декабря 2012

Если положить ListView внутрь ScrollView, мы можем использовать ListView как ScrollView.Вещи, которые должны быть в ListView, могут быть помещены в ListView.Другие макеты сверху и снизу ListView могут быть размещены путем добавления макетов в верхний и нижний колонтитулы ListView.Таким образом, весь ListView даст вам опыт прокрутки.

21 голосов
/ 20 июля 2013

Есть множество ситуаций, в которых имеет смысл иметь ListView в ScrollView.

Вот код, основанный на предложении Дугва ... работает фрагментом, занимает меньшепамять.

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();
}

вызовите setListViewHeightBasedOnChildren (просмотр списка) для каждого встроенного просмотра списка.

17 голосов
/ 17 апреля 2015

ListView на самом деле уже способен измерить себя, чтобы быть достаточно высоким, чтобы отобразить все элементы, но он этого не делает, когда вы просто указываете wrap_content (MeasureSpec.UNSPECIFIED). Это будет сделано, когда задана высота с MeasureSpec.AT_MOST. Обладая этими знаниями, вы можете создать очень простой подкласс для решения этой проблемы, который работает намного лучше, чем любое из решений, опубликованных выше. Вы все еще должны использовать wrap_content с этим подклассом.

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));
    }
}

Манипулирование heightMeasureSpec для AT_MOST с очень большим размером (Integer.MAX_VALUE >> 4) заставляет ListView измерить всех своих дочерних элементов до заданной (очень большой) высоты и соответственно установить его высоту.

Это работает лучше, чем другие решения по нескольким причинам:

  1. Он все правильно измеряет (отступы, делители)
  2. Измеряет ListView во время прохода меры
  3. Благодаря # 2 он корректно обрабатывает изменения ширины или количества элементов без дополнительного кода

С другой стороны, вы можете утверждать, что это зависит от недокументированного поведения в SDK, которое может измениться. С другой стороны, вы можете утверждать, что именно так wrap_content должен действительно работать с ListView и что текущее поведение wrap_content просто нарушено.

Если вы беспокоитесь о том, что поведение может измениться в будущем, вам просто нужно скопировать функцию onMeasure и связанные с ней функции из ListView.java в свой собственный подкласс, а затем запустить путь AT_MOST через onMeasure для UNSPECIFIED. .

Кстати, я считаю, что это совершенно правильный подход, когда вы работаете с небольшим количеством элементов списка. Это может быть неэффективно по сравнению с LinearLayout, но когда количество элементов мало, использование LinearLayout является ненужной оптимизацией и, следовательно, ненужной сложностью.

12 голосов
/ 04 августа 2014

Для него есть встроенная настройка.На ScrollView:

android:fillViewport="true"

В Java

mScrollView.setFillViewport(true);

Ромэн Гай подробно объясняет это здесь: http://www.curious -creature.org / 2010/08/15 /scrollviews-удобно-трик /

8 голосов
/ 12 июля 2014

Мы не можем использовать две прокрутки одновременно. У нас будет общая длина ListView и развернутый просмотр списка с общей высотой. Затем мы можем добавить ListView в ScrollView напрямую или с помощью LinearLayout, потому что у ScrollView непосредственно один дочерний элемент.скопируйте метод setListViewHeightBasedOnChildren (lv) в свой код и разверните просмотр списка, после чего вы можете использовать просмотр списка внутри scrollview.\ layout xml file

<?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 метод в классе Activity:

 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);
      }
8 голосов
/ 24 мая 2016

Вы создаете пользовательский ListView, который не прокручивается

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();    
    }
}

В вашем файле ресурсов макета

<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>

В файле Java

Создайте объект вашего customListview вместо ListView, например: NonScrollListView non_scroll_list = (NonScrollListView) findViewById (R.id.lv_nonscroll_list);

4 голосов
/ 04 августа 2013

Это комбинация ответов Дуга, Гая Грега и Пола. Я обнаружил, что все это было необходимо при попытке использовать это с настраиваемым адаптером списка и нестандартными элементами списка, в противном случае просмотр списка приводил к сбою приложения (также с ошибкой при ответе Nex):

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);
    }
...