Масштабировать изображение, чтобы заполнить ширину ImageView и сохранить соотношение сторон

200

У меня есть GridView. Данные GridViewзапроса от сервера.

Вот расположение элементов в GridView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/analysis_micon_bg"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/half_activity_vertical_margin"
    android:paddingLeft="@dimen/half_activity_horizontal_margin"
    android:paddingRight="@dimen/half_activity_horizontal_margin"
    android:paddingTop="@dimen/half_activity_vertical_margin" >

    <ImageView
        android:id="@+id/ranking_prod_pic"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/ranking_rank_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

Я запрашиваю данные с сервера, получаю URL-адрес изображения и загружаю изображение в Bitmap

public static Bitmap loadBitmapFromInputStream(InputStream is) {
    return BitmapFactory.decodeStream(is);
}

public static Bitmap loadBitmapFromHttpUrl(String url) {
    try {
        return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        return null;
    }
}

и есть код getView(int position, View convertView, ViewGroup parent)метода в адаптере

Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);

Размер изображения 210*210. Я запускаю свое приложение на Nexus 4. Изображение заполняет ImageViewширину, но ImageViewвысота не масштабируется. ImageViewне показывает все изображение

Как мне решить эту проблему?

Джо
источник

Ответы:

549

Без использования каких-либо пользовательских классов или библиотек:

<ImageView
    android:id="@id/img"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

scaleType="fitCenter" (по умолчанию, если не указано)

  • сделает его настолько широким, насколько позволяет родитель, и увеличит / уменьшит его, если необходимо, сохраняя соотношение сторон.

scaleType="centerInside"

  • если внутренняя ширина srcменьше родительской ширины
    , центр изображения будет горизонтальным
  • если внутренняя ширина srcбольше родительской ширины,
    то она станет настолько широкой, насколько это позволяет родительский элемент, и уменьшит пропорции.

Это не имеет значения , если вы используете android:srcили ImageView.setImage*методы , и ключ, вероятно adjustViewBounds.

TWiStErRob
источник
5
android: layout_height = "wrap_content" android: AdjustViewBounds = "true" android: scaleType = "fitCenter" делает свое дело
Джеймс Тан
@JamesTan fill_parentдля ширины - это просто хорошая практика, также работает фиксированный размер.
TWiStErRob
@TWiStErRob Я понимаю, что ему нужен Android: AdjustViewBounds = "true" с ним, в противном случае он не подходит соответственно по высоте. и да, ширина, чтобы соответствовать родителю, является полной версией
Джеймс Тан
9
Работает как прелесть в API 19+, но не в API 16 (протестировано). Пользовательский вид Алекса Семенюка работает и на API 16.
Ашкан Сарлак
1
@ F.Mysir whops 😅, да, не имеет значения, какой из них подходит, но для распространения хорошей практики я полностью согласен.
TWiStErRob
42

Мне нравится ответ arnefm, но он допустил небольшую ошибку (см. Комментарии), которую я постараюсь исправить:

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
 * ImageView that keeps aspect ratio when scaled
 */
public class ScaleImageView extends ImageView {

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

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

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

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    try {
      Drawable drawable = getDrawable();
      if (drawable == null) {
        setMeasuredDimension(0, 0);
      } else {
        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
        if (measuredHeight == 0 && measuredWidth == 0) { //Height and width set to wrap_content
          setMeasuredDimension(measuredWidth, measuredHeight);
        } else if (measuredHeight == 0) { //Height set to wrap_content
          int width = measuredWidth;
          int height = width *  drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
          setMeasuredDimension(width, height);
        } else if (measuredWidth == 0){ //Width set to wrap_content
          int height = measuredHeight;
          int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
          setMeasuredDimension(width, height);
        } else { //Width and height are explicitly set (either to match_parent or to exact value)
          setMeasuredDimension(measuredWidth, measuredHeight);
        }
      }
    } catch (Exception e) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  }

}

Таким образом, ваша ImageViewшкала будет правильно масштабирована и не будет иметь проблем с размерами, если (например) поместить внутрьScrollView

Алекс Семенюк
источник
спасибо, я нашел решение, чуть ниже ответа arnefm, первый комментарий, который я добавил. что thereэто ссылка
Джо
36

У меня была похожая проблема однажды. Я решил это, сделав собственный ImageView.

public class CustomImageView extends ImageView

Затем переопределите метод onMeasure для изображения. Я сделал что-то вроде этого, я считаю:

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    try {
        Drawable drawable = getDrawable();

        if (drawable == null) {
            setMeasuredDimension(0, 0);
        } else {
            float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
            float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
            if (imageSideRatio >= viewSideRatio) {
                // Image is wider than the display (ratio)
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = (int)(width / imageSideRatio);
                setMeasuredDimension(width, height);
            } else {
                // Image is taller than the display (ratio)
                int height = MeasureSpec.getSize(heightMeasureSpec);
                int width = (int)(height * imageSideRatio);
                setMeasuredDimension(width, height);
            }
        }
    } catch (Exception e) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

Это растянет изображение до размеров экрана, сохраняя соотношение сторон.

arnefm
источник
1
смотрите там . первый ответ
Джо
Ну, этот ответ не точный. Рассмотрим ситуацию, когда вы установили android:layout_height="wrap_content". В этом случае MeasureSpec.getSize(heightMeasureSpec)будет 0и ваш viewSideRatioрезультат Infinity(не удивительно, что Java float допускает такие значения). В этом случае и ваш, heightи widthбудет 0.
Алекс Семенюк
1
Чтобы заставить заполнение работать, мне нужно было поменять (imageSideRatio> = viewSideRatio) на (imageSideRatio <viewSideRatio) .. в противном случае хороший ответ
Марек Халмо
23

