Android: perché non esiste maxHeight per una vista?


184

View ha unminHeight ma in qualche modo manca un maxHeight:

Quello che sto cercando di ottenere è avere alcuni oggetti (viste) riempienti a ScrollView. Quando ci sono 1..3 elementi, voglio visualizzarli direttamente. Significa che ScrollViewha l'altezza di 1, 2 o 3 elementi.

Quando ci sono 4 o più elementi voglio ScrollViewche smetta di espandersi (quindi a maxHeight) e inizi a fornire lo scorrimento.

Tuttavia, purtroppo non c'è modo di impostare un maxHeight. Quindi probabilmente devo impostare la mia ScrollViewaltezza programmaticamente su WRAP_CONTENTquando ci sono 1..3 elementi e impostare l'altezza su 3*sizeOf(View)quando ci sono 4 o più oggetti.

Qualcuno può spiegare perché non è maxHeightdisponibile, quando esiste già un minHeight?

(A proposito: alcune viste, come ImageViewhanno maxHeightimplementato).


Ho postato una soluzione in altro thread: stackoverflow.com/questions/18425758/...
JMPergar

2
Ho creato una soluzione per questo su chintanrathod.com/…
Chintan Rathod,

La rimozione di maxHeight da parte di Google è fastidiosa e scomoda. Guarda tutte le cose che devi fare ora per ottenere lo stesso risultato.
Ian Wambai,

Risposte:


86

Nessuna di queste soluzioni ha funzionato per quello che mi serviva, che era uno ScrollView impostato su wrap_content ma con un maxHeight in modo da smettere di espandersi dopo un certo punto e iniziare a scorrere. Ho semplicemente ignorato il metodo onMeasure in ScrollView.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Questo potrebbe non funzionare in tutte le situazioni, ma certamente mi dà i risultati necessari per il mio layout. E affronta anche il commento di Madhu.

Se un layout presente sotto la vista di scorrimento, questo trucco non funzionerà - madhu 5 marzo alle 4:36


1
Quando non funzionerebbe? Sembra una soluzione molto pulita. Mi chiedo perché non l'hanno implementato nell'xml.
Christophe Smet,

1
Probabilmente non funzionerebbe se imposti manualmente l'altezza su 240. Non mi sono preoccupato di controllare la modalità in cui si trova lo scrollview perché avevo solo bisogno di supportare 1 modalità specifica (wrap_content).
sfrecciare il

C'è una modifica per questa risposta che dice 'Se alcuni layout presenti sotto la vista di scorrimento, questo trucco non funzionerà', il che non è vero. Ho un layout allineato nella parte inferiore della pagina e questo layout personalizzato ha "android: layout_above = '@ + ...'" che posiziona sempre il layout personalizzato sopra il layout inferiore :)
Machine Tribe

70

Per creare un ScrollViewo ListViewcon un maxHeight devi solo creare un Trasparenza lineare trasparente attorno ad esso con un'altezza di quello che vuoi che sia il maxHeight. Quindi impostare l'altezza di ScrollView su wrap_content. Questo crea uno ScrollView che sembra crescere fino a quando la sua altezza è uguale al LinearLayout principale.


3
Ciò è stato utile per impedire che una finestra di dialogo con contenuti scorrevoli si espanda a tutta l'altezza dello schermo.
Kyle Ivey il

Un piccolo suggerimento sarebbe di usare un FrameLayout invece di un LinearLayout ma dubito che sia importante.
Kyle Ivey il

43
Se un layout presente sotto la vista di scorrimento, questo trucco non funzionerà
Sreedhu Madhu

2
puoi per favore fornire qualche codice? non ha funzionato per me, quindi forse sto facendo qualcosa di sbagliato ...
sviluppatore Android

non funzionerà se hai dei pulsanti sottostanti. spingerà i pulsanti fuori dallo schermo
Li Tian Gong

43

Questo ha funzionato per me per renderlo personalizzabile in XML:

MaxHeightScrollView.java:

public class MaxHeightScrollView extends ScrollView {

private int maxHeight;
private final int defaultHeight = 200;

public MaxHeightScrollView(Context context) {
    super(context);
}

public MaxHeightScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    if (!isInEditMode()) {
        init(context, attrs);
    }
}

