Dopasować obraz do ImageView, zachować proporcje, a następnie zmienić rozmiar ImageView na wymiary obrazu?


164

Jak dopasować obraz o losowym rozmiarze do ImageView?
Kiedy:

  • Początkowe ImageViewwymiary to 250 dp * 250 dp
  • Większy rozmiar obrazu powinien zostać przeskalowany w górę / w dół do 250 dp
  • Obraz powinien zachować proporcje
  • Po ImageViewprzeskalowaniu wymiary powinny odpowiadać wymiarom skalowanego obrazu

Np. Dla obrazu 100 * 150, obraz i ImageViewpowinien być 166 * 250.
Np. Dla obrazu o wymiarach 150 * 100, obraz i ImageViewpowinien wynosić 250 * 166.

Jeśli ustawię granice jako

<ImageView
    android:id="@+id/picture"
    android:layout_width="250dp"
    android:layout_height="250dp"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="20dp"
    android:adjustViewBounds="true" />

obrazy pasują prawidłowo do formatu ImageView, ale ImageViewzawsze wynosi 250 * 250 dp.


Uh, masz na myśli zmianę rozmiaru na rozmiar ImageViewobrazu? Np. Obraz 100dp x 150dp skalowałby się ImageViewdo tych samych miar? A może masz na myśli, jak przeskalować obraz do ImageViewgranic. Np. Obraz 1000 x 875 dp zostałby przeskalowany do 250 x 250 dp. Czy musisz zachować proporcje?
— Jarno Argillander

Chcę, aby ImageView miał wymiary obrazu, a obraz miał największy wymiar równy 250 dp i zachował proporcje. Np. Dla obrazu 100 * 150 chcę, aby obraz i ImageView miały wartość 166 * 250. Zaktualizuję moje pytanie.
— lipiec

Czy chcesz wykonywać skalowanie / dostosowywanie tylko podczas wyświetlania działania (zrób raz) lub podczas wykonywania czynności, takich jak wybieranie zdjęcia z galerii / sieci (wykonaj wiele razy, ale nie podczas ładowania), czy też jedno i drugie?
— Jarno Argillander

Zobacz moją zmodyfikowaną odpowiedź, która powinna zrobić dokładnie tak, jak sobie
— życzysz

Odpowiedzi:


137

(Odpowiedź została mocno zmieniona po wyjaśnieniu pierwotnego pytania)

Po wyjaśnieniach:
Nie można tego zrobić tylko w formacie XML . Nie jest możliwe przeskalowanie zarówno obrazu, jak i obrazu, ImageViewtak aby jeden wymiar obrazu zawsze wynosił 250 dp i ImageViewmiał takie same wymiary jak obraz.

Ten kod skaluje Drawable się, ImageViewaby pozostać w kwadracie, takim jak 250dp x 250dp, z jednym wymiarem dokładnie 250dp i zachowaniem proporcji. Następnie ImageViewrozmiar jest zmieniany w celu dopasowania do wymiarów skalowanego obrazu. Kod jest używany w działaniu. Przetestowałem to za pomocą programu obsługi kliknięć przycisków.

Cieszyć się. :)

private void scaleImage(ImageView view) throws NoSuchElementException  {
    // Get bitmap from the the ImageView.
    Bitmap bitmap = null;

    try {
        Drawable drawing = view.getDrawable();
        bitmap = ((BitmapDrawable) drawing).getBitmap();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("No drawable on given view");
    } catch (ClassCastException e) {
        // Check bitmap is Ion drawable
        bitmap = Ion.with(view).getBitmap();
    }

    // Get current dimensions AND the desired bounding box
    int width = 0;

    try {
        width = bitmap.getWidth();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("Can't find bitmap on given view/drawable");
    }

    int height = bitmap.getHeight();
    int bounding = dpToPx(250);
    Log.i("Test", "original width = " + Integer.toString(width));
    Log.i("Test", "original height = " + Integer.toString(height));
    Log.i("Test", "bounding = " + Integer.toString(bounding));

    // Determine how much to scale: the dimension requiring less scaling is
    // closer to the its side. This way the image always stays inside your
    // bounding box AND either x/y axis touches it.  
    float xScale = ((float) bounding) / width;
    float yScale = ((float) bounding) / height;
    float scale = (xScale <= yScale) ? xScale : yScale;
    Log.i("Test", "xScale = " + Float.toString(xScale));
    Log.i("Test", "yScale = " + Float.toString(yScale));
    Log.i("Test", "scale = " + Float.toString(scale));

    // Create a matrix for the scaling and add the scaling data
    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);

    // Create a new bitmap and convert it to a format understood by the ImageView 
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth(); // re-use
    height = scaledBitmap.getHeight(); // re-use
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);
    Log.i("Test", "scaled width = " + Integer.toString(width));
    Log.i("Test", "scaled height = " + Integer.toString(height));

    // Apply the scaled bitmap
    view.setImageDrawable(result);

    // Now change ImageView's dimensions to match the scaled image
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);

    Log.i("Test", "done");
}