Использование android:scaleType="centerCrop".

Марк Буйкема
источник
Не совсем то, о чем спрашивал вопрос, но именно то, что мне было нужно!
nickjm
Можете ли вы разработать @Jameson? Вы имеете в виду версии Android?
Мик
Если я использую centerCrop, изображение немного обрезается сверху и снизу.
Sreekanth Karumanaghat,
9

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

package com.example;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class ScaledImageView extends ImageView {
    public ScaledImageView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        final Drawable d = getDrawable();

        if (d != null) {
            int width;
            int height;
            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
                height = MeasureSpec.getSize(heightMeasureSpec);
                width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
            } else {
                width = MeasureSpec.getSize(widthMeasureSpec);
                height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            }
            setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

И затем, чтобы не RelativeLayoutигнорировать измеренный размер, я сделал это:

    <FrameLayout
        android:id="@+id/image_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/something">

        <com.example.ScaledImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="150dp"/>
    </FrameLayout>
Дейв Коул
источник
5

Используйте эти свойства в ImageView, чтобы сохранить соотношение сторон:

android:adjustViewBounds="true"
android:scaleType="fitXY"
Атиф Махмуд
источник
9
fitXY НЕ сохраняет соотношение сторон. Это растягивает изображение, чтобы точно соответствовать ширине и высоте макета.
Заброшенная корзина
5

Это не будет применимо, если вы установите изображение в качестве фона в ImageView, необходимо установить на src (android: src).

Спасибо.

Гуна Сехар
источник
5

Чтобы создать изображение, ширина которого равна ширине экрана, а высота пропорционально установлена ​​в соответствии с соотношением сторон, выполните следующие действия.

Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {

                        // creating the image that maintain aspect ratio with width of image is set to screenwidth.
                        int width = imageView.getMeasuredWidth();
                        int diw = resource.getWidth();
                        if (diw > 0) {
                            int height = 0;
                            height = width * resource.getHeight() / diw;
                            resource = Bitmap.createScaledBitmap(resource, width, height, false);
                        }
                                              imageView.setImageBitmap(resource);
                    }
                });

Надеюсь это поможет.

Фатима км
источник
это лучший ответ, который я нашел, я искал 2 дня, спасибо Фатима
Картик Раманатан
4

Попробуйте это: это решило проблему для меня

   android:adjustViewBounds="true"
    android:scaleType="fitXY"
Инженерный разум
источник
4

Йо не нужен код Java. Вам просто нужно:

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:adjustViewBounds="true"
    android:scaleType="centerCrop" />

Ключ в родительском совпадении для ширины и высоты

Рому Диззи
источник
centerCrop фактически обрезает некоторую часть изображения.
Sreekanth Karumanaghat,
2

попробуйте эту простую строку ... добавьте эту строку в свой XML-код в теге представления изображений без добавления каких-либо зависимостей android: scaleType = "fitXY"

ziddi609
источник
fitXY не поддерживает соотношение сторон, поэтому изображение, скорее всего, будет растянуто
ProjectDelta
2

использовать android: ScaleType = "fitXY" im ImageView xml

Навид Наим
источник
fitXY не поддерживает соотношение сторон, поэтому изображение, скорее всего, будет растянуто
ProjectDelta
1

Вы можете попытаться сделать то, что делаете, вручную загрузив изображения, но я очень и очень рекомендую взглянуть на Universal Image Loader .

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

Пример кода:

//ImageLoader config
DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();

    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
            defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();

    if (ImageLoader.getInstance().isInited()) {
        ImageLoader.getInstance().destroy();
    }
    ImageLoader.getInstance().init(config);

    imageLoadingListener = new ImageLoadingListener() {
        @Override
        public void onLoadingStarted(String s, View view) {

        }

        @Override
        public void onLoadingFailed(String s, View view, FailReason failReason) {
            ImageView imageView = (ImageView) view;
            imageView.setImageResource(R.drawable.android);
            Log.i("Failed to Load " + s, failReason.toString());
        }

        @Override
        public void onLoadingComplete(String s, View view, Bitmap bitmap) {

        }

        @Override
        public void onLoadingCancelled(String s, View view) {

        }
    };

//Imageloader usage
ImageView imageView = new ImageView(getApplicationContext());
    if (orientation == 1) {
        imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
    } else {
        imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
    }
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);

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

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

rcbevans
источник
0

Просто используйте UniversalImageLoader и установите

DisplayImageOptions.Builder()
    .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
    .build();

и нет настроек масштаба в ImageView

Артем
источник
0

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

Вот мой xml

<ImageView
  android:id="@+id/imageViewer"
  android:layout_width="match_parent"
  android:layout_height="match_parent"//dp is not automaticly updated, when loading from a other source
  android:scaleType="fitCenter"
  tools:srcCompat="@drawable/a8" />

Я использую Kotlin и загружаю Drawable из файла активов, вот как я рассчитываю это

val d = Drawable.createFromStream(assets.open("imageData/${imageName}.png"), null)
bitHeight = d.minimumHeight//get the image height
imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
imageViewer.setImageDrawable(d)//set the image from the drawable
imageViewer.requestLayout()//here I apply it to the layout
DarkPh03n1X
источник
-1

Используйте Пикассо, который прост в использовании ..

В вашем адаптере ..

@Override
public void getView(int position, View convertView, ViewGroup parent) {

 ImageView view = (ImageView) convertView.findViewById(R.id.ranking_prod_pic);

 Picasso.with(context).load(url).into(view); //url is image url

 //you can resize image if you want

 /* Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(view) */

}

http://square.github.io/picasso/ введите описание изображения здесь

chiragkyada
источник