2012-12-21 2 views
68

Часто спрашивается, никогда не отвечает (по крайней мере, не воспроизводимым образом).Размер изображения для Android ImageView уменьшает изображение до ширины с гибкой высотой без обрезки или искажения

У меня есть изображение с изображением меньше чем вид. Я хочу масштабировать изображение по ширине экрана и регулировать высоту ImageView, чтобы отразить пропорционально правильную высоту изображения.

<ImageView 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
/> 

Это приводит к изображению с центром в точке своего первоначального размера (меньше, чем ширина экрана) с полями на стороне. Не хорошо.

Поэтому я добавил

android:adjustViewBounds="true" 

Тот же эффект, ни к чему хорошему. Я добавил

android:scaleType="centerInside" 

Такой же эффект, не хорошо. Я изменил centerInside на fitCenter. Тот же эффект, ничего хорошего. Я изменил centerInside на centerCrop.

android:scaleType="centerCrop" 

Теперь, наконец, изображение масштабируется к ширине экрана - но обрезается сверху и снизу! Поэтому я изменил centerCrop на fitXY.

android:scaleType="fitXY" 

Теперь изображение масштабируется по ширине экрана, но не масштабируется по оси у, в результате чего искажено изображение.

Удаление android:adjustViewBounds="true" не имеет эффекта. Добавление android:layout_gravity, как было предложено в другом месте, снова не влияет.

Я пробовал другие комбинации - безрезультатно. Итак, пожалуйста, кто-нибудь знает:

Как настроить XML ImageView для заполнения ширины экрана, масштабировать меньшее изображение, чтобы заполнить весь вид, отображая изображение с его форматным соотношением без искажений или кадрирование?

EDIT: Я также попытался установить произвольную числовую высоту. Это влияет только на настройку centerCrop. Он будет искажать изображение по вертикали в соответствии с высотой обзора.

+0

Вы пробовали 'андроида: scaleType = "fitCenter"'? – MCeley

+0

@MCeley у вас есть? (спойлер: он не работает.) – BrainSlugs83

+0

@ BrainSlugs83 У меня есть и все работает для меня. Кроме того, был задан вопрос о том, что просил автор. Нет необходимости в ловкости, особенно через семь месяцев после ее публикации. – MCeley

ответ

18

Существует нет жизнеспособное решение в стандарте макета XML.

Единственный надежный способ реагирования на размер динамического изображения - использовать код LayoutParams.

Разочарование.

7
android:scaleType="fitCenter" 

Вычислить масштаб, который будет поддерживать исходное соотношение сторон SRC, но также гарантирует, что SRC подходит полностью внутри ДСТ. По крайней мере одна ось (X или Y) будет точно соответствовать. Результат центрирован внутри dst.

редактировать:

Проблема здесь состоит в том, что layout_height="wrap_content" не «позволяет» изображение расширяться.Не Вы должны установить размер для него, для этого изменения

android:layout_height="wrap_content" 

в

android:layout_height="100dp" // or whatever size you want it to be 

edit2:

работает отлично:

<ImageView 
    android:id="@+id/imageView1" 
    android:layout_width="match_parent" 
    android:layout_height="300dp" 
    android:scaleType="fitCenter" 
    android:src="@drawable/img715945m" /> 
+1

Это приводит к первой версии изображения: по центру, не по горизонтали, с полями с обеих сторон. Не хорошо. – Mundi

+0

отредактировал мой ответ, проверьте его – Budius

+1

Это не сработает. Это приводит к изображению с точной высотой, но не расширяется горизонтально. Не хорошо. Также см. Мое редактирование в вопросе. – Mundi

105

Я решил эту проблему, создав Java-класс, который вы включить в вашем макете-файл:

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

Теперь вы используете это добавленние вашего класса на свой макет-файл:

<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> 
+4

+1 Отличное решение, отлично работает и для Nexus 7. И лучшая часть, которая также корректно работает в редакторе макетов в Eclipse. – Ridcully

+0

Работы на Галактике S4. Почему бы это не сделать :) THx – Malachiasz

+0

Спасибо. Отличное решение. Достаточно легко изменить, чтобы сделать то же самое на основе heightMeasureSpec. – speedynomads

3

Я столкнулся с той же проблемой, но с фиксированной высотой, масштабированной шириной, сохраняющей исходное соотношение сторон изображения. Я решил его с помощью взвешенной линейной компоновки. Вы можете надеяться изменить его для своих нужд.

<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> 
+0

Это решение отлично подойдет для меня. Это позволило мне дать моей ImageView определенную высоту, и ImageView динамически регулирует ее ширину, чтобы поддерживать ее соотношение сторон. – Luke

7

Это небольшое дополнение к отличному решению Марка Мартинсона.

Если ширина вашего изображения больше его высоты, тогда решение Марка оставит пространство вверху и внизу экрана.

Нижеследующее исправляет это путем сравнения ширины и высоты: если ширина изображения> = высота, то она будет масштабировать высоту в соответствии с высотой экрана, а затем масштабировать ширину, чтобы сохранить соотношение сторон. Аналогично, если высота изображения> ширина, то она будет масштабировать ширину в соответствии с шириной экрана, а затем масштабировать высоту, чтобы сохранить соотношение сторон.

Другими словами, он должным образом удовлетворяет определению 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); 
     } 
    } 
} 

Это решение работает автоматически в любом портретном или ландшафтном режиме. Вы ссылаетесь на него в своем макете так же, как и на решение Марка. Например:

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

У меня была та же проблема, что и у вас. После 30 минут попыток различного разнообразия я решил проблему таким образом.Это дает гибкой высоте и ширину регулируется родитель ширина больше

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

Ключ здесь - использовать adjustViewBounds – user3690202

0

одно дополнения к решению Марки Matinsson в. Я обнаружил, что некоторые из моих изображений были более масштабированными, чем другие. Поэтому я изменил класс так, чтобы изображение масштабировалось с максимальным коэффициентом. Если изображение слишком мало для масштабирования с максимальной шириной без размытости, оно перестает масштабироваться за пределы максимального предела.

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

}

0

Котлин версия Марка Martinsson в answer:

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