На самом деле 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 измерять все свои дочерние элементы до заданной (очень большой) высоты и соответственно устанавливать его высоту.
Это работает лучше, чем другие решения по нескольким причинам:
- Все правильно измеряет (отступы, разделители)
- Он измеряет ListView во время прохода меры
- Благодаря # 2 он корректно обрабатывает изменения ширины или количества элементов без дополнительного кода
С другой стороны, вы можете утверждать, что это зависит от недокументированного поведения в SDK, которое может измениться. С другой стороны, вы можете утверждать, что именно так wrap_content должен действительно работать с ListView и что текущее поведение wrap_content просто нарушено.
Если вы обеспокоены тем, что в будущем поведение может измениться, вам просто нужно скопировать функцию onMeasure и связанные с ней функции из ListView.java в свой собственный подкласс, а затем запустить путь AT_MOST через onMeasure для UNSPECIFIED.
Кстати, я считаю, что это совершенно правильный подход, когда вы работаете с небольшим количеством элементов списка. Это может быть неэффективно по сравнению с LinearLayout, но когда количество элементов мало, использование LinearLayout является ненужной оптимизацией и, следовательно, ненужной сложностью.