private void init(Context context, AttributeSet attrs) {
    if (attrs != null) {
        TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
        //200 is a defualt value
        maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_maxHeight, defaultHeight);

        styledAttrs.recycle();
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

attr.xml

<declare-styleable name="MaxHeightScrollView">
        <attr name="maxHeight" format="dimension" />
    </declare-styleable>

layout di esempio

<blah.blah.MaxHeightScrollView android:layout_weight="1"
                app:maxHeight="90dp"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content">
                <EditText android:id="@+id/commentField"
                    android:hint="Say Something"
                    android:background="#FFFFFF"
                    android:paddingLeft="8dp"
                    android:paddingRight="8dp"
                    android:gravity="center_vertical"
                    android:maxLines="500"
                    android:minHeight="36dp"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />
            </blah.blah.MaxHeightScrollView>

La creazione di una vista personalizzata è sicuramente il modo migliore per raggiungere questo obiettivo.
Bertram Gilfoyle,

E questo funziona se hai layout sotto la vista di scorrimento! Perfetto!
Ragaisis,

25

(So ​​che questo non risponde direttamente alla domanda, ma potrebbe essere utile per gli altri alla ricerca della funzionalità maxHeight)

ConstraintLayout offre l'altezza massima per i suoi figli tramite

app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true" 

o

app:layout_constraintWidth_max="300dp"
app:layout_constrainedWidth="true" 

Esempio di utilizzo qui .


2
Questo non funziona se la vista è in una catena verticale (come specificato nei documenti ConstraintLayout)
Arno Schoonbee,

Grazie per le informazioni.
user1506104

8

Avrei commentato la risposta di Whizzle se avessi potuto, ma ho ritenuto utile notare che per poter risolvere questo problema nel contesto della modalità multi-finestra in Android N, avevo bisogno di modificare leggermente il codice in questo modo:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if(MeasureSpec.getSize(heightMeasureSpec) > maxHeight) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Ciò consente al ridimensionamento del layout di essere inferiore all'altezza massima, ma impedisce anche che sia maggiore dell'altezza massima. Ho usato questa è una classe di layout che esegue l' override RelativeLayoute questo mi ha permesso di creare una finestra di dialogo personalizzata con a ScrollViewcome figlio di MaxHeightRelativeLayoutquella che non espande l'intera altezza dello schermo e si restringe anche per adattarsi alla dimensione della vedova più piccola in multi-finestra per Android N.


4

Non è possibile impostare maxHeight. Ma puoi impostare l'altezza.

Per fare ciò dovrai scoprire l'altezza di ogni elemento di scrollView. Quindi imposta l'altezza di scrollView su numberOfItens * heightOfItem.

Per scoprire l'altezza di un oggetto, procedere come segue:

View item = adapter.getView(0, null, scrollView);
item.measure(0, 0);
int heightOfItem = item.getMeasuredHeight();

Per impostare l'altezza, procedere come segue:

// if the scrollView already has a layoutParams:
scrollView.getLayoutParams().height = heightOfItem * numberOfItens;
// or
// if the layoutParams is null, then create a new one.
scrollView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, heightOfItem * numberOfItens));

4

Avvolgi la ScrollViewtua a piano LinearLayoutcon layout_height = "max_height", questo farà un lavoro perfetto. In effetti, ho questo codice in produzione dagli ultimi 5 anni con zero problemi.

<LinearLayout
        android:id="@+id/subsParent"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:gravity="bottom|center_horizontal"
        android:orientation="vertical">

        <ScrollView
            android:id="@+id/subsScroll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginEnd="15dp"
            android:layout_marginStart="15dp">

            <TextView
                android:id="@+id/subsTv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/longText"
                android:visibility="visible" />

        </ScrollView>
    </LinearLayout>

1
Soluzione perfetta davvero. Intelligente e semplice. È adatto anche per recyclerview.
Alex Belets,

3

La mia MaxHeightScrollViewvista personalizzata

public class MaxHeightScrollView extends ScrollView {
    private int maxHeight;

    public MaxHeightScrollView(Context context) {
        this(context, null);
    }

