Android ImageView зменшує зображення до ширини із гнучкою висотою без обрізання та спотворення


79

Часто запитують, ніколи не відповідають (принаймні не відтворюваним чином).

У мене є перегляд зображення із зображенням, яке менше, ніж вигляд. Я хочу масштабувати зображення до ширини екрана та відрегулювати висоту 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 для заповнення ширини екрана, масштабування меншого зображення, щоб заповнити весь вигляд, відображаючи зображення з його співвідношенням сторін без спотворень або обрізання?

EDIT: Я також спробував встановити довільну числову висоту. Це впливає лише на centerCropналаштування. Це спотворить зображення вертикально відповідно до висоти огляду.


Ви пробували android:scaleType="fitCenter"?
Michael Celey

1
@ BrainSlugs83 у мене є, і це все ще працює для мене. Крім того, було поставлено питання, щоб визначити, що спробував особа. Немає необхідності в спритності, особливо не через сім місяців після її розміщення.
Michael Celey

Вона не працює. -- Дивись нижче; Крім того, я спробував і можу переконатися, що він не працює (якщо ви спробували і побачите різні результати, обговоріть нижче і покажіть нам, що ми робимо неправильно - єдиний висновок, до якого я можу прийти, це те, що ви неправильно зрозуміли питання, або не пробували).
BrainSlugs83,

Відповіді:


110

Я вирішив це, створивши клас 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

Працює на Galaxy S4. Чому не так :) THx
Malachiasz

Дякую. Чудове рішення. Досить просто модифікувати, щоб зробити те саме на основі heightMeasureSpec.
speedynomads

1
Так, але ні ... Це рішення розмиває зображення. Якщо я використовую ImageView та fitCenter із фіксованою висотою, зображення не затьмарюється (але фіксована висота для мене не є рішенням). Як уникнути розмиття?
Гельтруда

Я шукав це рішення тижнями, слава! Хоча я хочу збільшити масштаб одного зображення, але це працює ідеально
Соко

36

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

4
Ключовим тут є використання AdjuViewBounds
user3690202

@ user3690202 ти маєш рацію, це вирішило і мою проблему. Подяка
Парт Патель

22

В рамках стандарту розмітки XML немає життєздатного рішення.

Єдиним надійним способом реагувати на динамічний розмір зображення є використання LayoutParamsв коді.

Невтішний.


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

2
Це призводить до першої версії зображення: по центру, а не розгорнутий горизонтально, з полями з обох сторін. Не добре.
Mundi

2
Це не працює. У результаті виходить зображення з точною висотою, але не розширене горизонтально. Не добре. Також див. Мою редакцію у питанні.
Мунді,

1
nasa.gov/images/content/715945main__NOB0093_4x3_516-387.jpg Важливою є те, що він здається меншим, ніж екран Nexus 7.
Мунді,

2
Не добре. Я не знаю висоти - вона динамічна. Це призведе до непередбачуваних спотворень. Правка в моєму питанні це вже охоплює. Вибачте - але дякую за ваші зусилля! Ганьба в Google!
Mundi

1
Я знав, що це варіант, але шукав рішення XML . Тим не менше, вам слід відредагувати це у своїй відповіді. Дякую!
Mundi

8

Це невелике доповнення до чудового рішення Марка Мартінссона.

Якщо ширина вашого зображення перевищує його висоту, рішення Марка залишить простір у верхній і нижній частині екрана.

Нижче це виправлено, порівнявши ширину та висоту: якщо ширина зображення> = висота, тоді вона буде масштабувати висоту відповідно до висоти екрану, а потім масштабувати ширину, щоб зберегти пропорції. Подібним чином, якщо висота зображення> ширина, тоді вона буде масштабувати ширину відповідно до ширини екрана, а потім масштабувати висоту, щоб зберегти пропорції.

Іншими словами, він належним чином відповідає визначенню 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" />

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>

Це рішення для мене чудово працювало. Це дозволило мені надати моєму ImageView певну висоту і мати ImageView динамічне регулювання його ширини, щоб зберегти співвідношення сторін.
Лука

0

Котлін версія Марка Martinsson в відповідь :

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

Ще одне доповнення до рішення Марка Матінсона. Я виявив, що деякі мої зображення були надмірно масштабованими, ніж інші. Тож я модифікував клас так, щоб зображення масштабувалось на максимальний коефіцієнт. Якщо зображення занадто мало для масштабування на максимальну ширину, не розмито, воно перестає масштабуватися за максимальну межу.

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);
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.