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));
- Android - Как создать интерактивный список?
- Как узнать использование памяти моего приложения в Android?
- Расширение от двух classов
- Как выполнить веб-запрос в своем streamе?
- Почему double width = 50/110000; выход составляет 0,000000000000000?
- Как разбивать Firestore на Android?
- Использование «this» с именем classа
- Импорт android.support не может быть разрешен
- Как возобновить Android-активность программно из фона
- Android - Сохранить изображение с URL на SD-карту
- свойство системы настройки
- Как округлить время до ближайшего квартального часа в java?
- Как сместить ActionBar вместе с NavigationDrawer
Я сделал это с пользовательским представлением. Установите 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”, и он отлично работал.
Это работает в соответствии с моим требованием