Android: Drawable oder Hintergrundbild skalieren?

134

In einem Layout möchte ich das Hintergrundbild (unter Beibehaltung des Seitenverhältnisses) auf den Platz skalieren, der beim Erstellen der Seite zugewiesen wurde. Hat jemand eine Idee, wie das geht?

Ich verwende layout.setBackgroundDrawable()und ein BitmapDrawable()zum Einstellen gravityzum Ausschneiden und Füllen, sehe aber keine Option zum Skalieren.

finnmglas
quelle
Ich hatte dieses Problem und folgte Dweebos Empfehlung mit der von Anke vorgeschlagenen Änderung: fitXY in fitCenter geändert. Gut gearbeitet.

Antworten:

90

Um die Skalierung von Hintergrundbildern anzupassen, erstellen Sie eine Ressource wie folgt:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

Dann wird es in der Ansicht zentriert, wenn es als Hintergrund verwendet wird. Es gibt auch andere Flags: http://developer.android.com/guide/topics/resources/drawable-resource.html

Aleks N.
quelle
6
Ich habe versucht, Bitmap zu verwenden, aber das Bild ist einfach zentriert und nicht skaliert, wie Sam es verlangt. Wenn ich ImageView verwende, funktioniert es wunderbar, ohne viel Aufhebens. (nb: In meinem Fall scrolle ich auch nicht.) Womit würde ich die Bitmap erweitern, um das Bild zu skalieren?
Joe D'Andrea
7
Mögliche Werte sind: "top" | "bottom" | "links" | "richtig" | "center_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "Mitte" | "füllen" | "clip_vertical" | "clip_horizontal"
Aleks N.
30
Keiner dieser Parameter skaliert die Bitmap, indem das Seitenverhältnis beibehalten wird. Die Antwort ist also falsch.
Malachiasz
7
Lieber Malachiasz, 'Mitte' respektiert das Seitenverhältnis. Das Bild wird jedoch nicht verkleinert. Dies bedeutet, dass die Verwendung etwas gefährlich ist. Abhängig von der Auflösung des Ziels wissen Sie nie, wie skaliert das Objekt genau ist. Eine weitere halbherzige Lösung von Google.
2
Haben Sie eine Möglichkeit, das scaleType-Attribut hinzuzufügen?
ademar111190
57

Ich habe nicht versucht, genau das zu tun, was Sie wollen, aber Sie können eine ImageView mit skalieren android:scaleType="fitXY"
und sie wird so dimensioniert, dass sie in die Größe passt, die Sie der ImageView geben.

Sie können also ein FrameLayout für Ihr Layout erstellen, die ImageView darin platzieren und dann alle anderen Inhalte, die Sie im FrameLayout benötigen, mit einem transparenten Hintergrund versehen.

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>
Dweebo
quelle
1
Ist es möglich, dies auf einer Oberfläche zu tun. Wenn nicht, kann ich die Vorschau (für eine Rekorder-App) mit FrameLayout anzeigen?
Namratha
1
@dweebo: Hast du das versucht? Es scheint nicht für mich zu funktionieren.
Speedplane
3
Downvoted, da die Verwendung von zwei UI-Komponenten anstelle von einer eine schlechte Idee ist, insbesondere für ausgelastete Komponenten wie ListView. Höchstwahrscheinlich treten beim Scrollen Probleme auf. Richtige Lösung: stackoverflow.com/a/9362168/145046
Aleks N.
Ich habe einen scaleType von fitCenter und centerCrop ausprobiert (zusammen mit einigen Variationen des Zeichens / Bildes für verschiedene Dichten) und es hat funktioniert! Beide Skalentypen machten für mich Sinn - eher eine persönliche Präferenz basierend auf dem Bild, vermute ich. Beachten Sie, dass ich Merge anstelle von FrameLayout verwendet habe und in meinem speziellen Fall kein Scrollen erfolgt.
Joe D'Andrea
2
centerInside ist richtig, nicht fitXY, da fitXY das Seitenverhältnis des Bildes nicht beibehält
Malachiasz
35

Es gibt eine einfache Möglichkeit, dies vom Drawable aus zu tun:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

Der einzige Nachteil ist, dass Ihr Bild nicht vollständig angezeigt wird, wenn nicht genügend Speicherplatz vorhanden ist. Es wird jedoch abgeschnitten. Ich konnte keinen Weg finden, dies direkt von einem Zeichenobjekt aus zu tun. Aber von den Tests, die ich gemacht habe, funktioniert es ziemlich gut und es schneidet nicht so viel vom Bild ab. Sie könnten mehr mit den Schwerkraftoptionen spielen.

