Na verdade, o ListView já é capaz de se medir para ser alto o suficiente para exibir todos os itens, mas não o faz quando você simplesmente especifica wrap_content (MeasureSpec.UNSPECIFIED). Isso será feito quando houver uma altura com MeasureSpec.AT_MOST. Com esse conhecimento, você pode criar uma subclasse muito simples para resolver esse problema, que funciona muito melhor do que qualquer uma das soluções postadas acima. Você ainda deve usar wrap_content com esta subclasse.
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));
}
}
Manipular o heightMeasureSpec como AT_MOST com um tamanho muito grande (Integer.MAX_VALUE >> 4) faz com que o ListView meça todos os seus filhos até a altura (muito grande) especificada e defina sua altura de acordo.
Isso funciona melhor que as outras soluções por alguns motivos:
- Mede tudo corretamente (preenchimento, divisores)
- Ele mede o ListView durante a passagem de medida
- Devido ao item 2, ele lida com alterações na largura ou no número de itens corretamente, sem nenhum código adicional
No lado negativo, você pode argumentar que fazer isso depende de um comportamento não documentado no SDK que pode mudar. Por outro lado, você pode argumentar que é assim que o wrap_content realmente deve funcionar com o ListView e que o comportamento atual do wrap_content está quebrado.
Se você está preocupado que o comportamento possa mudar no futuro, você deve simplesmente copiar a função onMeasure e as funções relacionadas do ListView.java e para sua própria subclasse, e fazer com que o caminho AT_MOST através do onMeasure seja executado também como NÃO ESPECIFICADO.
A propósito, acredito que essa é uma abordagem perfeitamente válida quando você trabalha com um pequeno número de itens da lista. Pode ser ineficiente quando comparado ao LinearLayout, mas quando o número de itens é pequeno, o uso do LinearLayout é otimização desnecessária e, portanto, complexidade desnecessária.