Просмотрщик фото для Андроид масштабировать изображение меньшего размера по ширине с гибкими высота без обрезки и искажений


часто спрашивали, никогда не отвечали (по крайней мере, не воспроизводимым способом).

у меня есть вид изображения с изображением, которое составляет меньше чем смотреть. Я хочу масштабировать изображение до ширины экрана и настроить высоту 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"

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

android:scaleType="fitXY"

теперь изображение масштабируется до ширины но не масштабируется по оси y, что приводит к искаженные изображения.

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

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

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

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

8 69

8 ответов:

Я решил эту проблему, создав 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>

У меня была такая же проблема, как у тебя. После 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" />

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

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

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

  android:scaleType="fitCenter"

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

edit:

проблема здесь в том, что 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" />

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

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

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

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

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

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

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

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

}

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

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