Як я можу помістити ListView у ScrollView без його згортання?


351

Я шукав рішення цієї проблеми, і єдина відповідь, яку я можу знайти, здається, " не вставляйте ListView у ScrollView ". Я ще не бачив реального пояснення, чому все-таки. Єдиною причиною, яку я можу вважати, є те, що Google не вважає, що ти повинен цього робити. Ну я так, так і зробив.

Отже, питання полягає в тому, як можна розмістити ListView у ScrollView, не згортаючи його до мінімальної висоти?


11
Тому що, коли пристрій використовує сенсорний екран, немає хорошого способу розмежування двох вкладених прокручуваних контейнерів. Він працює на традиційних робочих столах, де ви використовуєте смугу прокрутки для прокрутки контейнера.
Ромен Гай

57
Звичайно, є. Якщо вони торкаються всередині внутрішнього, прокрутіть це. Якщо внутрішня частина занадто велика, це поганий дизайн інтерфейсу. Це не означає, що це взагалі погана ідея.
DougW

5
Просто натрапив на це для свого додатка Планшет. Хоча на смартфоні це не надто гірше, на планшеті це жахливо, і ви легко можете вирішити, чи хоче користувач прокручувати зовнішній або внутрішній ScrollView. @DougW: Жахливий дизайн, я згоден.
медуза

4
@Romain - це досить стара публікація, тому мені цікаво, чи вже тут були вирішені проблеми, чи все ще немає хорошого способу використання ListView всередині ScrollView (або альтернативи, яка не передбачає кодування вручну всі тонкощі ListView у LinearLayout)?
Джим

3
@Jim: "або альтернатива, яка не передбачає кодування всіх рудностей ListView в LinearLayout вручну", - покладіть все всередині ListView. ListViewможе мати кілька стилів рядків плюс рядки, які не можна вибрати
CommonsWare

Відповіді:


196

Використання A, ListViewщоб не прокручувати, є надзвичайно дорогим і суперечить цілі цілі ListView. ЦЕ НЕ слід робити цього. Просто використовуйте LinearLayoutзамість цього.


69
Джерелом був би я, оскільки я відповідав за ListView останні 2 або 3 роки :) ListView робить багато додаткової роботи для оптимізації використання адаптерів. Спроба обійти це все ще змусить ListView зробити багато роботи, з якою LinearLayout не повинен був би робити. Я не буду вникати в деталі, оскільки тут недостатньо місця для пояснення реалізації ListView. Це також не вирішить поведінку ListView щодо подій, що торкаються. Також немає гарантії, що якщо ваш хак працює сьогодні, він все ще працюватиме в майбутньому випуску. Використання LinearLayout не мало би великої роботи.
Ромен Гай

7
Ну це розумна відповідь. Якщо я можу поділитися деякими відгуками, у мене набагато важливіший досвід iPhone, і я можу вам сказати, що документація Apple набагато краще написана щодо характеристик продуктивності та таких випадків використання (або анти-шаблонів). В цілому документація на Android набагато більш розповсюджена і менш зосереджена. Я розумію, що на це є деякі причини, але це тривала дискусія, тому якщо ви відчуваєте вимушеність спілкуватися про це, дайте мені знати.
DougW

6
Ви можете легко написати статичний метод, який створює LinearLayout з адаптера.
Ромен Гай

125
Це НЕ відповідь на How can I put a ListView into a ScrollView without it collapsing?... Ви можете сказати, що ви не повинні цього робити, але і дати відповідь, як це зробити, це хороша відповідь. Ви знаєте, що ListView має деякі інші цікаві функції, окрім прокрутки, функції, які LinearLayout не мають.
Хорхе Фуентес Гонсалес

44
Що робити, якщо у вас є регіон, що містить ListView + інші перегляди, і ви хочете, щоб усе прокручувалося як одне? Це здається розумним випадком використання для розміщення ListView у ScrollView. Заміна ListView на LinearLayout не є рішенням, оскільки тоді ви не можете використовувати Адаптери.
Barry Fruitman

262

Ось моє рішення. Я досить новачок у платформі 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);
    }
}

66
Ви тільки що відтворили дуже дорогий LinearLayout :)
Ромен Гай

82
За винятком того, що у LinearLayoutнього немає всіх притаманних елементів ListView- у ньому немає роздільників, перегляду заголовка / колонтитула, вибору списку, який відповідає кольорам теми інтерфейсу телефону тощо. Чесно кажучи, немає причини, чому ви не можете прокручувати внутрішній контейнер, поки він не досягне кінця, а потім перестав перехоплювати події дотику, щоб зовнішній контейнер прокручувався. Кожен браузер на робочому столі робить це під час використання колеса прокрутки. Сам Android може обробляти вкладені прокрутки, якщо ви переходите через трекбол, так чому б не через дотик?
Ніл Трафт