    public MaxHeightScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray styledAttrs =
                context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
        try {
            maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_mhs_maxHeight, 0);
        } finally {
            styledAttrs.recycle();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (maxHeight > 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

style.xml

<declare-styleable name="MaxHeightScrollView">
    <attr name="mhs_maxHeight" format="dimension" />
</declare-styleable>

utilizzando

<....MaxHeightScrollView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:mhs_maxHeight="100dp"
    >

    ...

</....MaxHeightScrollView>

2

Ho una risposta qui:

https://stackoverflow.com/a/29178364/1148784

Basta creare una nuova classe che estende ScrollView e sovrascriverne il onMeasuremetodo.

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (maxHeight > 0){
            int hSize = MeasureSpec.getSize(heightMeasureSpec);
            int hMode = MeasureSpec.getMode(heightMeasureSpec);

            switch (hMode){
                case MeasureSpec.AT_MOST:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.AT_MOST);
                    break;
                case MeasureSpec.UNSPECIFIED:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
                    break;
                case MeasureSpec.EXACTLY:
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.EXACTLY);
                    break;
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

2

Come accennato in precedenza, ConstraintLayout offre l'altezza massima per i suoi figli tramite:

app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true"

Inoltre, se l'altezza massima per un figlio di ConstraintLayout è incerta fino a quando l'app è in esecuzione, c'è ancora un modo per fare in modo che questo bambino adatti automaticamente un'altezza mutabile, indipendentemente da dove è stato posizionato nella catena verticale.

Ad esempio, dobbiamo mostrare una finestra di dialogo in basso con un'intestazione mutabile TextView, una ScrollView mutabile e un piè di pagina mutabile TextView. L'altezza massima della finestra di dialogo è 320dp , quando l'altezza totale non raggiunge 320dp ScrollView agisce come wrap_content, quando l'altezza totale supera ScrollView agisce come "maxHeight = 320dp - altezza intestazione - altezza piè di pagina".

Possiamo ottenerlo solo attraverso il file di layout XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="320dp">

    <TextView
        android:id="@+id/tv_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_10"
        android:gravity="center"
        android:padding="10dp"
        app:layout_constraintBottom_toTopOf="@id/scroll_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="header" />

    <ScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_30"
        app:layout_constrainedHeight="true"
        app:layout_constraintBottom_toTopOf="@id/tv_footer"
        app:layout_constraintHeight_max="300dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_header">

        <LinearLayout
            android:id="@+id/ll_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_sub1"
                android:layout_width="match_parent"
                android:layout_height="160dp"
                android:gravity="center"
                android:textColor="@color/orange_light"
                tools:text="sub1" />

            <TextView
                android:id="@+id/tv_sub2"
                android:layout_width="match_parent"
                android:layout_height="160dp"
                android:gravity="center"
                android:textColor="@color/orange_light"
                tools:text="sub2" />

        </LinearLayout>
    </ScrollView>

    <TextView
        android:id="@+id/tv_footer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black_50"
        android:gravity="center"
        android:padding="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/scroll_view"
        tools:text="footer" />
</android.support.constraint.ConstraintLayout>

La maggior parte del codice di importazione è breve:

app:layout_constraintVertical_bias="1"
app:layout_constraintVertical_chainStyle="packed"

app:layout_constrainedHeight="true"

L'utilizzo orizzontale di maxWidth è praticamente lo stesso.


1

Hai provato a utilizzare il valore layout_weight ? Se lo imposti su un valore maggiore di 0, la vista verrà estesa nello spazio rimanente disponibile.

Se avevi più viste che dovevano essere allungate, il valore diventerà un peso tra di loro.

Quindi, se avevi due viste entrambe impostate su un valore layout_weight di 1, si allungerebbero entrambi per riempire lo spazio ma si estenderebbero entrambi a una uguale quantità di spazio. Se si imposta uno di essi sul valore di 2, si estenderebbe il doppio rispetto all'altra vista.

Altre informazioni qui elencate in Layout lineare.


1
Il problema con i pesi è che sono relativi. Dimensioni dello schermo diverse, risultati diversi, ad es. Con uno schermo da 800px posso impostare un peso su un certo valore, in modo che un'area specifica finisca con i 200px desiderati. Tuttavia, con uno schermo da 850 px l'area specifica finisce per essere più grande di 200 px, ma gli elementi racchiusi sono ancora solo 200 px. Ecco perché ho preferito avere un valore maxHeightche posso impostare su un valore dip specifico. (
Alla

1

penso che puoi impostare l'altezza in fase di esecuzione solo per 1 oggetto scrollView.setHeight(200px), per 2 elementi scrollView.setheight(400px)per 3 o piùscrollView.setHeight(600px)


8
Non vuoi impostare la Dimensione in 'px'. È molto probabile che non si ottenga il risultato desiderato su più dispositivi. developer.android.com/guide/topics/resources/…
Hernd DerBeld

@HerndDerBeld è così che funziona nel codice. se lo desideri, puoi sempre convertire da DP a pixel abbastanza facilmente: float pixels = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources () .getDisplayMetrics ());
sviluppatore Android

1

Come sappiamo, i dispositivi con Android possono avere dimensioni dello schermo diverse. Come sappiamo ulteriormente le viste dovrebbero adattarsi dinamicamente e diventare lo spazio appropriato.

Se imposti un'altezza massima, potresti forzare la vista a non avere abbastanza spazio o occupare meno spazio. So che a volte sembra essere praticamente impostare un'altezza massima. Ma se la risoluzione cambierà radicalmente, e lo farà !, allora la vista, che ha un'altezza massima, non sembrerà appropriata.

penso che non ci sia un modo corretto di fare esattamente il layout che desideri. ti consiglierei di pensare al tuo layout usando i gestori del layout e i relativi meccanismi. non so cosa stai cercando di ottenere, ma per me sembra un po 'strano che un elenco mostri solo tre elementi e che l'utente debba scorrere.

btw. minHeight non è garantito (e forse non dovrebbe esistere neanche). può avere qualche vantaggio nel forzare la visibilità degli oggetti mentre altri oggetti relativi diventano più piccoli.


6
Questo è sbagliato. In base a questa logica, non saresti in grado di dire Android: layout_width = "500dp".
Glenn Maynard

Non vedo cosa dovrebbe essere sbagliato né la tua interpretazione logica. Se imposti layout_width (che a volte va bene), non è garantito che tu lo ottenga. 500dp forse guardare su un dispositivo appropriato, su un altro no. Gestori layout ftw! Forse potresti spiegare di più perché il voto negativo disturba :)
Diego Frehner,

3
È altrettanto valido dire android: maxWidth = "500dp" come dire android: layout_width = "500dp". Entrambi impongono dimensioni specifiche nella vista.
Glenn Maynard,

1

Se qualcuno sta considerando di utilizzare il valore esatto per LayoutParams, ad es

setLayoutParams(new LayoutParams(Y, X );

Ricorda di tenere conto della densità del display del dispositivo, altrimenti potresti avere comportamenti molto strani su dispositivi diversi. Per esempio:

Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics d = new DisplayMetrics();
display.getMetrics(d);
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, (int)(50*d.density) ));

