Android: come allungare un'immagine alla larghezza dello schermo mantenendo le proporzioni?


118

Voglio scaricare un'immagine (di dimensioni sconosciute, ma che è sempre approssimativamente quadrata) e visualizzarla in modo che riempia lo schermo orizzontalmente e si allunghi verticalmente per mantenere le proporzioni dell'immagine, su qualsiasi dimensione dello schermo. Ecco il mio codice (non funzionante). Allunga l'immagine orizzontalmente, ma non verticalmente, quindi è schiacciata ...

ImageView mainImageView = new ImageView(context);
    mainImageView.setImageBitmap(mainImage); //downloaded from server
    mainImageView.setScaleType(ScaleType.FIT_XY);
    //mainImageView.setAdjustViewBounds(true); 
    //with this line enabled, just scales image down
    addView(mainImageView,new LinearLayout.LayoutParams( 
            LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

tutti i dispositivi Android hanno esattamente lo stesso rapporto larghezza / altezza? In caso contrario, è semplicemente impossibile ridimensionare un'immagine per adattarla all'intera larghezza / altezza mantenendo il rapporto originale ...
NoozNooz42

4
No, non voglio che l'immagine riempia lo schermo, solo per ridimensionarsi in base alla larghezza dello schermo, non mi interessa quanta parte dello schermo l'immagine occupa verticalmente, purché l'immagine sia nelle proporzioni corrette.
fredley

1
Domanda simile, buona risposta: stackoverflow.com/questions/4677269/…
Pēteris Caune

1
"AdjustViewBounds" non ha funzionato?
Darpan

Risposte:


132

L'ho realizzato con una visualizzazione personalizzata. Imposta layout_width = "fill_parent" e layout_height = "wrap_content" e indirizzalo al disegno appropriato:

public class Banner extends View {

  private final Drawable logo;

  public Banner(Context context) {
    super(context);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs) {
    super(context, attrs);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  @Override protected void onMeasure(int widthMeasureSpec,
      int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = width * logo.getIntrinsicHeight() / logo.getIntrinsicWidth();
    setMeasuredDimension(width, height);
  }
}

12
Grazie!! Questa dovrebbe essere la risposta corretta! :) Ho regolato questo per un po 'più di flessibilità in questo post: stackoverflow.com/questions/4677269/… Lì uso un ImageView in modo che si possa impostare il drawable in XML o usarlo come il normale ImageView nel codice per impostare l'immagine. Funziona alla grande!
Patrick Boos

1
Genio pazzo! che ha funzionato come un campione! Grazie ha risolto il mio problema con un banner immagine in alto non a destra in tutte le dimensioni dello schermo! Grazie mille!
JPM

1
Fai attenzione che il logo sia nullo (se stai scaricando contenuti da Internet). Altrimenti, funziona alla grande, segui il post di Patrick per lo snippet XML.
Artem Russakovskii

44
Non riesco a credere quanto sia difficile fare cose su Android che sono banalmente facili in iOS e Windows Phone.
mxcl

1
Ho apportato alcune modifiche, ma invece di pubblicarle altrove, sarebbe possibile trasformare questa risposta in un wiki della comunità? Le mie modifiche sono tutte cose che gestiscono i casi speciali menzionati qui, così come la possibilità di scegliere quale lato viene ridimensionato tramite la definizione del layout.
Steve Pomeroy

24

Alla fine, ho generato manualmente le dimensioni, il che funziona benissimo:

DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = width * mainImage.getHeight() / mainImage.getWidth(); //mainImage is the Bitmap I'm drawing
addView(mainImageView,new LinearLayout.LayoutParams( 
        width, height));

Stavo cercando questo tipo di soluzione per risolvere il mio problema.Con l'aiuto di questo calcolo, l'ho ottenuto caricando correttamente l'immagine senza ridurre le proporzioni.
Steve

21

Ho appena letto il codice sorgente di ImageViewed è praticamente impossibile senza utilizzare le soluzioni di sottoclasse in questo thread. In ImageView.onMeasurearriviamo a queste righe:

        // Get the max possible width given our constraints
        widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);

        // Get the max possible height given our constraints
        heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);

Dove he wsono le dimensioni dell'immagine, ed p*è l'imbottitura.

E poi:

private int resolveAdjustedSize(int desiredSize, int maxSize,
                               int measureSpec) {
    ...
    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            /* Parent says we can be as big as we want. Just don't be larger
               than max size imposed on ourselves.
            */
            result = Math.min(desiredSize, maxSize);

Quindi, se hai un layout_height="wrap_content", verrà impostato widthSize = w + pleft + pright, o in altre parole, la larghezza massima è uguale alla larghezza dell'immagine.

Ciò significa che, a meno che non si imposti una dimensione esatta, le immagini non vengono MAI ingrandite . Considero questo un bug, ma buona fortuna a far sì che Google se ne accorga o lo risolva. Modifica: mangiando le mie stesse parole, ho inviato una segnalazione di bug e dicono che è stato risolto in una versione futura!

Un'altra soluzione

Ecco un'altra soluzione alternativa sottoclasse, ma dovresti (in teoria, non l'ho davvero testato molto!) Essere in grado di usarlo ovunque tu ImageView. Per usarlo impostare layout_width="match_parent", e layout_height="wrap_content". È anche molto più generale della soluzione accettata. Ad esempio, puoi adattarlo all'altezza e adattarlo alla larghezza.

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

