Android: Wie kann man ein Bild auf die Bildschirmbreite strecken und dabei das Seitenverhältnis beibehalten?

118

Ich möchte ein Bild herunterladen (unbekannter Größe, aber immer ungefähr quadratisch) und es so anzeigen, dass es den Bildschirm horizontal ausfüllt und sich vertikal erstreckt, um das Seitenverhältnis des Bildes bei jeder Bildschirmgröße beizubehalten. Hier ist mein (nicht funktionierender) Code. Es streckt das Bild horizontal, aber nicht vertikal, so dass es gequetscht wird ...

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));
Fredley
quelle
Haben alle Android-Geräte genau das gleiche Verhältnis von Breite zu Höhe? Wenn nicht, ist es einfach unmöglich, ein Bild so zu skalieren, dass es der gesamten Breite / Höhe entspricht, während das ursprüngliche Verhältnis
beibehalten wird
4
Nein, ich möchte nicht, dass das Bild den Bildschirm ausfüllt, sondern nur auf die Bildschirmbreite skaliert. Es ist mir egal, wie viel Bildschirm das Bild vertikal einnimmt, solange das Bild die richtigen Proportionen aufweist.
Fredley
1
Ähnliche Frage, gute Antwort: stackoverflow.com/questions/4677269/…
Pēteris Caune
1
Hat 'adjustViewBounds' nicht funktioniert?
Darpan

Antworten:

132

Ich habe dies mit einer benutzerdefinierten Ansicht erreicht. Setzen Sie layout_width = "fill_parent" und layout_height = "wrap_content" und zeigen Sie auf das entsprechende Zeichenobjekt:

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);
  }
}
Bob Lee
quelle
12
Danke dir!! Dies sollte die richtige Antwort sein! :) Ich habe dies für ein wenig mehr Flexibilität in diesem Beitrag angepasst : stackoverflow.com/questions/4677269/… Dort verwende ich eine ImageView, damit man das Drawable in der XML festlegen oder es wie eine normale ImageView im Code verwenden kann, um das Bild festzulegen. Funktioniert super!
Patrick Boos
1
Freakin Genie! das hat funktioniert wie ein Champion! Vielen Dank, es hat mein Problem mit einem Bildbanner oben gelöst, das nicht in allen Bildschirmgrößen richtig ist! Ich danke dir sehr!
JPM
1
Achten Sie darauf, dass das Logo null ist (wenn Sie Inhalte aus dem Internet herunterladen). Ansonsten funktioniert dies hervorragend. Folgen Sie Patricks Beitrag für das XML-Snippet.
Artem Russakovskii
44
Ich kann nicht glauben, wie schwierig es ist, Dinge auf Android zu tun, die unter iOS und Windows Phone trivial einfach sind.
mxcl
1
Ich habe einige Änderungen vorgenommen, aber anstatt sie woanders zu veröffentlichen, wäre es möglich, diese Antwort in ein Community-Wiki umzuwandeln? Meine Änderungen betreffen alle Dinge, die die hier genannten Sonderfälle behandeln, sowie die Möglichkeit zu wählen, welche Seite über die Layoutdefinition in der Größe geändert wird.
Steve Pomeroy
24

Am Ende habe ich die Dimensionen manuell generiert, was großartig funktioniert:

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));
Fredley
quelle
Ich habe nach einer solchen Lösung gesucht, um mein Problem zu beheben. Mit Hilfe dieser Berechnung konnte ich das Bild erfolgreich laden, ohne das Seitenverhältnis zu verringern.
Steve
21

Ich habe gerade den Quellcode für gelesen ImageViewund es ist im Grunde unmöglich, ohne die Unterklassenlösungen in diesem Thread zu verwenden. In kommen ImageView.onMeasurewir zu diesen Zeilen:

        // 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);

Wo hund wsind die Abmessungen des Bildes und p*ist die Polsterung.

Und dann:

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);

Wenn Sie also eine layout_height="wrap_content"haben, wird diese festgelegt widthSize = w + pleft + pright, oder mit anderen Worten, die maximale Breite entspricht der Bildbreite.

Dies bedeutet, dass Bilder NIEMALS vergrößert werden , wenn Sie keine genaue Größe festlegen . Ich halte dies für einen Fehler, aber viel Glück, dass Google ihn zur Kenntnis nimmt oder behebt. Bearbeiten: Als ich meine eigenen Worte aß, reichte ich einen Fehlerbericht ein und sie sagten, dass er in einer zukünftigen Version behoben wurde!

