Android: Как растянуть изображение до ширины экрана при сохранении пропорций?

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

ImageView mainImageView = new ImageView(context);
    mainImageView.setImageBitmap(mainImage); //downloaded from server
    mainImageView.setScaleType(ScaleType.FIT_XY);
    //mainImageView.setAdjustViewBounds(true); 
    //with this line enabled, just scales image down
    addView(mainImageView,new LinearLayout.LayoutParams( 
            LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
4b9b3361

Я сделал это с пользовательским представлением. Установите layout_width = "fill_parent" и layout_height = "wrap_content", и укажите его на подходящий drawable:

public class Banner extends View {

  private final Drawable logo;

  public Banner(Context context) {
    super(context);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs) {
    super(context, attrs);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  @Override protected void onMeasure(int widthMeasureSpec,
      int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = width * logo.getIntrinsicHeight() / logo.getIntrinsicWidth();
    setMeasuredDimension(width, height);
  }
}
131
ответ дан 08 июня '10 в 16:38
источник

В конце я сгенерировал размеры вручную, что отлично работает:

DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = width * mainImage.getHeight() / mainImage.getWidth(); //mainImage is the Bitmap I'm drawing
addView(mainImageView,new LinearLayout.LayoutParams( 
        width, height));
24
ответ дан 08 июня '10 в 20:28
источник

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

        // Get the max possible width given our constraints
        widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);

        // Get the max possible height given our constraints
        heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);

Где h и w - размеры изображения, а p* - отступы.

И затем:

private int resolveAdjustedSize(int desiredSize, int maxSize,
                               int measureSpec) {
    ...
    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            /* Parent says we can be as big as we want. Just don't be larger
               than max size imposed on ourselves.
            */
            result = Math.min(desiredSize, maxSize);

Итак, если у вас есть layout_height="wrap_content", он установит widthSize = w + pleft + pright, или, другими словами, максимальная ширина будет равна ширине изображения.

Это означает, что если вы не установите точный размер, изображения НИКОГДА не увеличиваются. Я считаю, что это ошибка, но удача в том, чтобы заставить Google обратить внимание или исправить ее. Редактирование: употребляя мои собственные слова, я отправил отчет об ошибках, и они говорят, что он исправлен в будущем выпуске!

Другое решение

Вот еще одно подклассовое обходное решение, но вы должны (теоретически, я его совсем не тестировал!) могли бы использовать его в любом месте ImageView. Чтобы использовать его, установите layout_width="match_parent" и layout_height="wrap_content". Это довольно много общего, чем принятое решение. Например. вы можете делать fit-to-height, а также fit-to-width.

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

// This works around the issue described here: http://stackoverflow.com/a/12675430/265521
public class StretchyImageView extends ImageView
{

    public StretchyImageView(Context context)
    {
        super(context);
    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Call super() so that resolveUri() is called.
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // If there no drawable we can just use the result from super.
        if (getDrawable() == null)
            return;

        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        int w = getDrawable().getIntrinsicWidth();
        int h = getDrawable().getIntrinsicHeight();
        if (w <= 0)
            w = 1;
        if (h <= 0)
            h = 1;

        // Desired aspect ratio of the view contents (not including padding)
        float desiredAspect = (float) w / (float) h;

        // We are allowed to change the view width
        boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;

        // We are allowed to change the view height
        boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

        int pleft = getPaddingLeft();
        int pright = getPaddingRight();
        int ptop = getPaddingTop();
        int pbottom = getPaddingBottom();

        // Get the sizes that ImageView decided on.
        int widthSize = getMeasuredWidth();
        int heightSize = getMeasuredHeight();

        if (resizeWidth && !resizeHeight)
        {
            // Resize the width to the height, maintaining aspect ratio.
            int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
            setMeasuredDimension(newWidth, heightSize);
        }
        else if (resizeHeight && !resizeWidth)
        {
            int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;
            setMeasuredDimension(widthSize, newHeight);
        }
    }
}
20
ответ дан 01 окт. '12 в 17:38
источник

Настройка adjustViewBounds на true и использование группы представления LinearLayout работала очень хорошо для меня. Нет необходимости подкласса или запрашивать показатели устройства:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);
17
ответ дан 16 апр. '11 в 2:49
источник

Я боролся с этой проблемой в той или иной форме для AGES, спасибо, спасибо, СПАСИБО....:)

