Просмотрщик фото для Андроид масштабировать изображение меньшего размера по ширине с гибкими высота без обрезки и искажений
часто спрашивали, никогда не отвечали (по крайней мере, не воспроизводимым способом).
у меня есть вид изображения с изображением, которое составляет меньше чем смотреть. Я хочу масштабировать изображение до ширины экрана и настроить высоту ImageView, чтобы отразить пропорционально правильную высоту изображения.
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
это приводит к тому, что изображение центрируется в исходном размере (меньше ширины экрана) с полями сбоку. Нет хороший.
поэтому я добавил
android:adjustViewBounds="true"
тот же эффект, ничего хорошего. Я добавил
android:scaleType="centerInside"
тот же эффект, ничего хорошего. Я изменился centerInside
до fitCenter
. Тот же эффект, ничего хорошего. Я изменился centerInside
до centerCrop
.
android:scaleType="centerCrop"
теперь, наконец, изображение scaled по ширине экрана - но обрезанные сверху и снизу! Так что я изменил centerCrop
до fitXY
.
android:scaleType="fitXY"
теперь изображение масштабируется до ширины но не масштабируется по оси y, что приводит к искаженные изображения.
удаление android:adjustViewBounds="true"
не имеет никакого эффекта. Добавление android:layout_gravity
, как было предложено в другом месте, опять не имеет никакого эффекта.
я пробовал другие комбинации -- безрезультатно. Так что, пожалуйста, кто-нибудь знает:
как настроить XML ImageView, чтобы заполнить ширину экрана, масштабировать меньшее изображение, чтобы заполнить весь вид, отображая изображение с его соотношением сторон без искажений или обрезки?
EDIT: я также попытался установить произвольную числовую высоту. Это имеет эффект только с centerCrop
настройка. Это будет искажать изображение по вертикали в соответствии с высотой просмотра.
8 ответов:
Я решил эту проблему, создав java-класс, который вы включаете в свой файл макета:
public class DynamicImageView extends ImageView { public DynamicImageView(final Context context, final AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if (d != null) { // ceil not round - avoid thin vertical gaps along the left/right edges final int width = MeasureSpec.getSize(widthMeasureSpec); final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
теперь вы используете это, добавив свой класс в свой файл макета:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <my.package.name.DynamicImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@drawable/about_image" /> </RelativeLayout>
У меня была такая же проблема, как у тебя. После 30 минут попыток различных вариаций я решил проблему таким образом. Это дает вам гибкий высота и ширина скорректирована до родительской ширины
<ImageView android:id="@+id/imageview_company_logo" android:layout_width="fill_parent" android:layout_height="fill_parent" android:adjustViewBounds="true" android:scaleType="fitCenter" android:src="@drawable/appicon" />
в стандарте XML-макета нет жизнеспособного решения.
единственный надежный способ реагировать на динамический размер изображения-это использовать
LayoutParams
в коде.разочарование.
android:scaleType="fitCenter"
вычислите масштаб, который будет поддерживать исходное соотношение сторон src, но также гарантирует, что src полностью вписывается в dst. По крайней мере, одна ось (X или Y) будет точно соответствовать. Результат центрируется внутри dst.
edit:
проблема здесь в том, что
layout_height="wrap_content"
не "позволяя" изображение, чтобы увеличить. Вам придется установить размер для него, для этого изменитьandroid:layout_height="wrap_content"
до
android:layout_height="100dp" // or whatever size you want it to be
edit2:
работает:
<ImageView android:id="@+id/imageView1" android:layout_width="match_parent" android:layout_height="300dp" android:scaleType="fitCenter" android:src="@drawable/img715945m" />
Это небольшое дополнение к отличному решению Марка Мартинссона.
Если ширина вашего изображения больше его высоты, то решение Марка оставит пространство в верхней и нижней части экрана.
ниже исправляет это, сначала сравнивая ширину и высоту: если ширина изображения >= высота, то он будет масштабировать высоту, чтобы соответствовать высоте экрана, а затем масштабировать ширину, чтобы сохранить соотношение сторон. Аналогично, если высота изображения > ширина, то оно будет масштабироваться ширина соответствует ширине экрана, а затем масштабировать высоту, чтобы сохранить соотношение сторон.
другими словами, он правильно удовлетворяет определению
scaleType="centerCrop"
:http://developer.android.com/reference/android/widget/ImageView.ScaleType.html
масштабировать изображение равномерно (сохранить соотношение сторон изображения), так что оба размера (ширина и высота) изображения равна или больше, чем соответствующий размер вида (минус отступ).
package com.mypackage; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; public class FixedCenterCrop extends ImageView { public FixedCenterCrop(final Context context, final AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if(d != null) { int height = MeasureSpec.getSize(heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); if(width >= height) height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); else width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
Это решение автоматически работает в портретном или ландшафтном режиме. Вы ссылаетесь на него в своем макете так же, как и в решении Марка. Например:
<com.mypackage.FixedCenterCrop android:id="@+id/imgLoginBackground" android:src="@drawable/mybackground" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:scaleType="centerCrop" />
я столкнулся с той же проблемой, но с фиксированной высотой, масштабируемой шириной, сохраняя исходное соотношение сторон изображения. Я решил его с помощью взвешенной линейной компоновки. Вы можете, надеюсь, изменить его для ваших нужд.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/image" android:layout_width="0px" android:layout_height="180dip" android:layout_weight="1.0" android:adjustViewBounds="true" android:scaleType="fitStart" /> </LinearLayout>
еще одно дополнение к решению Марка Матинссона. Я обнаружил, что некоторые из моих фотографий были более масштабных, чем другие. Поэтому я изменил класс так, чтобы изображение масштабировалось с максимальным коэффициентом. Если изображение слишком мало для масштабирования на максимальной ширине без размытия, оно перестает масштабироваться за пределами максимального предела.
public class DynamicImageView extends ImageView { final int MAX_SCALE_FACTOR = 2; public DynamicImageView(final Context context) { super(context); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { final Drawable d = this.getDrawable(); if (d != null) { // ceil not round - avoid thin vertical gaps along the left/right edges int width = View.MeasureSpec.getSize(widthMeasureSpec); if (width > (d.getIntrinsicWidth()*MAX_SCALE_FACTOR)) width = d.getIntrinsicWidth()*MAX_SCALE_FACTOR; final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth()); this.setMeasuredDimension(width, height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
}
Котлин версия Марка Мартинссона ответ:
class DynamicImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val drawable = this.drawable if (drawable != null) { //Ceil not round - avoid thin vertical gaps along the left/right edges val width = View.MeasureSpec.getSize(widthMeasureSpec) val height = Math.ceil((width * drawable.intrinsicHeight.toFloat() / drawable.intrinsicWidth).toDouble()).toInt() this.setMeasuredDimension(width, height) } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec) } }