private int dpToPx(int dp) {
    float density = getApplicationContext().getResources().getDisplayMetrics().density;
    return Math.round((float)dp * density);
}

Kod xml dla ImageView:

<ImageView a:id="@+id/image_box"
    a:background="#ff0000"
    a:src="@drawable/star"
    a:layout_width="wrap_content"
    a:layout_height="wrap_content"
    a:layout_marginTop="20dp"
    a:layout_gravity="center_horizontal"/>


Dzięki tej dyskusji dotyczącej kodu skalowania:
http://www.anddev.org/resize_and_rotate_image_-_example-t621.html


AKTUALIZACJA 7 listopada 2012:
Dodano sprawdzanie zerowego wskaźnika zgodnie z sugestią w komentarzach


1
ImageView zawsze będzie 250 * 250.
— lipiec

2
Dobrze. Nie można tego zrobić tylko w XML. Wymagany jest kod Java. Z XML możesz skalować obraz lub format ImageView, nie oba.
— Jarno Argillander

93
nie zdawałem sobie sprawy, że możesz zamienić android: na:
— StackOverflowed

2
Ion to framework do asynchronicznej sieci i ładowania obrazów: github.com/koush/ion
— Thomas,

1
Java jest wyjątkowo brzydkim językiem, ponieważ wymaga napisania tak dużej ilości kodu do tak prostych zadań.
— Dmitry

245

Może nie być odpowiedzią na to konkretne pytanie, ale jeśli ktoś, tak jak ja, szuka odpowiedzi, jak dopasować obraz w ImageView z ograniczonym rozmiarem (na przykład maxWidth), zachowując współczynnik proporcji, a następnie pozbyć się nadmiernej przestrzeni zajmowanej przez ImageView, to najprostszym rozwiązaniem jest użycie następujących właściwości w XML:

    android:scaleType="centerInside"
    android:adjustViewBounds="true"

13
Działa to, jeśli nie chcesz, aby obraz był powiększany, jeśli jest za mały.
— Janusz

jak go skalować, jeśli jest za mały, a także zachować proporcje?
— Kaustubh Bhagwat

jeśli ktoś potrzebuje, „fitCenter” jest kolejnym atrybutem dla scaleType i nie przeskaluje obrazu, ale dla każdego dużego obrazu zmieści maksymalny rozmiar obrazu w oknie widoku, zachowując proporcje
— yogesh prajapati

aby powiększyć małe obrazy, użyj zamiast tego scaleType = "centerCrop".
— Eaweb

Jeszcze jedną rzeczą dla mnie do pracy z tym rozwiązaniem jest użycie „android: src”, a nie „android: background” do refowania mojego obrazu.
— Codingpan

45
<ImageView android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:scaleType="centerCrop"
           android:adjustViewBounds="true"/>

23

Poniższy kod sprawia, że ​​bitmapa jest idealna przy tym samym rozmiarze widoku obrazu. Uzyskaj wysokość i szerokość obrazu bitmapowego, a następnie oblicz nową wysokość i szerokość za pomocą parametrów imageview. To daje wymagany obraz o najlepszym współczynniku proporcji.

int currentBitmapWidth = bitMap.getWidth();
int currentBitmapHeight = bitMap.getHeight();

int ivWidth = imageView.getWidth();
int ivHeight = imageView.getHeight();
int newWidth = ivWidth;

newHeight = (int) Math.floor((double) currentBitmapHeight *( (double) new_width / (double) currentBitmapWidth));

Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap, newWidth, newHeight, true);

imageView.setImageBitmap(newbitMap)

cieszyć się.


3
Spowoduje to po prostu zmniejszenie pierwotnej wysokości o ten sam współczynnik, o jaki zmniejszono szerokość. To nie gwarantuje, że newHeight <ivHeight. Najlepiej byłoby sprawdzić, który współczynnik jest większy (currentBitmapHeight / ivHeight, currentBitmapWidth / ivWidth) i na tej podstawie podjąć dalszą decyzję.
— Sumit Trehan

1
To faktycznie działa doskonale, chociaż nie potrzebujesz opcji ivHeight ani newWidth, po prostu umieść ivWidth w obliczeniach.
— Stuart

14

spróbuj dodać android:scaleType="fitXY"do swojego ImageView.