Ein anderer Weg , wird es sein, nur ein Layout zu erstellen, in dem Sie eine verwenden ImageViewund stellen Sie das scaleTypean fitCenter.

Ionut Negru
quelle
2
Diese Lösung ist definitiv das, wonach ich lange gesucht habe, um die Hintergrundeigenschaft meines Themas zu ersetzen und die Bildskalierung anders zu vermeiden, wenn die Ansicht Registerkarten enthält oder nicht. Vielen Dank für das Teilen.
Bibu
1
Es zeigt Fehler:error: <item> must have a 'name' attribute.
Dmitry
thx @lonutNegru, +1 für die Rettung meines Tages :)
Ravi Vaniya vor
18

Halten Sie das Bildformat Sie verwenden müssen android:scaleType=fitCenteroder fitStartusw. verwenden fitXYnicht das ursprüngliche Seitenverhältnis des Bildes zu halten!

Beachten Sie, dass dies nur für Bilder mit einem srcAttribut funktioniert , nicht für das Hintergrundbild.

Anke
quelle
18

Verwenden Sie das Bild als Hintergrundgröße für das Layout:

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

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

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


    </LinearLayout>

</FrameLayout>
Konstantin Konopko
quelle
Vielen Dank! Funktioniert auch android:scaleType="centerCrop".
CoolMind
5

Wenn Sie das Drawable einer ImageView mithilfe der setBackgroundDrawableMethode festlegen , wird das Bild immer skaliert. Parameter wie adjustViewBoundsoder anders ScaleTypeswerden einfach ignoriert. Die einzige Lösung, um das gefundene Seitenverhältnis ImageViewbeizubehalten , besteht darin, die Größe nach dem Laden Ihres Zeichens zu ändern . Hier ist das Code-Snippet, das ich verwendet habe:

// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;

if (h2w > 1) {
    // height is greater than width
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth * h2w;
    } else {
        newContainerHeight = (float) containerHeight;
        newContainerWidth = newContainerHeight / h2w;
    }
} else {
    // width is greater than height
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth / h2w; 
    } else {
        newContainerWidth = (float) containerHeight;
        newContainerHeight = newContainerWidth * h2w;       
    }
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);

Im obigen Code-Snippet ist bmp das Bitmap-Objekt, das angezeigt werden soll, und imageView ist dasImageView Objekt

Ein wichtiger Punkt ist die Änderung der Layoutparameter . Dies ist notwendig , da setMaxHeightund setMaxWidthnur einen Unterschied machen , wenn die Breite und Höhe definiert , um den Inhalt zu wickeln, nicht die Eltern zu füllen. Fill Eltern auf der anderen Seite ist die am Anfang gewünschte Einstellung, da sonst containerWidthund containerHeightbeide werden Werte auf 0 Also gleich, in Ihrer Layout - Datei so etwas wie dies werden Sie für Ihre Image haben:

...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...
Maddob
quelle
4

Dies ist nicht die leistungsstärkste Lösung, aber wie jemand vorgeschlagen hat, können Sie anstelle des Hintergrunds FrameLayout oder RelativeLayout erstellen und ImageView als Pseudohintergrund verwenden - andere Elemente werden einfach darüber positioniert:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitStart"
        android:src="@drawable/menu_icon_exit" />

    <Button
        android:id="@+id/bSomeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="61dp"
        android:layout_marginTop="122dp"
        android:text="Button" />

</RelativeLayout>

Das Problem mit ImageView besteht darin, dass nur die verfügbaren scaleTypes verfügbar sind: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.samples )

und um "das Hintergrundbild zu skalieren (sein Seitenverhältnis beizubehalten)" in einigen Fällen, wenn Sie möchten, dass ein Bild den gesamten Bildschirm ausfüllt (z. B. Hintergrundbild) und das Seitenverhältnis des Bildschirms sich von dem des Bildes unterscheidet, ist der erforderliche Skalentyp freundlich von TOP_CROP, weil:

CENTER_CROP zentriert das skalierte Bild, anstatt die Oberkante an der Oberkante der Bildansicht auszurichten, und FIT_START passt die Bildschirmhöhe an und füllt nicht die Breite. Und wie Benutzer Anke bemerkte, behält FIT_XY das Seitenverhältnis nicht bei.

Gerne hat jemand ImageView erweitert, um TOP_CROP zu unterstützen

public class ImageViewScaleTypeTopCrop extends ImageView {
    public ImageViewScaleTypeTopCrop(Context context) {
        super(context);
        setup();
    }

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

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