29
listItem.measure(0,0)буде кидати NPE, якщо listItem є екземпляром ViewGroup. Я раніше додав таке listItem.measure:if (listItem instanceof ViewGroup) listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Siklab.ph

4
Виправлення для ListViews за допомогою прокладки, окрім 0:int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
Павло

8
На жаль, цей метод дещо хибний, коли ListViewможе містити елементи різної висоти. Заклик getMeasuredHeight()лише повертає орієнтовану мінімальну висоту на відміну від фактичної висоти. Я спробував використати getHeight()натомість, але це повертає 0. Чи має хтось уявлення про те, як підійти до цього?
Метт Хаггінс

89

Це обов'язково спрацює ............
Ви повинні просто замінити свій <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;
    }
}

4
Це дуже добре. Це також дозволяє розмістити фрагмент Карт Google всередині ScrollView, і він все ще працює із збільшенням / зменшенням масштабу. Дякую.
Магнус Йоханссон

Хтось помітив якісь мінуси у такому підході? Боюсь, так просто: o
Марія Мілошевич

1
Гарне рішення, має бути прийнята відповідь +1! Я змішав це з відповіддю нижче (від djunod), і це чудово працює!
tanou

1
Це єдине виправлення, яке змушує прокручувати і клацати на дітях, які працюють одночасно для мене. Велика подяка
pellyadolfo

25

Введений ListViewусередину ScrollView, ми можемо використовувати ListViewяк ScrollView. Речі, які повинні бути, ListViewможна помістити всередину ListView. Інші макети вгорі та внизу ListViewможна розмістити, додавши макети в колонтитул та колонтитул ListView. Тож цілий ListViewдасть вам досвід прокрутки.


3
Це найелегантніша і відповідніша відповідь. Більш докладної інформації про цей підхід см цієї відповіді: stackoverflow.com/a/20475821/1221537
adamdport

21

Існує безліч ситуацій, коли має сенс мати ListView у ScrollView.

Ось код на основі пропозиції ДугW ... працює в фрагменті, займає менше пам’яті.

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 (перегляд списку) для кожного вбудованого перегляду списку.


Він не працюватиме, коли елементи списку мають різний розмір, тобто деякий перегляд тексту, який розгортається на кілька рядків. Будь-яке рішення?
Хобайб

Перевірте мій коментар тут - це дає ідеальне рішення - stackoverflow.com/a/23315696/1433187
Khobaib

працював для мене з API 19, але не з API 23: Тут додається багато білого простору під listView.
Lucker10

17

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

Маніпулювання висотоюMeasureSpec 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 є непотрібною оптимізацією і, отже, зайвою складністю.


Також працює з елементами змінної висоти в списку перегляду!
Денні

@Jason Y що це означає, Integer.MAX_VALUE >> 4 означає ?? що там 4 ??
KJEjava48

@Jason Y для мене він працював лише після того, як я змінив Integer.MAX_VALUE >> 2 на Integer.MAX_VALUE >> 21. Чому так? Також працює з ScrollView, а не з NestedScrollView.
KJEjava48

13

Для цього є вбудована настройка. На ScrollView:

android:fillViewport="true"

На Java

mScrollView.setFillViewport(true);

Ромен Гай пояснює це глибоко тут: http://www.curious-creature.org/2010/08/15/scrollviews-handy-trick/


2
Це працює для LinearLayout, як він демонструє. Це не працює для ListView. Виходячи з дати цієї посади, я думаю, що він написав це, щоб надати приклад своєї альтернативи, але це не відповідає на питання.
DougW

це швидке рішення

10

Ви створюєте користувацький 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);


8

Ми не могли використати два прокрутки simulteniuosly. У нас буде загальна довжина ListView та розширення перегляду списку на загальну висоту. Скопіюйте метод setListViewHeightBasedOnChildren (lv) у свій код та розгорніть listview, тоді ви можете використовувати listview всередині прокрутки. \ макет xml-файлу

<?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 у класі активності:

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

Гарне виправлення! Він працює за потребою. Дякую. У моєму випадку я маю купу переглядів перед списком, і ідеальним рішенням буде мати лише один вертикальний прокручування на поданні.
Proverbio

4

Це поєднання відповідей Дугва, Гарного Гая Грега та Пола. Я виявив, що це все потрібно при спробі використовувати це з користувацьким адаптером listview та нестандартними елементами списку, інакше listview перервав додаток (також зазнав аварії з відповіддю 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);
    }

Куди закликати цей метод?
Dhrupal

Після створення списку перегляду та встановлення адаптера.
Покинутий кошик

Це стає занадто повільним у довгих списках.
cakan

@cakan Також було написано на 2 роки раніше як вирішення, щоб дозволити разом використовувати певні компоненти інтерфейсу, які насправді не повинні поєднуватися. Ви додаєте непотрібну кількість накладних витрат у сучасному Android, тому слід очікувати, що список уповільниться, коли він стане великим.
Покинутий кошик