5
Spowoduje to zmianę współczynnika kształtu, jeśli oryginalny obraz nie jest kwadratowy.
— lipca

1
fitXYprawie zawsze zmieni proporcje obrazu. OP wyraźnie wspomina, że ​​MUSI zostać zachowany współczynnik kształtu.
— IcyFlame

7

Po wyszukaniu dnia myślę, że to najłatwiejsze rozwiązanie:

imageView.getLayoutParams().width = 250;
imageView.getLayoutParams().height = 250;
imageView.setAdjustViewBounds(true);

2
Dzięki za miłą odpowiedź, ale myślę, że lepiej jest dodać adjustViewBoundsdo XML-a

7

Najlepszym rozwiązaniem, które działa w większości przypadków, jest

Oto przykład:

<ImageView android:id="@+id/avatar"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY"/>

1
Nie polegaj na przestarzałym interfejsie API (fill_parent)
— fdermishin

jak to odpowiada na pytanie OP. To nie utrzyma współczynnika aspet
— Alex

6

to wszystko można zrobić za pomocą XML ... inne metody wydają się dość skomplikowane. W każdym razie wystarczy ustawić wysokość na dowolną wartość w dp, a następnie ustawić szerokość, aby zawijać zawartość lub odwrotnie. Użyj scaleType fitCenter, aby dopasować rozmiar obrazu.

<ImageView
    android:layout_height="200dp"
    android:layout_width="wrap_content"
    android:scaleType="fitCenter"
    android:adjustViewBounds="true"
    android:src="@mipmap/ic_launcher"
    android:layout_below="@+id/title"
    android:layout_margin="5dip"
    android:id="@+id/imageView1">

4

Użyj tego kodu:

<ImageView android:id="@+id/avatar"
           android:layout_width="fill_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY" />

4

Edytowana odpowiedź Jarno Argillandersa :

Jak dopasować obraz do szerokości i wysokości:

1) Zainicjuj ImageView i ustaw obraz:

iv = (ImageView) findViewById(R.id.iv_image);
iv.setImageBitmap(image);

2) Teraz zmień rozmiar:

scaleImage(iv);

Edytowana scaleImagemetoda: ( można zastąpić OCZEKIWANE wartości graniczne )

private void scaleImage(ImageView view) {
    Drawable drawing = view.getDrawable();
    if (drawing == null) {
        return;
    }
    Bitmap bitmap = ((BitmapDrawable) drawing).getBitmap();

    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int xBounding = ((View) view.getParent()).getWidth();//EXPECTED WIDTH
    int yBounding = ((View) view.getParent()).getHeight();//EXPECTED HEIGHT

    float xScale = ((float) xBounding) / width;
    float yScale = ((float) yBounding) / height;

    Matrix matrix = new Matrix();
    matrix.postScale(xScale, yScale);

    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth();
    height = scaledBitmap.getHeight();
    BitmapDrawable result = new BitmapDrawable(context.getResources(), scaledBitmap);

    view.setImageDrawable(result);

    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);
}

Oraz .xml:

<ImageView
    android:id="@+id/iv_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal" />

Myślę, że to rzutowanie: Parametry LinearLayout.LayoutParams = (LinearLayout.LayoutParams) view.getLayoutParams (); powinien iść w drugą stronę, ponieważ MarginLayoutParams dziedziczy po ViewGroup.LayoutParams.
— Jay Jacobs

3

Zrobiło to w moim przypadku.

             <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:scaleType="centerCrop"
                android:adjustViewBounds="true"
                />

2

jeśli to nie działa, zamień android: background na android: src

android: src odegra główną sztuczkę

    <ImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
    android:src="@drawable/bg_hc" />

działa dobrze jak urok

wprowadź opis obrazu tutaj


1

Musiałem mieć ImageView i Bitmap, więc Bitmap jest skalowany do rozmiaru ImageView, a rozmiar ImageView jest taki sam, jak skalowany Bitmap :).

Przeglądałem ten post, aby dowiedzieć się, jak to zrobić, iw końcu zrobiłem to, co chciałem, ale nie w sposób opisany tutaj.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/acpt_frag_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/imageBackground"
android:orientation="vertical">

<ImageView
    android:id="@+id/acpt_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:adjustViewBounds="true"
    android:layout_margin="@dimen/document_editor_image_margin"
    android:background="@color/imageBackground"
    android:elevation="@dimen/document_image_elevation" />

a następnie w metodzie onCreateView

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_scanner_acpt, null);

    progress = view.findViewById(R.id.progress);

    imageView = view.findViewById(R.id.acpt_image);
    imageView.setImageBitmap( bitmap );

    imageView.getViewTreeObserver().addOnGlobalLayoutListener(()->
        layoutImageView()
    );

    return view;
}

