Масштабирование изображения с сохранением соотношения сторон в фоновом режиме - PullRequest
58 голосов
/ 27 марта 2012

Как сделать фоновое изображение подходящим для вида, но сохранить его соотношение сторон при использовании <bitmap /> в качестве фона для рисования XML? Ни одно из значений <bitmap> android:gravity не дает желаемого эффекта.

Ответы [ 9 ]

57 голосов
/ 27 марта 2012

Невозможно добиться манипулирования фоновым атрибутом только внутри xml-файлов.Есть два варианта:

  1. Вы обрезаете / масштабируете растровое изображение программно с помощью Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) и устанавливаете его как фон для View.

  2. Вы используете ImageView вместо фона, помещая его в качестве первого элемента макета и определяя для него атрибут android:scaleType:

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
    
            android:src="@drawable/backgrnd"
            android:scaleType="centerCrop" />
    
        ...
    
        rest layout components here
    
        ...
    
    </RelativeLayout>
    
34 голосов
/ 21 февраля 2014

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

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.

Надеюсь, эта информация поможет вам достичь того, чего вы хотите.

3 голосов
/ 23 июня 2017

Я хотел сделать что-то похожее в моем обычном классе Drawable. Вот важные части:

public class CustomBackgroundDrawable extends Drawable
{
    private Rect mTempRect = new Rect();
    private Paint mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    ...

public void draw(@NonNull Canvas canvas)
{
    Rect bounds = getBounds();
    if (mBitmap != null ) {
        if (mScaleType == ScaleType.SCALE_FILL) {
            //bitmap scales to fill the whole bounds area (bitmap can be cropped)
            if (bounds.height() > 0 && bounds.height() > 0) {
                float scale = Math.min(mBitmap.getWidth()/(float)bounds.width(), mBitmap.getHeight()/(float)bounds.height());

                float bitmapVisibleWidth = scale * bounds.width();
                float bitmapVisibleHeight = scale * bounds.height();

                mTempRect.set((int)(mBitmap.getWidth()-bitmapVisibleWidth)/2, 0, (int)(bitmapVisibleWidth+mBitmap.getWidth())/2, (int)bitmapVisibleHeight);
                canvas.drawBitmap(mBitmap, mTempRect, bounds, mBitmapPaint);
            }
        } else if (mScaleType == ScaleType.SCALE_FIT) {
            //bitmap scales to fit in bounds area
            if (bounds.height() > 0 && bounds.height() > 0) {
                float scale = Math.min((float)bounds.width()/mBitmap.getWidth(), (float)bounds.height()/mBitmap.getHeight());

                float bitmapScaledWidth = scale * mBitmap.getWidth();
                float bitmapScaledHeight = scale * mBitmap.getHeight();
                int centerPadding = (int)(bounds.width()-bitmapScaledWidth)/2;
                mTempRect.set(bounds.left + centerPadding, bounds.top, bounds.right - centerPadding, bounds.top+(int)bitmapScaledHeight);
                canvas.drawBitmap(mBitmap, null, mTempRect, mBitmapPaint);
            }
        }
    }
}

При таком подходе вы можете гибко применять любую логику масштабирования, которая вам нужна

3 голосов
/ 13 сентября 2012

Другой подход заключается в создании патча 9 изображений вашего обычного изображения и масштабировании его так, как вы хотите.

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

Надеюсь, вы поняли идею.

2 голосов
/ 13 января 2017

Попробуйте использовать InsetDrawable (хорошо для меня).

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

InsetDrawable insetDrawable = new InsetDrawable(drawable, insetLeft, insetTop, insetRight, insetBottom);

Специально используется для настройки фона, размер которого меньше или меньше, чем у вида.

Смотрите здесь: https://developer.android.com/reference/android/graphics/drawable/InsetDrawable.html

2 голосов
/ 12 августа 2013

Использование метода, описанного a.ch, отлично работает для меня, за исключением того, что я использовал этот тип шкалы, который работал намного лучше для того, что мне нужно:

android:scaleType="centerCrop"

Вот полный список доступных типов шкал: http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

1 голос
/ 05 мая 2017

Если ваше растровое изображение шире, чем оно высокое, используйте android:gravity="fill_vertical".В противном случае используйте android:gravity="fill_horizontal".Это имеет эффект, аналогичный использованию android:scaleType="centerCrop" на ImageView.

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="fill_vertical"
    android:src="@drawable/image" />

. Если вы поддерживаете несколько ориентаций, вы можете создать один XML-файл растрового изображения в папке drawable-port, а другой - в drawable-land папка.

1 голос
/ 07 февраля 2017

Старый вопрос, но ни один из других ответов не работал для меня.Этот код XML сделал однако:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:scaleType="centerCrop"
        android:src="@drawable/background_image"/> 

</RelativeLayout>
0 голосов
/ 30 мая 2018

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

Здесь я установил такие же ширина и высота для квадратных изображений [или вы можете wrap_content на обоих].

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentTop="true"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        android:src="@drawable/background_image"/> 

</RelativeLayout>

adjust view bounds и scale type center fit добиваются цели.

...