3

Не слід ставити ListView у ScrollView, оскільки ListView вже є ScrollView. Отже, це було б як розміщення ScrollView в ScrollView.

Що ви намагаєтеся досягти?


4
Я намагаюся досягти, щоб список ListView всередині ScrollView. Я не хочу, щоб мій ListView прокручувався, але немає простого властивості, щоб встановити myListView.scrollEnabled = false;
DougW

Як сказав Ромен Гай, це дуже не є метою ListView. ListView - це перегляд, оптимізований для відображення довгого списку елементів, з великою кількістю складної логіки, щоб зробити ледаче завантаження переглядів, повторне використання переглядів тощо. Нічого цього не має сенсу, якщо ви скажете ListView не прокручувати. Якщо ви просто хочете купу елементів у вертикальній колонці, використовуйте LinearLayout.
Шеріл Саймон

9
Проблема полягає в тому, що існує ряд рішень, які пропонує ListView, включаючи те, що ви згадуєте. Більшість функцій, які адаптери адаптують, хоча стосуються управління та контролю даних, а не оптимізації користувальницького інтерфейсу. IMO там повинен був бути простим ListView і більш складним, який робить все повторне використання та інше.
DougW

2
Абсолютно погоджений. Зауважте, що легко використовувати існуючий адаптер з LinearLayout, але я хочу всіх приємних речей, які надає ListView, як роздільники, і що я не відчуваю, як реалізувати вручну.
Артем Русаковський

1
@ArtemRussakovskii Чи можете ви пояснити свій простий спосіб заміни ListView на LinearLayout наявним адаптером?
happyhardik

3

Я перетворив @ DougW's Utilityв C # (використовується в Xamarin). Далі добре працює для елементів фіксованої висоти у списку, і це буде здебільшого добре, або, принаймні, вдалий початок, якщо лише деякі елементи трохи більше, ніж стандартні.

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

Дякую @DougW, це змусило мене вийти з місця, коли мені довелося працювати з OtherPeople'sCode. :-)


Одне питання: Коли ви викликали setListViewHeightBasedOnChildren()метод? Тому що це не працює для мене весь час.
Олександр Д.

@AlexandreD ви викликаєте Utility.setListViewHeightBasedOnChildren (yourListView), коли ви створили список елементів. тобто переконайтеся, що ви створили свій список спочатку! Я виявив, що я роблю список, і я навіть відображав свої елементи в Console.WriteLine ... але якимось чином елементи були не в тій мірі. Після того, як я це зрозумів і переконався, що маю правильний обсяг для свого списку, це спрацювало як принадність.
Філ Райан

Справа в тому, що мої списки створені в моєму ViewModel. Як я можу чекати, коли мої списки будуть створені в моєму вікні перед будь-яким викликом до Utility.setListViewHeightBasedOnChildren (yourListView)?
Олександр Д.

3

Це єдине, що працювало для мене:

на Lollipop далі ви можете використовувати

yourtListView.setNestedScrollingEnabled(true);

Це вмикає або вимикає вкладені прокрутки для цього виду, якщо вам потрібна зворотна сумісність із старішою версією ОС, вам доведеться використовувати RecyclerView.


Це не вплинуло на перегляди мого списку щодо API 22 в діалоговому фрагменті в діяльності AppCompat. Вони все ще руйнуються. @Phil Райана з відповіддю працював з трохи модифікації.
Хакан Шелік MK1

3

До цього не було можливості. Але з випуском нових бібліотек Appcompat та бібліотек дизайну цього можна досягти.

Вам просто потрібно використовувати NestedScrollView https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

Я не знаю, буде працювати з Listview чи ні, але працює з RecyclerView.

Фрагмент коду:

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

Класно, я не пробував цього, тому не можу підтвердити, що внутрішній ListView не руйнується, але схоже, що це може бути наміром. Глибоко сумуючи, що не бачив Ромена Гая в журналі вини;)
DougW

1
Це працює з recilerView, але не listView. Thanx
IgniteCoders

2

ей, у мене було подібне питання. Я хотів відобразити подання списку, яке не прокручувалося, і я виявив, що маніпулювання параметрами працювало, але було неефективним і поводилося б по-різному на різних пристроях. Як результат, це частина мого коду розкладу, що насправді робить це дуже ефективно .

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

Примітка: якщо ви користуєтесь цим, notifyDataSetChanged();він не працюватиме за призначенням, оскільки представлення не будуть перероблені. Зробіть це, якщо вам потрібна робота навколо

adapter.registerDataSetObserver(new DataSetObserver() {

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

            }

        });

2

При використанні ListView всередині ScrollView є дві проблеми.

1- ListView повинен повністю розширитися до висоти своїх дітей. цей ListView вирішує це:

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

