Często pytany, nigdy nie odpowiadał (przynajmniej nie w powtarzalny sposób).
Mam widok obrazu z obrazem, który jest mniejszy niż widok. Chcę przeskalować obraz do szerokości ekranu i dostosować wysokość ImageView, aby odzwierciedlić proporcjonalnie poprawną wysokość obrazu.
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Skutkuje to wyśrodkowaniem obrazu w jego oryginalnym rozmiarze (mniejszym niż szerokość ekranu) z marginesami z boku. Nie dobrze.
Więc dodałem
android:adjustViewBounds="true"
Ten sam efekt, niedobry. dodałem
android:scaleType="centerInside"
Ten sam efekt, niedobry. Zmieniłem centerInside
się fitCenter
. Ten sam efekt, niedobry. Zmieniłem centerInside
się centerCrop
.
android:scaleType="centerCrop"
Wreszcie obraz jest skalowany do szerokości ekranu - ale przycięty na górze i na dole! Więc zmieniłem centerCrop
na fitXY
.
android:scaleType="fitXY"
Teraz obraz jest skalowany do szerokości ekranu, ale nie jest skalowany na osi Y, co powoduje zniekształcenie obrazu.
Usunięcie android:adjustViewBounds="true"
nie ma żadnego efektu. Dodanie android:layout_gravity
, jak zasugerowano w innym miejscu, znowu nie ma żadnego efektu.
Próbowałem innych kombinacji - bezskutecznie. Więc proszę, czy ktoś wie:
Jak skonfigurować XML ImageView, aby wypełniał szerokość ekranu, przeskalował mniejszy obraz, aby wypełnić cały widok, wyświetlając obraz ze współczynnikiem proporcji bez zniekształceń i przycinania?
EDYCJA: Próbowałem też ustawić dowolną wysokość liczbową. Ma to wpływ tylko na centerCrop
ustawienie. Zniekształci obraz w pionie zgodnie z wysokością widoku.
źródło
android:scaleType="fitCenter"
?Odpowiedzi:
Rozwiązałem to, tworząc klasę java, którą umieszczasz w pliku układu:
public class DynamicImageView extends ImageView { public DynamicImageView(final Context context, final AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if (d != null) { // ceil not round - avoid thin vertical gaps along the left/right edges final int width = MeasureSpec.getSize(widthMeasureSpec); final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
Teraz możesz użyć tego, dodając swoją klasę do pliku układu:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <my.package.name.DynamicImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@drawable/about_image" /> </RelativeLayout>
źródło
Miałem ten sam problem, co twój. Po 30 minutach prób różnych wariantów rozwiązałem problem w ten sposób. Zapewnia elastyczną wysokość i szerokość dostosowaną do szerokości rodzica
<ImageView android:id="@+id/imageview_company_logo" android:layout_width="fill_parent" android:layout_height="fill_parent" android:adjustViewBounds="true" android:scaleType="fitCenter" android:src="@drawable/appicon" />
źródło
Nie ma realnego rozwiązania w ramach standardu układu XML.
Jedynym niezawodnym sposobem reagowania na dynamiczny rozmiar obrazu jest użycie
LayoutParams
w kodzie.Niezadowalający.
źródło
android:scaleType="fitCenter"
Oblicz skalę, która zachowa oryginalny współczynnik proporcji src, ale zapewni również, że src zmieści się całkowicie w dst. Przynajmniej jedna oś (X lub Y) będzie dokładnie pasować. Wynik jest wyśrodkowany wewnątrz dst.
edytować:
Problem polega na tym, że
layout_height="wrap_content"
nie „pozwala” na rozszerzenie obrazu. Będziesz musiał ustawić dla niego rozmiar, na tę zmianęandroid:layout_height="wrap_content"
do
android:layout_height="100dp" // or whatever size you want it to be
edit2:
działa w porządku:
<ImageView android:id="@+id/imageView1" android:layout_width="match_parent" android:layout_height="300dp" android:scaleType="fitCenter" android:src="@drawable/img715945m" />
źródło
To mały dodatek do doskonałego rozwiązania Marka Martinssona.
Jeśli szerokość obrazu jest większa niż jego wysokość, rozwiązanie Marka pozostawi miejsce na górze i na dole ekranu.
Poniższe rozwiązanie rozwiązuje ten problem, najpierw porównując szerokość i wysokość: jeśli szerokość obrazu> = wysokość, przeskaluje wysokość, aby dopasować ją do wysokości ekranu, a następnie przeskaluje szerokość, aby zachować proporcje. Podobnie, jeśli wysokość obrazu> szerokość, przeskaluje szerokość w celu dopasowania do szerokości ekranu, a następnie wyskaluje wysokość, aby zachować proporcje.
Innymi słowy, poprawnie spełnia definicję
scaleType="centerCrop"
:http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
package com.mypackage; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; public class FixedCenterCrop extends ImageView { public FixedCenterCrop(final Context context, final AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if(d != null) { int height = MeasureSpec.getSize(heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); if(width >= height) height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); else width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
To rozwiązanie automatycznie działa w trybie pionowym lub poziomym. Odwołujesz się do niego w swoim układzie, tak jak robisz to w rozwiązaniu Marka. Na przykład:
<com.mypackage.FixedCenterCrop android:id="@+id/imgLoginBackground" android:src="@drawable/mybackground" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:scaleType="centerCrop" />
źródło
Napotkałem ten sam problem, ale ze stałą wysokością i skalowaną szerokością zachowując oryginalne proporcje obrazu. Rozwiązałem to za pomocą ważonego układu liniowego. Miejmy nadzieję, że możesz go zmodyfikować do swoich potrzeb.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/image" android:layout_width="0px" android:layout_height="180dip" android:layout_weight="1.0" android:adjustViewBounds="true" android:scaleType="fitStart" /> </LinearLayout>
źródło
Kotlin wersja Mark Martinsson odpowiedź :
class DynamicImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val drawable = this.drawable if (drawable != null) { //Ceil not round - avoid thin vertical gaps along the left/right edges val width = View.MeasureSpec.getSize(widthMeasureSpec) val height = Math.ceil((width * drawable.intrinsicHeight.toFloat() / drawable.intrinsicWidth).toDouble()).toInt() this.setMeasuredDimension(width, height) } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec) } }
źródło
Jeszcze jeden dodatek do rozwiązania Marka Matinssona. Zauważyłem, że niektóre z moich obrazów były przeskalowane niż inne. Więc zmodyfikowałem klasę tak, aby obraz był skalowany o maksymalny współczynnik. Jeśli obraz jest zbyt mały, aby można go było przeskalować z maksymalną szerokością bez rozmycia, przestaje skalować poza maksymalny limit.
public class DynamicImageView extends ImageView { final int MAX_SCALE_FACTOR = 2; public DynamicImageView(final Context context) { super(context); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if (d != null) { // ceil not round - avoid thin vertical gaps along the left/right edges int width = View.MeasureSpec.getSize(widthMeasureSpec); if (width > (d.getIntrinsicWidth()*MAX_SCALE_FACTOR)) width = d.getIntrinsicWidth()*MAX_SCALE_FACTOR; final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
źródło