Я просто хотел указать, что вы можете получить обобщаемое решение из того, что сделал Боб Ли, просто расширив View и переопределив onMeasure. Таким образом, вы можете использовать это с любым возможным вам, и он не сломается, если нет изображения:

    public class CardImageView extends View {
        public CardImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

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

        public CardImageView(Context context) {
            super(context);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Drawable bg = getBackground();
            if (bg != null) {
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * bg.getIntrinsicHeight() / bg.getIntrinsicWidth();
                setMeasuredDimension(width,height);
            }
            else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
13
ответ дан 07 апр. '12 в 13:10
источник

В некоторых случаях эта волшебная формула прекрасно решает проблему.

Для тех, кто борется с этим приходом с другой платформы, размер и форма "в соответствии с прекрасно обрабатываются на Android, но его трудно найти.

Вам обычно нужна эта комбинация:

  • width match parent,
  • высота обертывания,
  • adjustViewBounds включен (sic)
  • шкала fitCenter
  • cropToPadding OFF (sic)

Тогда это автоматическое и удивительное.

Если вы iOS-разработчик, совершенно удивительно, как просто вы можете делать "полностью динамические высоты ячеек" в представлении таблицы.. err, я имею в виду ListView. Наслаждайтесь.

<com.parse.ParseImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/post_image"
    android:src="@drawable/icon_192"
    android:layout_margin="0dp"
    android:cropToPadding="false"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
    android:background="#eff2eb"/>

enter image description here

10
ответ дан 21 мая '14 в 10:43
источник

Каждый делает это программируемо, поэтому я думал, что этот ответ будет идеально вписываться здесь. Этот код работал для моего в xml. Я еще не думал о соотношении, но все же хотел бы разместить этот ответ, если он кому-то поможет.

android:adjustViewBounds="true"

Приветствия..

5
ответ дан 20 сент. '15 в 20:20
источник

Я сделал это с этими значениями в LinearLayout:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
5
ответ дан 09 нояб. '11 в 23:25
источник

Мне удалось достичь этого, используя только этот код XML. Возможно, что eclipse не отображает высоту, чтобы показать, что она расширяется, чтобы соответствовать; однако, когда вы на самом деле запускаете это на устройстве, он правильно отображает и дает желаемый результат. (ну, по крайней мере, для меня)

<FrameLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content">

     <ImageView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:adjustViewBounds="true"
          android:scaleType="centerCrop"
          android:src="@drawable/whatever" />
</FrameLayout>
5
ответ дан 09 апр. '14 в 23:23
источник

Очень простое решение - просто использовать функции, предоставляемые RelativeLayout.

Вот xml, который позволяет со стандартным Android Views:

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

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <LinearLayout
            android:id="@+id/button_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_alignParentBottom="true"
            >
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <ImageView 
            android:src="@drawable/cat"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:layout_above="@id/button_container"/>
    </RelativeLayout>
</ScrollView>

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

3
ответ дан 30 сент. '13 в 10:17
источник

Простое задание adjustViewBounds="true" и scaleType="fitCenter" в файле XML для ImageView!

<ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/image"

        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        />

Примечание: layout_width установлен на match_parent

2
ответ дан 26 февр. '17 в 12:35
источник

Вы можете использовать мой StretchableImageView, сохраняя соотношение сторон (по ширине или по высоте) в зависимости от ширины и высоты рисования:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

public class StretchableImageView extends ImageView{

    public StretchableImageView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if(getDrawable()!=null){
            if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * getDrawable().getIntrinsicHeight()
                            / getDrawable().getIntrinsicWidth();
                setMeasuredDimension(width, height);
            }else{
                int height = MeasureSpec.getSize(heightMeasureSpec);
                int width = height * getDrawable().getIntrinsicWidth()
                            / getDrawable().getIntrinsicHeight();
                setMeasuredDimension(width, height);
            }
        }
    }
}
1
ответ дан 11 нояб. '15 в 18:08
источник

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

1
ответ дан 15 февр. '11 в 1:32
источник

Посмотрите, что есть намного более легкое решение вашей проблемы:

ImageView imageView;

protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_layout);
    imageView =(ImageView)findViewById(R.id.your_imageView);
    Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image);
    Point screenSize = new Point();
    getWindowManager().getDefaultDisplay().getSize(screenSize);
    Bitmap temp = Bitmap.createBitmap(screenSize.x, screenSize.x, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(temp);
    canvas.drawBitmap(imageBitmap,null, new Rect(0,0,screenSize.x,screenSize.x), null);
    imageView.setImageBitmap(temp);
}
1
ответ дан 09 июня '15 в 23:46
источник

Вы устанавливаете ScaleType в ScaleType.FIT_XY. Согласно javadocs, это растянет изображение, чтобы оно соответствовало всей области, при необходимости изменив соотношение сторон. Это объясняет поведение, которое вы видите.

Чтобы получить нужное поведение... FIT_CENTER, FIT_START или FIT_END близки, но если изображение уже, чем оно высокое, оно не начнет заполнять ширину. Вы можете посмотреть, как это реализовано, и вы, вероятно, должны быть в состоянии выяснить, как настроить его для своей цели.

1
ответ дан 07 июня '10 в 20:48
источник

Для меня андроид: scaleType = "centerCrop" не разрешил мою проблему. Фактически это расширило изображение. Поэтому я пробовал с android: scaleType = "fitXY", и он отлично работал.

0
ответ дан 13 марта '14 в 23:05
источник

Это работает отлично в соответствии с моим требованием

<ImageView android:id="@+id/imgIssue" android:layout_width="fill_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitXY"/>

0
ответ дан 24 авг. '15 в 9:00
источник