// This works around the issue described here: http://stackoverflow.com/a/12675430/265521
public class StretchyImageView extends ImageView
{

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

    public StretchyImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public StretchyImageView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Call super() so that resolveUri() is called.
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // If there's no drawable we can just use the result from super.
        if (getDrawable() == null)
            return;

        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        int w = getDrawable().getIntrinsicWidth();
        int h = getDrawable().getIntrinsicHeight();
        if (w <= 0)
            w = 1;
        if (h <= 0)
            h = 1;

        // Desired aspect ratio of the view's contents (not including padding)
        float desiredAspect = (float) w / (float) h;

        // We are allowed to change the view's width
        boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;

        // We are allowed to change the view's height
        boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

        int pleft = getPaddingLeft();
        int pright = getPaddingRight();
        int ptop = getPaddingTop();
        int pbottom = getPaddingBottom();

        // Get the sizes that ImageView decided on.
        int widthSize = getMeasuredWidth();
        int heightSize = getMeasuredHeight();

        if (resizeWidth && !resizeHeight)
        {
            // Resize the width to the height, maintaining aspect ratio.
            int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
            setMeasuredDimension(newWidth, heightSize);
        }
        else if (resizeHeight && !resizeWidth)
        {
            int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;
            setMeasuredDimension(widthSize, newHeight);
        }
    }
}

Ho inviato una segnalazione di bug . Guarda, poiché viene ignorato!
Timmmm

3
Apparentemente devo mangiare le mie parole - dicono che è stato risolto in una versione futura!
Timmmm

Grazie mille Tim. Questa dovrebbe essere l'ultima risposta giusta. Ho cercato molto tempo e niente è meglio della tua soluzione.!
tainy

cosa succede se devo impostare maxHeight quando si utilizza StretchyImageView sopra. Non ho trovato alcuna soluzione per questo.
tainy

17

L'impostazione di AdjustViewBounds su true e l'utilizzo di un gruppo di viste LinearLayout ha funzionato molto bene per me. Non è necessario creare sottoclassi o richiedere le metriche del dispositivo:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);

1
... o utilizzando la proprietà XML ImageView - android: AdjustViewBounds = "true"
Anatoliy Shuba

Perfetto! Questo è stato così facile e allunga l'immagine perfettamente. Perché questa risposta non è quella corretta? La risposta con CustomView non ha funzionato per me (qualche strano errore xml)
user3800924

13

Ho lottato con questo problema in una forma o nell'altra per AGES, grazie, grazie, GRAZIE .... :)