a następnie kod layoutImageView ()

private void layoutImageView(){

    float[] matrixv = new float[ 9 ];

    imageView.getImageMatrix().getValues(matrixv);

    int w = (int) ( matrixv[Matrix.MSCALE_X] * bitmap.getWidth() );
    int h = (int) ( matrixv[Matrix.MSCALE_Y] * bitmap.getHeight() );

    imageView.setMaxHeight(h);
    imageView.setMaxWidth(w);

}

Rezultatem jest to, że obraz idealnie pasuje do wnętrza, zachowując proporcje i nie ma dodatkowych pikseli z ImageView, gdy bitmapa jest w środku.

Wynik

Ważne jest, aby ImageView miał wrap_content i dostosowałViewBounds do true, wtedy setMaxWidth i setMaxHeight będą działać, jest to napisane w kodzie źródłowym ImageView,

/*An optional argument to supply a maximum height for this view. Only valid if
 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
 * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
 * layout params to WRAP_CONTENT. */

0

Musiałem to zrobić w układzie ograniczeń z Picassem, więc połączyłem niektóre z powyższych odpowiedzi i wymyśliłem to rozwiązanie (znam już proporcje obrazu, który ładuję, więc to pomaga):

Wywołane w moim kodzie aktywności gdzieś po setContentView (...)

protected void setBoxshotBackgroundImage() {
    ImageView backgroundImageView = (ImageView) findViewById(R.id.background_image_view);

    if(backgroundImageView != null) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = (int) Math.round(width * ImageLoader.BOXART_HEIGHT_ASPECT_RATIO);

        // we adjust the height of this element, as the width is already pinned to the parent in xml
        backgroundImageView.getLayoutParams().height = height;

        // implement your Picasso loading code here
    } else {
        // fallback if no element in layout...
    }
}

W moim XML

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteY="0dp"
tools:layout_editor_absoluteX="0dp">

    <ImageView
        android:id="@+id/background_image_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="fitStart"
        app:srcCompat="@color/background"
        android:adjustViewBounds="true"
        tools:layout_editor_absoluteY="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginBottom="0dp"
        android:layout_marginRight="0dp"
        android:layout_marginLeft="0dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <!-- other elements of this layout here... -->

</android.support.constraint.ConstraintLayout>

Zwróć uwagę na brak atrybutu constraintBottom_toBottomOf. ImageLoader to moja własna klasa statyczna do ładowania obrazów metodami i stałymi.


0

Używam bardzo prostego rozwiązania. Tutaj mój kod:

imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.getLayoutParams().height = imageView.getLayoutParams().width;
imageView.setMinimumHeight(imageView.getLayoutParams().width);

Moje zdjęcia są dodawane dynamicznie w widoku siatki. Po wprowadzeniu tych ustawień w widoku obrazu obraz może być automatycznie wyświetlany w proporcjach 1: 1.


0

Użyj prostej matematyki, aby zmienić rozmiar obrazu. albo możesz zmienić rozmiar, ImageViewalbo możesz zmienić rozmiar rysowanego obrazu niż ustawiony ImageView. znajdź szerokość i wysokość swojej mapy bitowej, którą chcesz ustawić ImageViewi wywołaj żądaną metodę. załóżmy, że twoja szerokość 500 jest większa niż wysokość niż metoda wywołania

//250 is the width you want after resize bitmap
Bitmat bmp = BitmapScaler.scaleToFitWidth(bitmap, 250) ;
ImageView image = (ImageView) findViewById(R.id.picture);
image.setImageBitmap(bmp);

Używasz tej klasy do zmiany rozmiaru mapy bitowej.

public class BitmapScaler{
// Scale and maintain aspect ratio given a desired width
// BitmapScaler.scaleToFitWidth(bitmap, 100);
 public static Bitmap scaleToFitWidth(Bitmap b, int width)
  {
    float factor = width / (float) b.getWidth();
    return Bitmap.createScaledBitmap(b, width, (int) (b.getHeight() * factor), true);
  }


  // Scale and maintain aspect ratio given a desired height
  // BitmapScaler.scaleToFitHeight(bitmap, 100);
  public static Bitmap scaleToFitHeight(Bitmap b, int height)
  {
    float factor = height / (float) b.getHeight();
    return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true);
   }
 }

kod xml to

<ImageView
android:id="@+id/picture"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:adjustViewBounds="true"
android:scaleType="fitcenter" />

0

Szybka odpowiedź:

<ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@drawable/yourImage"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.