0

Innanzitutto ottieni l'altezza dell'elemento in pixel

View rowItem = adapter.getView(0, null, scrollView);   
rowItem.measure(0, 0);    
int heightOfItem = rowItem.getMeasuredHeight();

quindi semplicemente

Display display = getWindowManager().getDefaultDisplay();    
DisplayMetrics displayMetrics = new DisplayMetrics();    
display.getMetrics(displayMetrics);    
scrollView.getLayoutParams().height = (int)((heightOfItem * 3)*displayMetrics .density);

0

se volete creare una scrollview o listview non overflow, ma solo su un RelativeLayout con una vista superiore e inferiore in alto e in basso per esso:

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_above="@+id/topview"
    android:layout_below="@+id/bottomview" >

Questo ha bisogno di più credito. Implementazione specifica ma semplice.
Luke Allison,

0

Ho usato uno ScrollView personalizzato realizzato in Kotlin che utilizza maxHeight. Esempio di utilizzo:

<com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:maxHeight="100dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
</com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight>

Ecco il codice di ScrollViewWidthMaxHeight:

import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
import timber.log.Timber

class ScrollViewWithMaxHeight @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ScrollView(context, attrs, defStyleAttr) {

companion object {
    var WITHOUT_MAX_HEIGHT_VALUE = -1
}

private var maxHeight = WITHOUT_MAX_HEIGHT_VALUE

init {
    val a = context.obtainStyledAttributes(
        attrs, R.styleable.ScrollViewWithMaxHeight,
        defStyleAttr, 0
    )
    try {
        maxHeight = a.getDimension(
            R.styleable.ScrollViewWithMaxHeight_android_maxHeight,
            WITHOUT_MAX_HEIGHT_VALUE.toFloat()
        ).toInt()
    } finally {
        a.recycle()
    }
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    var heightMeasure = heightMeasureSpec
    try {
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (maxHeight != WITHOUT_MAX_HEIGHT_VALUE) {
            heightSize = maxHeight
            heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST)
        } else {
            heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.UNSPECIFIED)
        }
        layoutParams.height = heightSize
    } catch (e: Exception) {
        Timber.e(e, "Error forcing height")
    } finally {
        super.onMeasure(widthMeasureSpec, heightMeasure)
    }
}

fun setMaxHeight(maxHeight: Int) {
    this.maxHeight = maxHeight
}
}

che necessita anche di questa dichiarazione in values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollViewWithMaxHeight">
        <attr name="android:maxHeight" />
    </declare-styleable>
</resources>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.