Volevo solo sottolineare che puoi ottenere una soluzione generalizzabile da ciò che ha fatto Bob Lee semplicemente estendendo View e sovrascrivendo suMeasure. In questo modo puoi usarlo con qualsiasi disegnabile che desideri e non si romperà se non c'è immagine:

    public class CardImageView extends View {
        public CardImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        public CardImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

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

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Drawable bg = getBackground();
            if (bg != null) {
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * bg.getIntrinsicHeight() / bg.getIntrinsicWidth();
                setMeasuredDimension(width,height);
            }
            else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

2
Di tutte le tante, tante soluzioni a questo problema, questa è la prima che è solo una soluzione drop-in, dove le altre hanno sempre degli svantaggi (come non poter cambiare l'immagine in fase di runtime senza una riscrittura del codice). Grazie!
MacD

Questa è pura meraviglia - ho lottato con questo per un po 'di tempo e tutte le soluzioni ovvie che utilizzano gli attributi xml non hanno mai funzionato. Con questo potrei semplicemente sostituire il mio ImageView con la visualizzazione estesa e tutto ha funzionato come previsto!
Slott

Questa è in assoluto la migliore soluzione a questo problema.
erlando

È un'ottima risposta. Puoi usare questa classe nel file xml senza preoccupazioni in questo modo: <com.yourpackagename.CardImageView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: background = "@ drawable / your_photo" />. Grande!
arniotaki

11

In alcuni casi questa formula magica risolve meravigliosamente il problema.

Per chiunque abbia problemi con questo proveniente da un'altra piattaforma, l' opzione "dimensione e forma per adattarsi" è gestita magnificamente in Android, ma è difficile da trovare.

Di solito vuoi questa combinazione:

  • genitore corrispondenza larghezza,
  • contenuto a capo automatico,
  • AdjustViewBounds attivato (sic)
  • scale fitCenter
  • cropToPadding OFF (sic)

Quindi è automatico e sorprendente.

Se sei uno sviluppatore iOS, è assolutamente incredibile quanto semplicemente, in Android, puoi fare "altezze di cella totalmente dinamiche" in una vista tabella .. ehm, intendo ListView. Godere.

<com.parse.ParseImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/post_image"
    android:src="@drawable/icon_192"
    android:layout_margin="0dp"
    android:cropToPadding="false"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
    android:background="#eff2eb"/>

inserisci qui la descrizione dell'immagine


6

Sono riuscito a ottenere questo risultato utilizzando solo questo codice XML. Potrebbe essere il caso che eclipse non visualizzi l'altezza per mostrare che si espande per adattarsi; tuttavia, quando si esegue effettivamente su un dispositivo, viene eseguito correttamente il rendering e fornisce il risultato desiderato. (beh almeno per me)

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

     <ImageView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:adjustViewBounds="true"
          android:scaleType="centerCrop"
          android:src="@drawable/whatever" />
</FrameLayout>

Spiacente, non funziona. Ridimensiona la larghezza, ma centerCrop non mantiene le proporzioni.
ricosrealm

1
Dopo molte ore alle prese con classi personalizzate che estendevano ImageView (come scritto in molte risposte e articoli) e avendo un'eccezione come "Impossibile trovare le seguenti classi", ho rimosso quel codice. All'improvviso ho notato che un problema nel mio ImageView era nell'attributo di sfondo! L'ho cambiato in srce ImageView è diventato proporzionale.
CoolMind

5

L'ho fatto con questi valori all'interno di un LinearLayout:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent

Potresti fare un esempio reale? Dove inserisco questi valori nel mio file xml?
John Smith Opzionale

5

Tutti lo fanno in modo programmato, quindi ho pensato che questa risposta si adatterebbe perfettamente qui. Questo codice ha funzionato per my in xml. NON sto ancora pensando al rapporto, ma volevo comunque inserire questa risposta se potesse aiutare qualcuno.

android:adjustViewBounds="true"

Saluti..


3

Una soluzione molto semplice consiste nell'usare solo le funzionalità fornite da RelativeLayout.

Ecco l'xml che lo rende possibile con Android standard Views:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <LinearLayout
            android:id="@+id/button_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_alignParentBottom="true"
            >
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <ImageView 
            android:src="@drawable/cat"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:layout_above="@id/button_container"/>
    </RelativeLayout>
</ScrollView>

Il trucco è che si imposta ImageViewin modo che riempia lo schermo, ma deve essere sopra gli altri layout. In questo modo ottieni tutto ciò di cui hai bisogno.


2

La sua semplice questione di impostazione adjustViewBounds="true"e scaleType="fitCenter"nel file XML per ImageView!

<ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/image"

        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        />

Nota: layout_widthè impostato sumatch_parent


1

Stai impostando ScaleType su ScaleType.FIT_XY. Secondo i javadoc , questo allungherà l'immagine per adattarla all'intera area, cambiando le proporzioni se necessario. Questo spiegherebbe il comportamento che stai vedendo.

Per ottenere il comportamento desiderato ... FIT_CENTER, FIT_START o FIT_END sono vicini, ma se l'immagine è più stretta che alta, non inizierà a riempire la larghezza. Potresti guardare come vengono implementati, e probabilmente dovresti essere in grado di capire come regolarli per il tuo scopo.


2
Avevo provato FIT_CENTER, ma non gli altri. Sfortunatamente non funzionano neanche, sia con setAdjustViewBounds impostato su true che su false. C'è comunque per ottenere la larghezza in pixel dello schermo del dispositivo, così come la larghezza / altezza dell'immagine scaricata e impostare manualmente la dimensione?
fredley

1

ScaleType.CENTER_CROP farà quello che vuoi: allungare a tutta larghezza e ridimensionare l'altezza di conseguenza. se l'altezza in scala supera i limiti dello schermo, l'immagine verrà ritagliata.


1

Guarda, c'è una soluzione molto più semplice al tuo problema:

ImageView imageView;

protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_layout);
    imageView =(ImageView)findViewById(R.id.your_imageView);
    Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image);
    Point screenSize = new Point();
    getWindowManager().getDefaultDisplay().getSize(screenSize);
    Bitmap temp = Bitmap.createBitmap(screenSize.x, screenSize.x, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(temp);
    canvas.drawBitmap(imageBitmap,null, new Rect(0,0,screenSize.x,screenSize.x), null);
    imageView.setImageBitmap(temp);
}

1

Puoi usare il mio StretchableImageView preservando le proporzioni (per larghezza o altezza) a seconda della larghezza e dell'altezza del disegnabile:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

public class StretchableImageView extends ImageView{

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

    public StretchableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public StretchableImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if(getDrawable()!=null){
            if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * getDrawable().getIntrinsicHeight()
                            / getDrawable().getIntrinsicWidth();
                setMeasuredDimension(width, height);
            }else{
                int height = MeasureSpec.getSize(heightMeasureSpec);
                int width = height * getDrawable().getIntrinsicWidth()
                            / getDrawable().getIntrinsicHeight();
                setMeasuredDimension(width, height);
            }
        }
    }
}

0

Per me android: scaleType = "centerCrop" non ha risolto il mio problema. In realtà ha ampliato di più l'immagine. Così ho provato con android: scaleType = "fitXY" e ha funzionato alla grande.


0

Questo funziona bene secondo il mio requisito

<ImageView android:id="@+id/imgIssue" android:layout_width="fill_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitXY"/>

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.