    private void setup() {
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {

        float frameWidth = frameRight - frameLeft;
        float frameHeight = frameBottom - frameTop;

        if (getDrawable() != null) {

            Matrix matrix = getImageMatrix();
            float scaleFactor, scaleFactorWidth, scaleFactorHeight;

            scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
            scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();

            if (scaleFactorHeight > scaleFactorWidth) {
                scaleFactor = scaleFactorHeight;
            } else {
                scaleFactor = scaleFactorWidth;
            }

            matrix.setScale(scaleFactor, scaleFactor, 0, 0);
            setImageMatrix(matrix);
        }

        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

https://stackoverflow.com/a/14815588/2075875

Jetzt wäre IMHO perfekt, wenn jemand ein benutzerdefiniertes Drawable schreiben würde, das das Bild so skaliert. Dann könnte es als Hintergrundparameter verwendet werden.


Reflog empfiehlt, Drawable vor der Verwendung vorab zu skalieren. Hier ist eine Anleitung, wie es geht: Java (Android): Wie skaliere ich ein Drawable ohne Bitmap? Obwohl dies den Nachteil hat, dass die hochskalierte Drawable / Bitmap mehr RAM benötigt, während die von ImageView verwendete Skalierung im laufenden Betrieb nicht mehr Speicher benötigt. Vorteil könnte eine geringere Prozessorlast sein.

Malachiasz
quelle
Schön! Tut genau das, wonach ich gesucht habe: Hintergrundbild, um den gesamten Bildschirm auszufüllen, indem die Breite proportional vergrößert und der untere Bildrand abgeschnitten wird.
CMash
4

Der folgende Code macht die Bitmap perfekt mit der gleichen Größe der Bildansicht. Holen Sie sich die Höhe und Breite des Bitmap-Bilds und berechnen Sie die neue Höhe und Breite mithilfe der Parameter der Bildansicht. Dadurch erhalten Sie das gewünschte Bild mit dem besten Seitenverhältnis.

int bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)
Naresh Sharma
quelle
2

Was Dweebo vorschlug, funktioniert. Aber meiner bescheidenen Meinung nach ist es unnötig. Ein Hintergrund zeichnbar skaliert gut für sich. Die Ansicht sollte eine feste Breite und Höhe haben, wie im folgenden Beispiel:

 < RelativeLayout 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="500dip"
        android:layout_height="450dip"
        android:layout_centerInParent="true"
        android:background="@drawable/my_drawable"
        android:orientation="vertical"
        android:padding="30dip"
        >
        ...
     </LinearLayout>
 < / RelativeLayout>
Yar
quelle
2
Nach meinem Verständnis wird ein Hintergrund erweitert, um eine Ansicht zu füllen, aber nicht zu verkleinern.
Edward Falk
1

Eine Möglichkeit, es zu versuchen, besteht darin, das Bild in das Feld einzufügen drawable-nodpi Ordner und den Hintergrund eines Layouts auf die ID der zeichnbaren Ressource festzulegen.

Dies funktioniert definitiv mit Skalierung, ich habe jedoch nicht mit Skalierung getestet.

Sababado
quelle
0

Kotlin:

Wenn Sie eine Bitmap in einer Ansicht zeichnen müssen, skaliert auf FIT.

Sie können die richtigen Berechnungen durchführen, um bm die Höhe gleich dem Container festzulegen und die Breite anzupassen, falls das Verhältnis von bm Breite zu Höhe geringer ist als das Verhältnis von Containerbreite zu Höhe oder umgekehrt im umgekehrten Szenario.

Bilder:

Beispielbild 1

Beispielbild 2

// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()

if (rb < rc) {
    // Bitmap Width to Height ratio is less than Container ratio
    // Means, bitmap should pin top and bottom, and have some space on sides.
    //              _____          ___
    // container = |_____|   bm = |___| 
    val bmHeight = ch - 4 //4 for container border
    val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
    val bmWidth = cw - 4 //4 for container border
    val bmHeight = 1f/rb * cw
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}
danitinez
quelle
-4

Sie müssen dieses Zeichen vorab skalieren, bevor Sie es als Hintergrund verwenden können

reflog
quelle
Ist das wirklich die einzige Option? Gibt es keinen Containertyp zum Platzieren der ImageView, in dem die Anzeigegröße angegeben werden kann?
GrkEngineer
Ich habe auch Probleme mit SurfaceView
AZ_
3
Es gibt nichts Schöneres als Aspektanpassung, Aspektfüllung, oben links, oben usw. wie unter iOS? Das ist
scheiße
Wie @Aleksej sagte, eine unwahre Antwort.
Lerner
-5

Sie können eine der folgenden Optionen verwenden:

android :vity = "fill_horizontal | clip_vertical"

Oder

android :vity = "fill_vertical | clip_horizontal"

Eli Baynesay
quelle