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

Я сделал это с пользовательским представлением. Установите 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); } } 

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

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

Я просто прочитал исходный код для ImageView и это практически невозможно без использования подclassовых решений в этом streamе. В 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 обратить внимание или исправить ее. Редактирование: Я пишу свои собственные слова, я отправил отчет об ошибке, и они говорят, что он был исправлен в будущем выпуске!

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

Вот еще одно подclassовое обходное решение, но вы должны (теоретически, я его действительно не тестировал!) Могли бы использовать его везде, где вы 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); } } } 

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

 //NOTE: "this" is a subclass of LinearLayout ImageView splashImageView = new ImageView(context); splashImageView.setImageResource(R.drawable.splash); splashImageView.setAdjustViewBounds(true); addView(splashImageView); 

Я боролся с этой проблемой в той или иной форме для 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); } } } 

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

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

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

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

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

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

  

введите описание изображения здесь

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

 Scale type: fitStart Layout gravity: fill_horizontal Layout height: wrap_content Layout weight: 1 Layout width: fill_parent 

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

    

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

 android:adjustViewBounds="true" 

Ура ..

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

Вот xml, который позволяет с помощью стандартных представлений Android:

            

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

Его простой вопрос: adjustViewBounds="true" и scaleType="fitCenter" в XML-файле для ImageView!

  

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

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

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

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

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

 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); } Размер 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); } 

Вы можете использовать мой 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); } } } } 

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

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

  • Как завершить сканирование при завершении ввода?
  • Как мне отображать закругленные углы в Android?
  • Java SE 6 против JRE 1.6 против JDK 1.6 - Что это значит?
  • Динамический список в приложении Android
  • Как Facebook добавляет номера значков на значок приложения в Android?
  • Передача функции в качестве параметра в java
  • Повторное исключение: почему метод компилируется без предложения бросков?
  • Как анализировать дату с GMT TimeZone на IST TimeZone и наоборот в android
  • Является ли законным вызывать метод запуска дважды в одной теме?
  • Как изменить JDK по умолчанию для IntelliJ IDEA?
  • Незарегистрированное исключение java.sql.SQLException; должны быть пойманы или объявлены брошенными?
  • Давайте будем гением компьютера.