it-roy-ru.com

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"

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

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

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

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

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

73
Mundi

В стандарте компоновки XML нет жизнеспособного решения. 

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

Неудовлетворительно.

20
Mundi

Я решил это, создав 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>
108
Mark Martinsson

У меня была такая же проблема, как и у тебя. После 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" />
31
haydarkarrar
  Android:scaleType="fitCenter"

Вычислите масштаб, который будет поддерживать исходное соотношение сторон src, но также будет гарантировать, что src полностью помещается в dst. По крайней мере одна ось (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" />
7
Budius

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

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

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

Другими словами, он правильно удовлетворяет определению 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" />
7
Michael Christoff

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

<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>
3
tbm

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

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

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

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
azmath