Eine andere Lösung

Hier ist eine weitere Problemumgehung für Unterklassen, aber Sie sollten (theoretisch habe ich es nicht wirklich viel getestet!) In der Lage sein, sie überall dort zu verwenden, wo Sie möchten ImageView. Um es zu verwenden, setzen Sie layout_width="match_parent"und layout_height="wrap_content". Es ist viel allgemeiner als die akzeptierte Lösung. Zum Beispiel können Sie sowohl Höhen- als auch Breitenanpassungen durchführen.

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);
        }
    }
}
Timmmm
quelle
Ich habe einen Fehlerbericht eingereicht . Beobachten Sie, wie es ignoriert wird!
Timmmm
3
Anscheinend muss ich meine Worte essen - sie sagen, dass es in einer zukünftigen Version behoben wurde!
Timmmm
Vielen Dank Tim. Dies sollte die endgültige richtige Antwort sein. Ich habe viel Zeit gesucht und nichts ist besser als Ihre Lösung.!
Tainy
Was ist, wenn ich bei Verwendung der obigen StretchyImageView die maximale Höhe festlegen muss? Ich habe keine Lösung dafür gefunden.
Tainy
17

Das Setzen von adjustViewBounds auf true und das Verwenden einer LinearLayout-Ansichtsgruppe hat bei mir sehr gut funktioniert. Sie müssen keine Unterklassen erstellen oder nach Gerätemetriken fragen:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);
Matt Stoker
quelle
1
... oder mit der ImageView XML-Eigenschaft - android: adjustViewBounds = "true"
Anatoliy Shuba
Perfekt! Das war so einfach und es streckt das Bild perfekt. Wie ist diese Antwort nicht die richtige? Die Antwort mit der CustomView hat bei mir nicht funktioniert (ein seltsamer XML-Fehler)
user3800924
13

Ich habe mit diesem Problem in der einen oder anderen Form für AGES zu kämpfen, danke, danke, danke ... :)

Ich wollte nur darauf hinweisen, dass Sie eine verallgemeinerbare Lösung aus dem, was Bob Lee getan hat, erhalten können, indem Sie einfach View erweitern und onMeasure überschreiben. Auf diese Weise können Sie dies mit jedem gewünschten Zeichen verwenden, und es wird nicht beschädigt, wenn kein Bild vorhanden ist:

    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);
            }
        }
    }
duhrer
quelle
2
Von all den vielen, vielen Lösungen für dieses Problem ist dies die erste, die nur eine Drop-In-Lösung ist, bei der die anderen immer einen Nachteil haben (z. B. das Bild zur Laufzeit nicht ändern zu können, ohne dass der Code neu geschrieben werden muss). Danke dir!
MacD
Das ist pure Großartigkeit - ich habe seit einiger Zeit damit zu kämpfen und alle offensichtlichen Lösungen, die die XML-Attribute verwenden, haben nie funktioniert. Damit konnte ich einfach meine ImageView durch die erweiterte Ansicht ersetzen und alles funktionierte wie erwartet!
Slott
Dies ist absolut die beste Lösung für dieses Problem.
Erlando
Das ist eine gute Antwort. Sie können diese Klasse in XML-Dateien ohne folgende Sorgen verwenden: <com.yourpackagename.CardImageView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: background = "@ drawable / your_photo" />. Toll!
Arniotaki
11

In einigen Fällen löst diese Zauberformel das Problem auf wunderbare Weise.

Für alle, die Probleme damit haben, von einer anderen Plattform zu kommen, wird die Option "Größe und Form passen" in Android wunderbar gehandhabt, ist aber schwer zu finden.

Sie möchten normalerweise diese Kombination:

  • Breite übereinstimmen Eltern,
  • Höhenverpackungsinhalt,
  • adjustViewBounds eingeschaltet (sic)
  • skalieren fitCenter
  • cropToPadding OFF (sic)

Dann ist es automatisch und erstaunlich.

Wenn Sie ein iOS-Entwickler sind, ist es absolut erstaunlich, wie einfach Sie in Android "total dynamische Zellenhöhen" in einer Tabellenansicht erstellen können. Ähm, ich meine ListView. Genießen.

<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"/>

Geben Sie hier die Bildbeschreibung ein

Fattie
quelle
6

