Ограничить высоту ListView на Android

92

Я хочу показать кнопку под ListView. Проблема в том, что если ListViewрасширяется (добавляются элементы ...), кнопка выталкивается за пределы экрана.

Я пробовал использовать LinearLayoutс весами (как это было предложено в Android: почему для View нет maxHeight? ), Но либо я неправильно указал веса, либо это просто не сработало.

Кроме того, я где-то нашел подсказку использовать RelativeLayout. ListViewЗатем будет установлено над кнопкой с android:layout_aboveпарами.

Проблема в том, что я не знаю, как потом разместить кнопку. В найденном мною примере вид ниже ListViewбыл настроен с помощью android:layout_alignParentBottom, но я не хочу, чтобы моя кнопка цеплялась за нижнюю часть экрана.

Есть идеи, кроме использования метода setHeight и некоторого расчета необходимого места?


Изменить: я получил много полезных ответов.

  • Решение bigstone и user639183 работало почти идеально. Однако мне пришлось добавить дополнительный отступ / маржу в нижнюю часть кнопки, так как она все равно будет вытолкнута наполовину за пределы экрана (но затем остановлена)

  • Ответ Адинии с относительной компоновкой подойдет только в том случае, если вы хотите, чтобы кнопка была закреплена в нижней части экрана. Это не то, что я планировал, но все же может быть полезно другим.

  • Решение AngeloS было тем, что я выбрал в конце, поскольку оно просто создавало желаемые эффекты. Тем не менее, я внес два небольших изменения LinearLayoutвокруг кнопки:

    • Во-первых, поскольку я не хотел, чтобы в моем макете были какие-либо абсолютные значения, я изменил его android:layout_height="45px"на wrap_content, который тоже отлично работает.

    • Во-вторых, поскольку я хотел, чтобы кнопка располагалась по центру по горизонтали, что поддерживается только по вертикали LinearLayout, я изменил android: direction = "horizontal" на "vertical".

    AngeloS также заявил в своем первоначальном посте, что он не уверен, имел ли какой-либо эффект android:layout_weight="0.1"параметр в LinearLayoutокружении ListView; Я просто попробовал, и это действительно так! Без этого кнопка снова выталкивается за пределы экрана.

медуза
источник
таким образом, внутренний относительный макет становится длиннее экрана, можно добавить один шаг android:layout_alignParentBottom="true". Но для ясности: хотите ли вы, чтобы кнопка оставалась прикрепленной к нижней части ListView, когда элементов мало? Если да, посмотрите, что говорит Рич.
bigstones
да, вот чего я хочу.
медуза
годы спустя у нас теперь максимальная высота благодаря ConstraintLayout: stackoverflow.com/questions/42694355/…
user1506104

Ответы:

55

У меня была именно эта проблема, и для ее решения я создал два отдельных, LinearLayoutsчтобы разместить ListViewи Buttonсоответственно. Оттуда я вставил оба в другой Linear Layoutи установил этот контейнер android:orientationна vertical. Я также установить вес , LinearLayoutчто размещался ListViewна , 0.1но я не знаю , если это имеет какой - либо эффект. Оттуда вы можете установить высоту нижнего контейнера (с вашей кнопкой) на любую желаемую высоту.

ИЗМЕНИТЬ, вот что я имею в виду:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />

</LinearLayout>

Вышеупомянутое решение зафиксирует кнопку в нижней части экрана.


Чтобы кнопка находилась внизу списка, измените высоту ListView01на wrap_content:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"
    android:orientation="horizontal">

    <ListView
        android:id="@+id/ListView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:dividerHeight="2px"></ListView>
</LinearLayout>

<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="45px"
    android:background="@drawable/drawable"
    android:orientation="horizontal">

    <Button
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/btn_more2"
        android:paddingRight="20px" />
</LinearLayout>

