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.

lip
źródło
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

Jarno Argillander
źródło
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"
Wyświetlana nazwa
źródło
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"/>
diesel
źródło
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ę.

Naresh Sharma
źródło
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.

DeeFour
źródło
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);
MikeyB
źródło
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"/>
Nikita Vishwakarma
źródło
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">
Piorun
źródło
4

Użyj tego kodu:

<ImageView android:id="@+id/avatar"
           android:layout_width="fill_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY" />
Abhay Anand
źródło
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" />
Volodymyr Kulyk
źródło
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"
                />
Ronny Kibet
źródło
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

Harvinder Singh
źródło
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. */
alekshandru
źródło
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.

the_dude_abides
źródło
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.

Bilal Demir
źródło
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" />
Ritesh Namdev
źródło
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" />
myworldbox
źródło