Ich habe es geschafft, dies nur mit diesem XML-Code zu erreichen. Es kann vorkommen, dass Eclipse die Höhe nicht rendert, um zu zeigen, dass sie sich entsprechend ausdehnt. Wenn Sie dies jedoch tatsächlich auf einem Gerät ausführen, wird es ordnungsgemäß gerendert und liefert das gewünschte Ergebnis. (Na zumindest für mich)

<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>
NeilDA
quelle
Entschuldigung, das funktioniert nicht. Es skaliert die Breite, aber centerCrop behält das Seitenverhältnis nicht bei.
Ricosrealm
1
Nach vielen Stunden mit benutzerdefinierten Klassen, die ImageView erweitern (wie in vielen Antworten und Artikeln beschrieben) und einer Ausnahme wie "Die folgenden Klassen konnten nicht gefunden werden", entfernte ich diesen Code. Plötzlich bemerkte ich, dass ein Problem in meiner ImageView im Hintergrundattribut lag! Geändert in srcund die ImageView wurde proportional.
CoolMind
5

Ich habe es mit diesen Werten in einem LinearLayout gemacht:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
helduel
quelle
Könnten Sie ein echtes Beispiel geben? Wo füge ich diese Werte in meine XML-Datei ein?
John Smith Optional
5

Jeder macht das programmgesteuert, also dachte ich, diese Antwort würde perfekt hierher passen. Dieser Code funktionierte für meine in der XML. Ich denke noch NICHT über das Verhältnis nach, wollte aber trotzdem diese Antwort platzieren, wenn es jemandem helfen würde.

android:adjustViewBounds="true"

Prost..

Sindri Þór
quelle
3

Eine sehr einfache Lösung besteht darin, nur die von bereitgestellten Funktionen zu verwenden RelativeLayout.

Hier ist die XML, die es mit Standard-Android ermöglicht 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>

Der Trick besteht darin, dass Sie festlegen ImageView, dass der Bildschirm ausgefüllt wird, er sich jedoch über den anderen Layouts befinden muss. Auf diese Weise erreichen Sie alles, was Sie brauchen.

Warpzit
quelle
2

Es ist einfach zu setzen adjustViewBounds="true"und scaleType="fitCenter"in der XML-Datei für die ImageView!

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

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

Hinweis: layout_widthist auf eingestelltmatch_parent

Kaushik NP
quelle
1

Sie setzen den ScaleType auf ScaleType.FIT_XY. Laut den Javadocs wird das Bild dadurch auf den gesamten Bereich gestreckt und bei Bedarf das Seitenverhältnis geändert . Das würde das Verhalten erklären, das Sie sehen.

Um das gewünschte Verhalten zu erzielen ... FIT_CENTER, FIT_START oder FIT_END sind nahe beieinander. Wenn das Bild jedoch schmaler als hoch ist, wird die Breite nicht ausgefüllt. Sie könnten sich jedoch ansehen, wie diese implementiert werden, und Sie sollten wahrscheinlich in der Lage sein, herauszufinden, wie Sie sie für Ihren Zweck anpassen können.

Cheryl Simon
quelle
2
Ich hatte FIT_CENTER ausprobiert, aber nicht die anderen. Leider funktionieren sie auch nicht, da setAdjustViewBounds auf true und false gesetzt ist. Gibt es überhaupt eine Möglichkeit, die Pixelbreite des Bildschirms des Geräts sowie die Breite / Höhe des heruntergeladenen Bildes abzurufen und die Größe manuell einzustellen?
Fredley
1

ScaleType.CENTER_CROP macht das, was Sie wollen: Dehnen Sie es auf die volle Breite und skalieren Sie die Höhe entsprechend. Wenn die skalierte Höhe die Bildschirmgrenzen überschreitet, wird das Bild zugeschnitten.

P. Melch
quelle
1

Schauen Sie, es gibt eine weitaus einfachere Lösung für Ihr Problem:

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);
}
user3673209
quelle
1

Sie können mein StretchableImageView verwenden , um das Seitenverhältnis (nach Breite oder Höhe) abhängig von der Breite und Höhe des Zeichens beizubehalten:

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);
            }
        }
    }
}
Valerybodak
quelle
0

Für mich hat der Android: scaleType = "centerCrop" mein Problem nicht gelöst. Es hat das Bild tatsächlich viel mehr erweitert. Also habe ich es mit Android versucht: scaleType = "fitXY" und es hat hervorragend funktioniert.

Ricardo
quelle
0

Dies funktioniert gut gemäß meiner Anforderung

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

Anand Savjani
quelle