АнджелоС
источник
Я считаю это чем-то похожим на <divтеги в HTML ... это просто оболочка, и это решение определенно работает.
AngeloS
А что касается накладных расходов, то это не сильно отличается от того, что вы делаете с относительными макетами ... прямо сейчас у вас есть одна внешняя оболочка, а вы вкладываете другую ... я предлагаю вам вложить еще одну и разделить представление списка и кнопку в два контейнера, создавая эффект стека в вертикальной линейной компоновке. Я не могу не подчеркнуть, что это рабочее решение.
AngeloS
1
ДА! это работает! Важно отметить, что единственная разница между вашим первым и вторым решениями заключается в том, что для linearlayout требуется wrap_content, поэтому в итоге я установил начальный тип: imgur.com/c9L1Z . Для всех, кто это обнаружит: взвешивание важно, но может принимать любое значение.
Сэм,
2
нет необходимости обертывать все подобные представления - ознакомьтесь с решением @ Sparkle
Ричард Ле Мезурье
Нужно ли ставить 45 пикселей или точное значение для макета кнопки? Мне нужно вместо этого поставить wrap_content
Марио Веласко
65

Я решил эту проблему в коде, установив высоту списка только в том случае, если в нем более 5 элементов:

if(adapter.getCount() > 5){
        View item = adapter.getView(0, null, listView);
        item.measure(0, 0);         
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, (int) (5.5 * item.getMeasuredHeight()));
        listView.setLayoutParams(params);
}

Обратите внимание, что я установил максимальную высоту в 5,5 раза больше высоты одного элемента, поэтому пользователь будет точно знать, что нужно сделать прокрутку! :)

Шейл
источник
1
Хороший ответ. С помощью этого кода я определяю высоту своего элемента, и теперь я могу установить размер своего списка, чтобы показать, сколько элементов я хочу. Спасибо.
Derzu
3
Это лучший ответ. Зачем писать 50 строк вложенного XML, если можно решить проблему с помощью 5 строк кода?
Грег Эннис
Спасибо @shaylh. Работает отлично.
Anju
Несмотря на то, что я хорошо очистил XML-решения, я все же использовал эту идею в своем конечном продукте.
Ричард Ле Мезурье
1
@JayR: комментировать никогда не поздно
Манза
21

При последнем редактировании вам просто нужно также добавить android:layout_above="@+id/butt1"свой код ListView.

И чтобы показать вам (и всем, кто пробовал ответить ранее), что вам действительно не нужно использовать более одного RelativeLayout, вот ваш исправленный код :

   <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bkg">
    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView1"
        android:layout_alignParentTop="true">
    </TextView>
    <TextView
        android:id="@+id/textView2"
        android:layout_below="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="TextView2"
        android:layout_centerHorizontal="true">
    </TextView>
    <Button
        android:id="@+id/butt1"
        android:text="string/string3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true">
    </Button>
    <ListView
        android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dip"
        android:layout_marginBottom="2dip"
        android:drawSelectorOnTop="false"
        android:visibility="visible"
        android:layout_below="@+id/textView2"
        android:layout_above="@+id/butt1" />
    </RelativeLayout>

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

