Android: масштабування малювального або фонового зображення?


134

На макеті я хочу масштабувати фонове зображення (зберігаючи його співвідношення сторін) до місця, виділеного під час створення сторінки. Хтось має уявлення, як це зробити?

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


У мене виникла ця проблема, і я дотримувався рекомендації Двебо зі зміною, запропонованою Енке: змінив 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/dravable-resource.html


6
Я спробував використовувати растрову карту, але зображення просто по центру, а не масштабується, як просить Сем. Коли я використовую ImageView, він працює чудово, немає ніяких суєт / суєти. (nb: Я також не прокручую у своєму випадку.) Що б я збільшив растрову карту, щоб масштабувати зображення?
Джо Д'Андреа

7
Можливі значення: "верх" | "дно" | "зліва" | "правильно" | "center_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "центр" | "заливка" | "clip_vertical" | "clip_horizontal"
Алекс Н.

30
жоден із цих параметрів не масштабує растрову карту, зберігаючи її пропорцію, тому відповідь є неправильною.
Малахіаш

7
Шановний Малахіаш, "центр" поважає співвідношення сторін. Але це не зменшить масштаб зображення. Що означає, що це дещо небезпечна функція використання. Залежно від дозволу цілі, ви ніколи не знаєте, наскільки точно вийде річ. Ще одне напівсистематичне рішення від 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>

1
Чи можливо це зробити на поверхні. Якщо ні, чи можу я показати попередній перегляд (для програми для запису записів) за допомогою FrameLayout?
Намратха

1
@dweebo: Ви пробували це? Здається, це не працює для мене.
швидкісний літак

3
Незважаючи на те, що використовувати два компоненти інтерфейсу замість одного, це погана ідея, особливо для таких зайнятих компонентів, як ListView. Швидше за все, у вас виникнуть проблеми під час прокрутки. Правильне рішення: stackoverflow.com/a/9362168/145046
Алекс Н.

Я спробував scaleType fitCenter та centerCrop (разом з кількома варіаціями малювання / зображення для різної щільності), і він працював! Будь-який тип шкали мав сенс для мене - я підозрюю, що більше особистих уподобань на основі іміджу. Зауважте, що я використовував об'єднання замість FrameLayout, і в моєму конкретному випадку немає прокрутки.
Джо Д'Андреа

2
centerInside належним чином, не fitXY, оскільки fitXY не підтримує співвідношення сторін зображення
Малахіаш

35

Існує простий спосіб зробити це з малюючого:

your_dravable.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.
Дмитро

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

Якщо ви встановите Dravable of ImageView за допомогою setBackgroundDrawableметоду, зображення завжди буде масштабуватися. Параметри як adjustViewBoundsабо різні 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"/>
...

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 полягає в тому, що доступні лише шкали типу: 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 )

та "масштабування фонового зображення (збереження його співвідношення сторін)" в деяких випадках, коли ви хочете, щоб зображення заповнило весь екран (наприклад, фонове зображення), а співвідношення сторін екрана відрізняється від зображення, необхідний масштаб з TOP_CROP, оскільки:

CENTER_CROP центрує масштабоване зображення замість вирівнювання верхнього краю до верхнього краю подання зображення, і FIT_START відповідає висоті екрана, а не заповнює ширину. І як помітив користувач Anke, 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

Тепер ІМХО було б ідеально, якби хтось написав власну програму Dravable, яка масштабує зображення. Тоді його можна використовувати як фоновий параметр.


Reflog пропонує передбачити масштаб малюнку перед його використанням. Ось інструкція, як це зробити: Java (Android): Як масштабувати графік без бітової карти? Незважаючи на те, що він має недолік, те, що розширений графічний / растровий малюнок буде використовувати більше оперативної пам’яті, а масштабування під час використання ImageView не потребує більше пам’яті. Перевагою може бути менше завантаження процесора.


Хороший! Робить саме те, що було після: фонове зображення, щоб заповнити весь екран, збільшивши пропорційно ширину та обрізаючи нижню частину зображення.
CMash

4

Наведений нижче код робить ідеальне зображення з однаковим розміром перегляду зображень. Отримайте висоту та ширину растрового зображення, а потім обчисліть нову висоту та ширину за допомогою параметрів imageview. Це дає вам потрібне зображення з найкращим співвідношенням сторін.

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>

2
Я розумію, що фон розшириться, щоб заповнити подання, але не зменшиться.
Едвард Фолк

1

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

Це, безумовно, працює зі зменшенням масштабів, я ще не перевіряв масштабування.


0

Котлін:

Якщо вам потрібно було намалювати растрову карту у вікні View, масштабується до 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())
}

-4

перед тим, як використовувати його як фон, вам доведеться попередньо змінити масштабований малюнок


Це справді єдиний варіант? Чи немає типу контейнера для розміщення ImageView, в якому можна вказати розмір відображення?
GrkEngineer

У мене також виникають проблеми із SurfaceView
AZ_

3
Немає нічого подібного до підбору аспектів, заповнення аспектів, вгорі зліва, вгорі тощо, як на iOS? Це смокче ..
Maciej Swic

Як сказав @Aleksej, неправдива відповідь.
учень

-5

Ви можете скористатися одним із наступних:

android: gravity = "fill_horizontal | clip_vertical"

Або

android: gravity = "fill_vertical | clip_horizontal"

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