Висота дільника повинна бути 0, замість цього використовуйте набивання рядками.

2- ListView споживає сенсорні події, тому ScrollView не можна прокручувати, як зазвичай. Цей ScrollView вирішує цю проблему:

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

Це найкращий спосіб, який я знайшов зробити трюк!


2

Я використовую рішення, щоб додати весь вміст ScrollView (що має бути вище та під списком listView) як headerView та footerView у ListView.

Так воно працює, як і перегляд перетворень, як це має бути.


1

завдяки коду Vinay ось мій код, коли у вас не може бути перегляду списку всередині прокрутки, але вам потрібно щось подібне

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

RelaLayout залишається всередині ScrollView, щоб він став прокручуватися :)


1

Ось невелика модифікація на @djunod «s відповідь , що мені потрібно , щоб зробити його роботу відмінно:

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

: Привіт, ваша відповідь працює більшу частину часу, але вона не працює, якщо перегляд списку має вигляд заголовка та вигляд ступні. Не могли б ви це перевірити?
hguser

Мені потрібно рішення, коли елементи списку мають різний розмір, тобто деякий перегляд тексту, який розширюється на кілька рядків. Будь-яка пропозиція?
Хобайб

1

спробуйте це, це працює для мене, я забув, де я його знайшов, десь у переповненні стека, я не тут, щоб пояснити, чому це не працює, але це відповідь :).

    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 !, я нарешті дізнався, звідки я дістав код. тут! : ListView всередині ScrollView не прокручується на Android


Тут описано, як запобігти втраті взаємодії прокрутки, але питання стосувалося збереження висоти списку перегляду.
Покинутий кошик

1

Хоча запропоновані методи setListViewHeightBasedOnChildren () працюють у більшості випадків, в деяких випадках спеціально з великою кількістю елементів, я помітив, що останні елементи не відображаються. Тому я вирішив імітувати просту версію поведінки ListView , щоб повторно використовувати будь-який код адаптера, ось це альтернатива ListView:

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

Усі ці відповіді неправильні !!! Якщо ви намагаєтеся поставити перегляд списку у подання прокрутки, вам слід передумати дизайн. Ви намагаєтеся поставити ScrollView у ScrollView. Втручання у список погіршить його роботу. Він був розроблений таким, як Android.

Якщо ви дійсно хочете, щоб список знаходився в тому ж прокрутку, що й інші елементи, все, що вам потрібно зробити, - це додати інші елементи у верхню частину списку, використовуючи просту заяву комутатора у вашому адаптері:

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


}

Адаптер списку може обробляти все, оскільки він надає лише те, що видно.


0

Вся ця проблема просто пішла б, якби LinearLayout мав метод setAdapter, тому що тоді, коли ви сказали комусь використовувати його замість цього, альтернатива була б тривіальною.

Якщо ви дійсно хочете прокрутити ListView всередині іншого перегляду прокрутки, це не допоможе, але в іншому випадку це принаймні дасть вам уявлення.

Потрібно створити спеціальний адаптер для об'єднання всього вмісту, який ви хочете прокрутити, і встановити для цього адаптер ListView.

Я не маю під рукою зразок коду, але якщо ви хочете щось подібне.

<ListView/>

(other content)

<ListView/>

Потім потрібно створити адаптер, який представляє весь цей вміст. ListView / Adapters також досить розумні, щоб керувати різними типами, але вам потрібно написати адаптер самостійно.

API інтерфейсу для android просто не настільки зрілий, як майже все інше там, тому він не має таких самих смаків, що й інші платформи. Крім того, коли ви робите щось на android, вам потрібно бути в андроїдському (unix) менталітеті, де ви очікуєте, що для того, щоб зробити все, що вам, ймовірно, доведеться зібрати функціональність з менших частин і написати купу власного коду, щоб отримати його робота.


0

Коли ми поміщаємо ListViewвсередину, ScrollViewвиникають дві проблеми. Один - це ScrollViewвимірювання своїх дітей у режимі НЕВЕРОЯТНО, тому ListViewвстановлює власну висоту, щоб вмістити лише один предмет (я не знаю чому), інший - ScrollViewперехоплює сенсорну подію, тому ListViewне прокручується.

Але ми можемо розмістити ListViewвсередині ScrollViewякийсь обхід. Ця публікація , як на мене, пояснює рішення. Таким чином, ми також можемо зберегти ListViewфункцію переробки.


0

Замість того, щоб переглядати список всередині Scrollview, покладіть решту вмісту між переглядом listlist і відкриттям Scrollview як окремий вигляд і встановіть його як заголовок перегляду. Таким чином, ви нарешті будете лише переглядом списку, який займається прокруткою.



-1

Ось моя версія коду, яка обчислює загальну висоту подання списку. Цей для мене працює:

   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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.