Адиния
источник
1
Большое спасибо за вашу помощь, но фиксировать кнопку внизу экрана было не тем, что я планировал. Он должен быть внизу ListView.
медуза
В самом деле, кажется, на этот раз LinearLayouts может быть единственным решением для того, что вы хотите; Я пробовал разные комбинации с RelativeLayouts, и ни одна из них не работала :( ... Но я рада, что вы нашли свой ответ.
Адиния
1
Спасибо! Это идеально!
Guicara
6

Используя LinearLayout, установите высоту вашего ListViewи Buttonдо wrap_contentи добавьте вес = 1 к ListView. Это должно работать так, как вы хотите.
И да, установите layout_gravityиз Buttonвbottom

Эрназм
источник
6

Нашел способ сделать это без использования вложенных контейнеров, вдохновленный решением AngeloS.

Я использовал LinearLayoutвертикальную ориентацию, в которой есть ListViewкнопки и. Я установил android:layout_weight="0.1"для ListView.

Ему удается всегда удерживать кнопку внизу списка. И кнопка не убирается с экрана при увеличении списка.

<ListView
    android:id="@+id/notes_list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0.1"        
    />

<Button
    android:id="@+id/create_note_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"        
    android:onClick="onCreateButtonClicked"
    android:text="@string/create_notes"
    />
Искриться
источник
2
Нет, просто попробовал, и кнопка остается внизу экрана, а не внизу списка. :(
Адиния 09
@Adinia Это отлично работает, по крайней мере, на 4.4.3. Хорошее решение, Sparkle, должно набрать больше голосов.
Ричард Ле Мезурье
Хммм .. может у вас есть другие настройки в вашем макете? Я только что снова протестировал Nexus 4 с 4.4.3, и кнопка остается внизу макета, а не в списке, она не прикрепляется к списку.
Adinia
2
@Richard Le Mesurier Действительно, android:layout_height="wrap_content" для LinearLayout, как и в вашем решении, он работает отлично, и не только на 4.4.3, но, поскольку Sparkle не упоминал об этом, я пробовал это match_parentраньше.
Adinia
Также работаю над Pie. +1
Зубаир Юнас
5

RelativeLayout является решением, если вы не хотите, чтобы кнопка оставалась слишком близко к низу, вы можете добавить поля (добавление кнопки сломало бы ее, потому что она использует фон из 9 патчей и устанавливает собственное заполнение).

Было бы то же самое, если бы вы использовали LinearLayout: способ добиться того, что вы хотите, - установить ListViewдля параметра height = 0 и weight = 1, а для кнопки height = wrap_content и weight = 0. (установка обоих на wrap_content, как сказал user639183, не ограничивает ListViewвысоту).

большие камни
источник
Да, это ограничит ListViewвысоту
ernazm
@ user639183 только что попробовал, вы правы, извините, я был так уверен в этом.
bigstones
Я изменил вес, как было предложено (раньше у меня был ListView weight = 2 и вес кнопки = 1, интересно, почему это не сработало?) Эффект очень интересный: теперь кнопка выдвигается только наполовину за пределы экрана. На самом деле, я могу заставить его оставаться там, где я хочу, добавив немного "android: layout_marginBottom". Однако я мог бы также попробовать подход Максима и посмотреть, работает ли он тоже.
медуза
@jellyfish: я не знаю, мои уверенности были опровергнуты @ user639183. Идея состоит в том, что две точки будут разделять всю высоту. Количество акций, которые они получают, определяется их весом, поэтому 1-0 -> все для первого (2-1 предоставит 1/3 места для второго). Я думал, что установка wrap_content в режиме прокрутки сделает его таким же высоким, как и его содержимое. Однако .. вы установили внешний макет на fill_parent?
bigstones
Очень странно, что вложенный RelativeLayout (как предлагает Максим) не работает. Я мог бы просто опубликовать это выше, так как у меня такое чувство, что я просто что-то пропустил ...
медуза
2

Вероятно, вы можете сделать эту работу, используя вложенные контейнеры. Возможно, вам понадобится обернуть Button во второй (внутренний) контейнер, чтобы он занимал больше места, и просто выровняйте Button по верхней части внутреннего контейнера.

Возможно что-то вроде этого:

RelativeLayout

-- Посмотреть список

- RelativeLayout

---- Кнопка

Используя различные варианты выравнивания двух контейнеров, вы сможете заставить это работать.

Богатый
источник
Я думаю, что использование relativeLayout - лучшее решение, но я действительно не понимаю, зачем вам использовать вложенные контейнеры, когда вы можете просто использовать что-то вроде android:layout_marginTop="30dip" кнопки, например, если вам нужно больше места между списком и кнопкой.
Adinia,
в вопросе OP сказал, что если он строго позиционирует кнопку относительно ListView, она исчезает с экрана, когда ListView становится достаточно высоким. В таких случаях вы должны усложнить задачу с вложенными контейнерами
Rich
2

Это самый чистый способ сделать это:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
  <ListView
      android:id="@+id/ListView01"
      android:layout_width="fill_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android:dividerHeight="2px">
  </ListView>
  <FrameLayout
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:background="@drawable/drawable"
      >
    <Button android:background="@drawable/btn_more2"
        android:id="@+id/moreButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:paddingRight="20px"
        />    
  </LinearLayout>
</LinearLayout>

Это похоже на решение Angelos, но вам не нужен linearlayout, который завершает ListView. Также вы можете использовать FrameLayout, который легче по сравнению с LinearLayout, чтобы обернуть кнопку.

user2994360
источник
2

Идея воплощена в очень хороших решениях:

Кажется, что они оба отлично работают, по крайней мере, на v4.4.3.

Мне все еще пришлось потратить некоторое время на написание тестового кода, чтобы проверить его и подтвердить каждый метод. Ниже приведен автономный минимальный тестовый код.


Макет основан на решении Sparkle, так как он содержит меньше вложенных макетов. Также был обновлен, чтобы отразить текущие проверки LINT.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context="MainActivity" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Grow List"
        tools:ignore="HardcodedText" />

</LinearLayout>

Activity предоставляет шаблонный код для самостоятельной проверки решения.

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ListView listview = (ListView)findViewById(R.id.listview);
        final ArrayAdapter<String> adapter = 
                new ArrayAdapter<String>(this,
                                         android.R.layout.simple_list_item_1,
                                         new ArrayList<String>());
        adapter.setNotifyOnChange(true);
        listview.setAdapter(adapter);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                int count = adapter.getCount();
                adapter.add(String.valueOf(count));
                listview.setSelection(count);
            }
        });
    }
}

