Android: масштабировать графическое или фоновое изображение?

134

В макете я хочу масштабировать фоновое изображение (сохраняя его пропорции) до пространства, выделенного при создании страницы. У кого-нибудь есть идеи как это сделать?

Я использую layout.setBackgroundDrawable()и BitmapDrawable()для установки gravityдля отсечения и заполнения, но не вижу никакой опции для масштабирования.

finnmglas
источник
У меня возникла эта проблема, и я последовал рекомендации Dweebo с предложенным Анке изменением: изменил fitXY на fitCenter. Сработало хорошо.

Ответы:

90

Чтобы настроить масштабирование фоновых изображений, создайте такой ресурс:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

Тогда он будет центрирован в виде, если используется в качестве фона. Есть и другие флаги: http://developer.android.com/guide/topics/resources/drawable-resource.html

Алекс Н.
источник
6
Я пытался использовать растровое изображение, но изображение просто центрировано, а не масштабировано, как просит Сэм. Когда я использую ImageView, он работает прекрасно, без суеты. (nb: я также не прокручиваю в моем случае.) Чем бы я увеличил растровое изображение, чтобы масштабировать изображение?
Джо д'Андреа
7
Возможные значения: «top» | "дно" | "левый" | "правильно" | "center_vertical" | "fill_vertical" | "Горизонтальный центр" | "fill_horizont" | "центр" | "заполнить" | "clip_vertical" | "clip_horizontal"
Алекс Н.
30
ни один из этих параметров не масштабирует растровое изображение, сохраняя его соотношение сторон, поэтому ответ неверен.
Малахияш
7
Уважаемый Malachiasz, «центр» уважает соотношение сторон. Но это не уменьшит изображение. Это означает, что это довольно опасная функция для использования. В зависимости от разрешения цели вы никогда не узнаете, насколько масштабной будет полученная вещь. Еще одно недоделанное решение от Google.
2
есть способ добавить атрибут scaleType?
ademar111190
57

Не пытался делать именно то, что вы хотите, но вы можете масштабировать с помощью ImageView, android:scaleType="fitXY"
и он будет соответствовать размеру, который вы даете ImageView.

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

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>
dweebo
источник
1
Возможно ли сделать это на поверхности. Если нет, я могу показать предварительный просмотр (для приложения рекордера), используя FrameLayout?
Намрата
1
@dweebo: ты пробовал это? Кажется, это не работает для меня.
скоростной самолет
3
Понижено, поскольку использование двух компонентов пользовательского интерфейса вместо одного - плохая идея, особенно для таких загруженных компонентов, как ListView. Скорее всего, у вас будут проблемы при прокрутке. Правильное решение: stackoverflow.com/a/9362168/145046
Алекс Н.
Я попробовал масштабированный тип fitCenter и centerCrop (вместе с несколькими вариантами рисования / изображения для разных плотностей), и это сработало! Любой тип шкалы имел смысл для меня - больше личного предпочтения, основанного на изображении, я подозреваю. Обратите внимание, что я использовал слияние вместо FrameLayout, и в моем конкретном случае прокрутки нет.
Джо д'Андреа
2
centerInside правильно, не fitXY, так как fitXY не сохраняет пропорции изображения
Malachiasz
35

Есть простой способ сделать это из отрисовки:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

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

Другой способ - просто создать макет, в котором вы будете использовать ImageViewи установить scaleTypeзначение fitCenter.

Ионут Негру
источник
2
Это решение определенно то, что я давно искал, чтобы заменить свойство фона моей темы и избежать изменения масштаба изображения, независимо от того, есть ли в представлении вкладки или нет. Спасибо, что поделились этим.
Бибу
1
Показывает ошибку:error: <item> must have a 'name' attribute.
Дмитрий
спасибо @lonutNegru, +1 за спасение моего дня :)
Рави Вания
18

Чтобы сохранить соотношение сторон, вы должны использовать android:scaleType=fitCenterили fitStartт. Д. Использование fitXYне сохранит исходное соотношение сторон изображения!

Обратите внимание, что это работает только для изображений с srcатрибутом, но не для фонового изображения.

Анке
источник
18

Используйте изображение в качестве фона для макета:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >


    </LinearLayout>

</FrameLayout>
Константин Конопко
источник
Спасибо! Также android:scaleType="centerCrop"работает.
CoolMind,
5

Когда вы устанавливаете Drawable для ImageView с помощью setBackgroundDrawableметода, изображение всегда будет масштабироваться. Параметры as adjustViewBoundsor different ScaleTypesбудут игнорироваться. Единственное решение сохранить найденное мной соотношение сторон - это изменить размер файла ImageViewпосле загрузки. Вот фрагмент кода, который я использовал:

// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;

if (h2w > 1) {
    // height is greater than width
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth * h2w;
    } else {
        newContainerHeight = (float) containerHeight;
        newContainerWidth = newContainerHeight / h2w;
    }
} else {
    // width is greater than height
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth / h2w; 
    } else {
        newContainerWidth = (float) containerHeight;
        newContainerHeight = newContainerWidth * h2w;       
    }
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);

В приведенном выше фрагменте кода bmp - это объект Bitmap, который должен отображаться, а imageView - это ImageViewобъект.

Важно отметить изменение параметров макета . Это необходимо, потому что setMaxHeightи setMaxWidthбудет иметь значение только в том случае, если ширина и высота определены для обертывания содержимого, а не для заполнения родительского элемента. Заливка родителя с другой стороны , это желаемое значение в начале, потому что иначе containerWidthи containerHeightоба будут иметь значения , равные 0. Итак, в вашем файле макета вы будете иметь что - то подобное для вашего ImageView:

...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...
maddob
источник
4

Это не самое эффективное решение, но, как кто-то предложил вместо фона, вы можете создать FrameLayout или RelativeLayout и использовать ImageView в качестве псевдофона - другие элементы будут расположены прямо над ним:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitStart"
        android:src="@drawable/menu_icon_exit" />

    <Button
        android:id="@+id/bSomeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="61dp"
        android:layout_marginTop="122dp"
        android:text="Button" />

</RelativeLayout>

Проблема с ImageView заключается в том, что доступны только типы scaleTypes: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html). )

и для «масштабирования фонового изображения (сохраняя его соотношение сторон)» в некоторых случаях, когда вы хотите, чтобы изображение заполняло весь экран (например, фоновое изображение), а соотношение сторон экрана отличается от изображения, необходимый scaleType является любезным из TOP_CROP, потому что:

CENTER_CROP центрирует масштабированное изображение вместо выравнивания верхнего края по верхнему краю представления изображения, а FIT_START соответствует высоте экрана и не заполняет ширину. И, как заметила пользователь Анке, FIT_XY не сохраняет соотношение сторон.

К счастью, кто-то расширил ImageView для поддержки TOP_CROP

public class ImageViewScaleTypeTopCrop extends ImageView {
    public ImageViewScaleTypeTopCrop(Context context) {
        super(context);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup() {
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {

        float frameWidth = frameRight - frameLeft;
        float frameHeight = frameBottom - frameTop;

        if (getDrawable() != null) {

            Matrix matrix = getImageMatrix();
            float scaleFactor, scaleFactorWidth, scaleFactorHeight;

            scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
            scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();

            if (scaleFactorHeight > scaleFactorWidth) {
                scaleFactor = scaleFactorHeight;
            } else {
                scaleFactor = scaleFactorWidth;
            }

            matrix.setScale(scaleFactor, scaleFactor, 0, 0);
            setImageMatrix(matrix);
        }

        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

https://stackoverflow.com/a/14815588/2075875

Теперь ИМХО было бы идеально, если бы кто-нибудь написал собственный Drawable, который так масштабирует изображение. Тогда его можно было бы использовать как параметр фона.


Reflog предлагает предварительно масштабировать drawable перед его использованием. Вот инструкция, как это сделать: Java (Android): Как масштабировать чертеж без растрового изображения? Хотя у него есть недостаток, это масштабируемое изображение / растровое изображение будет использовать больше оперативной памяти, в то время как масштабирование на лету, используемое ImageView, не требует больше памяти. Преимущество могло заключаться в меньшей загрузке процессора.

Malachiasz
источник
Хороший! Делает именно то, что мне нужно: фоновое изображение для заполнения всего экрана за счет пропорционального увеличения ширины и обрезки нижней части изображения.
CMash
4

Код ниже делает растровое изображение идеально с тем же размером изображения. Получите высоту и ширину растрового изображения, а затем вычислите новую высоту и ширину с помощью параметров изображения. Это даст вам необходимое изображение с наилучшим соотношением сторон.

int bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)
Нареш Шарма
источник
2

То, что предложил Двибо, работает. Но, по моему скромному мнению, в этом нет необходимости. Фоновый рисунок хорошо масштабируется сам по себе. Вид должен иметь фиксированную ширину и высоту, как в следующем примере:

 < RelativeLayout 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="500dip"
        android:layout_height="450dip"
        android:layout_centerInParent="true"
        android:background="@drawable/my_drawable"
        android:orientation="vertical"
        android:padding="30dip"
        >
        ...
     </LinearLayout>
 < / RelativeLayout>
Yar
источник
2
Насколько я понимаю, фон будет расширяться, чтобы заполнить вид, но не сжиматься.
Эдвард Фальк,
1

Один из вариантов, который можно попробовать - поместить изображение в drawable-nodpiпапку и установить для фона макета идентификатор ресурса с возможностью рисования.

Это определенно работает с уменьшением масштаба, хотя я не тестировал масштабирование.

Sababado
источник
0

Котлин:

Если вам нужно было нарисовать растровое изображение в представлении, масштабируйте его до FIT.

Вы можете выполнить правильные вычисления, чтобы установить высоту bm, равную контейнеру, и отрегулировать ширину, если отношение ширины bm к высоте меньше, чем отношение ширины контейнера к высоте, или обратное в противоположном сценарии.

Изображений:

Пример изображения 1

Пример изображения 2

// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()

if (rb < rc) {
    // Bitmap Width to Height ratio is less than Container ratio
    // Means, bitmap should pin top and bottom, and have some space on sides.
    //              _____          ___
    // container = |_____|   bm = |___| 
    val bmHeight = ch - 4 //4 for container border
    val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
    val bmWidth = cw - 4 //4 for container border
    val bmHeight = 1f/rb * cw
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}
danitinez
источник
-4

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

reflog
источник
Неужели это единственный вариант? Нет ли типа контейнера для размещения ImageView, в котором можно указать размер отображения?
GrkEngineer
У меня также проблемы с SurfaceView
AZ_
3
Нет ничего лучше, чем форматирование, заполнение, верхний левый, верхний и т.д., как на iOS? Это отстой ..
Maciej Swic
Как сказал @Aleksej, неверный ответ.
ученик
-5

Вы можете использовать одно из следующих:

Android: гравитация = "fill_horizontal | clip_vertical"

Или

Android: гравитация = "fill_vertical | clip_horizontal"

Эли Бэйнсей
источник