Android: как растянуть изображение до ширины экрана при сохранении соотношения сторон? - PullRequest
118 голосов
/ 07 июня 2010

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

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));

Ответы [ 17 ]

132 голосов
/ 08 июня 2010

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

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);
  }
}
24 голосов
/ 08 июня 2010

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

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));
21 голосов
/ 01 октября 2012

Я только что прочитал исходный код для 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". Это гораздо более общее, чем принятое решение. Например. Вы можете сделать подбор по высоте, а также по ширине.

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's 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's contents (not including padding)
        float desiredAspect = (float) w / (float) h;

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

        // We are allowed to change the view's 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);
        }
    }
}
17 голосов
/ 16 апреля 2011

Установка 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);
13 голосов
/ 07 апреля 2012

Я боролся с этой проблемой в той или иной форме за 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);
            }
        }
    }
10 голосов
/ 21 мая 2014

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

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

Обычно вы хотите эту комбинацию:

  • ширина совпадения с родителем,
  • высота обтекания,
  • AdjustViewBounds включен (SIC)
  • масштаб fitCenter
  • cropToPadding OFF (sic)

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

Если вы разработчик для iOS, просто поразительно, как просто вы можете делать "абсолютно динамические высоты ячеек" в табличном представлении ... я имею в виду 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

5 голосов
/ 10 ноября 2011

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

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
5 голосов
/ 20 сентября 2015

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

android:adjustViewBounds="true"

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

5 голосов
/ 10 апреля 2014

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

<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>
3 голосов
/ 30 сентября 2013

Очень простое решение - просто использовать функции, предоставляемые 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 на весь экран, но он должен быть выше других макетов. Таким образом, вы достигнете всего, что вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...