Не стесняйтесь добавлять или улучшать, так как это «вики сообщества».

Ришар Ле Мезурье
источник
1

Попробуйте использовать RelativeLayout с кнопкой внизу. Установите высоту макета как «wrap_content». Я не пробовал. Просто идея

Максим
источник
Я действительно думал, что это должно сработать, но похоже, что это не так. Макет заполняет все оставшееся пространство, как если бы высота была установлена ​​на match_parent. Хотя в этом нет смысла.
медуза
1

в LinearLayout просто добавьте android:layout_weight="1"к вам ListView

РассветЮ
источник
0

Вот что сработало для меня ...

 if(adapter.getCount() > 3){
                    View item = adapter.getView(0, null, impDateListView);
                    item.measure(0, 0);         
                    LayoutParams params = impDateListView.getLayoutParams();
                    params.width = LayoutParams.MATCH_PARENT;
                    params.height = 3 * item.getMeasuredHeight();
                    impDateListView.setLayoutParams(params);


                }

Примечание: params.width = ... Необходимо также установить ...

В приведенном выше примере вы используете TableRow и ListView внутри TableRow ...

Атул Дравид - White Pvt. ООО
источник
0

обернуть контент в линейный макет

в представлении списка добавить: android: layout_weight = "1"

в вашей кнопке установите фиксированную высоту

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

<ListView
    android:id="@+id/my_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1">
</ListView>

 <Button
    android:text="Button"
    android:layout_width="match_parent"
    android:layout_height="50dp"/>

</LinearLayout>
Тома
источник
0

Если вы хотите, чтобы содержимое под списком перемещалось вниз по мере добавления элементов в список, попробуйте следующее решение:

Добавьте положительное заполнение в нижнюю часть списка и негативное в верхнюю часть «нижнего колонтитула». Это будет работать в линейном или относительном макете.

<ListView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="50dp"/>

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-50dp"/>
Женни Пушкарская
источник
-1

Поскольку атрибута MaxHeight нет, лучше всего либо установить для высоты ListView заданное значение, либо использовать wrap_content.

Крис Кэшвелл
источник
-2

Я решил это, поместив ListView в ScrollView. Линту это не понравилось, но, добавив minHeight и установив для fillViewport значение true, я получил хороший результат.

    <ScrollView 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="1dp"
        android:layout_marginRight="1dp"
        android:minHeight="100dp"
        android:fillViewport="true"
        android:fadeScrollbars="false"
        android:paddingTop="2dp" 
        android:orientation="vertical"
        android:scrollbarAlwaysDrawVerticalTrack="true" >

        <ListView
            android:id="@+id/workoutAElistRoutines"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="fill_vertical"
            android:background="@drawable/dialog_inner_border_tan"
            android:choiceMode="singleChoice"
            android:orientation="vertical"
            android:paddingLeft="3dp"
            android:paddingRight="3dp" >

        </ListView>
    </ScrollView>        
Джон
источник
Я пробовал этот подход, даже если он ограничивает размер списка, он не позволяет мне прокручивать список, что неудобно. Ты знаешь почему?
Эдуардо
7
НИКОГДА не помещайте ListView в ScrollView!
jenzz
Да, никогда не помещайте ListView в ScrollView. Но с помощью этого «взлома» это возможно ... stackoverflow.com/a/14577399/1255990
Леонардо Кардосо