Skala Gambar untuk mengisi lebar ImageView dan menjaga rasio aspek

198

Saya punya GridView. Data GridViewpermintaan dari server.

Berikut adalah tata letak item di 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>

Saya meminta data dari server, mendapatkan url gambar dan memuat gambar ke 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;
    }
}

dan ada kode getView(int position, View convertView, ViewGroup parent)metode dalam adaptor

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

Ukuran gambar adalah 210*210. Saya menjalankan aplikasi saya pada Nexus 4. Gambar memang mengisi ImageViewlebar, tetapi ImageViewtingginya tidak skala. ImageViewtidak menampilkan seluruh gambar.

Bagaimana saya mengatasi masalah ini?

Joe
sumber

Jawaban:

544

Tanpa menggunakan kelas atau pustaka khusus:

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

scaleType="fitCenter" (default saat dihilangkan)

  • akan membuatnya selebar yang dimungkinkan oleh orang tua dan naik / turun skala sesuai kebutuhan menjaga aspek rasio.

scaleType="centerInside"

  • jika lebar intrinsik srclebih kecil dari lebar induk
    akan memusatkan gambar secara horizontal
  • jika lebar intrinsik srclebih besar dari lebar induk
    akan membuatnya selebar yang diizinkan induk dan rasio aspek penjagaan skala bawah.

Tidak masalah jika Anda menggunakan android:srcatau ImageView.setImage*metode dan kuncinya mungkin adalah adjustViewBounds.

TWiStErRob
sumber
5
android: layout_height = "wrap_content" android: adjustViewBounds = "true" android: scaleType = "fitCenter" melakukan trik
James Tan
@JamesTan fill_parentuntuk lebar hanyalah praktik yang baik, ukuran tetap juga berfungsi.
TWiStErRob
@TWiStErRob saya sadari itu membutuhkan android: adjustViewBounds = "true" dengannya, jika tidak maka tidak cocok untuk ketinggian. dan ya, lebar agar pas dengan orang tua adalah versi lengkap
James Tan
9
Bekerja seperti pesona pada API 19+, tetapi tidak pada API 16 (diuji). Tampilan kustom Alex Semeniuk juga berfungsi di API 16.
Ashkan Sarlak
1
@ F.Mysir memukuli 😅, ya, tidak masalah yang mana untuk masalah ini, tetapi untuk menyebarkan praktik yang baik, saya sepenuhnya setuju.
TWiStErRob
42

Saya suka jawaban arnefm tetapi dia membuat kesalahan kecil (lihat komentar) yang akan saya coba perbaiki:

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

}

Dengan demikian Anda ImageViewakan diskalakan dengan benar dan tidak akan memiliki masalah dimensi jika (misalnya) dimasukkan ke dalamScrollView

Alex Semeniuk
sumber
thx, saya telah menemukan solusinya, tepat di bawah jawaban arnefm, komentar pertama yang saya tambahkan. yang theremerupakan link
Joe
36

Saya punya masalah yang sama sekali. Saya menyelesaikannya dengan membuat ImageView kustom.

public class CustomImageView extends ImageView

Kemudian timpa metode onMeasure dari tampilan gambar. Saya melakukan sesuatu seperti ini, saya percaya:

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

Ini akan meregangkan gambar agar sesuai dengan layar sambil mempertahankan rasio aspek.

arnefm
sumber
1
lihat disana . jawaban pertama
Joe
Baiklah, jawaban ini tidak akurat. Pertimbangkan situasi di mana Anda menetapkan android:layout_height="wrap_content". Dalam hal ini MeasureSpec.getSize(heightMeasureSpec)akan 0dan Anda viewSideRatioakan menghasilkan Infinity(tidak perlu bertanya-tanya, Java float memungkinkan nilai-nilai tersebut.) Dalam hal ini Anda heightdan widthakan 0.
Alex Semeniuk
1
Bagi saya untuk membuat isian berfungsi saya perlu menukar (imageSideRatio> = viewSideRatio) ke (imageSideRatio <viewSideRatio) .. jika tidak, jawaban yang bagus
Marek Halmo
23

Gunakan android:scaleType="centerCrop".

Mark Buikema
sumber
Tidak persis apa yang ditanyakan, tetapi apa yang saya butuhkan!
nickjm
Bisakah Anda menguraikan @Jameson? Apakah maksud Anda versi Android?
Mick
Jika saya menggunakan centerCrop, gambar akan terpotong sedikit di bagian atas dan bawah.
Sreekanth Karumanaghat
9

Saya melakukan sesuatu yang mirip dengan di atas dan kemudian membenturkan kepala ke dinding selama beberapa jam karena tidak berfungsi di dalam a RelativeLayout. Saya berakhir dengan kode berikut:

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

Dan untuk mencegah RelativeLayoutdari mengabaikan dimensi yang diukur saya melakukan ini:

    <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>
Dave Cole
sumber
5

Gunakan properti ini di ImageView untuk menjaga rasio aspek:

android:adjustViewBounds="true"
android:scaleType="fitXY"
Atif Mahmood
sumber
9
fitXY TIDAK menjaga rasio aspek. Itu membentang gambar agar sesuai dengan lebar dan tinggi tata letak persis.
Abandoned Cart
5

Ini tidak akan berlaku jika Anda mengatur gambar sebagai latar belakang di ImageView, perlu diatur di src (android: src).

Terima kasih.

Guna Sekhar
sumber
5

Untuk membuat gambar dengan lebar sama dengan lebar layar, dan tinggi diatur secara proporsional sesuai dengan aspek rasio, lakukan hal berikut.

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

Semoga ini membantu.

Fathima km
sumber
ini adalah jawaban terbaik yang saya temukan, saya mencari 2 hari, terima kasih fathima
Karthick Ramanathan
4

Coba ini: itu memecahkan masalah bagi saya

   android:adjustViewBounds="true"
    android:scaleType="fitXY"
Pikiran Rekayasa
sumber
4

Anda tidak memerlukan kode java. Anda hanya perlu:

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

Kuncinya ada di induk pertandingan untuk lebar dan tinggi

Romu Dizzy
sumber
centerCrop sebenarnya memotong beberapa bagian dari gambar.
Sreekanth Karumanaghat
2

coba dengan baris sederhana ini ... tambahkan baris ini dalam kode xml Anda di tag tampilan gambar tanpa menambahkan ketergantungan Android: scaleType = "fitXY"

ziddi609
sumber
fitXY tidak mempertahankan aspek rasio sehingga gambar kemungkinan akan diregangkan
ProjectDelta
2

gunakan android: ScaleType = "fitXY" di ImageView xml

Naveed Naeem
sumber
fitXY tidak mempertahankan aspek rasio sehingga gambar kemungkinan akan diregangkan
ProjectDelta
1

Anda dapat mencoba melakukan apa yang Anda lakukan dengan memuat gambar secara manual, tetapi saya akan sangat menyarankan melihat Universal Image Loader .

Saya baru-baru ini mengintegrasikannya ke dalam proyek saya dan saya harus mengatakan itu fantastis. Apakah semua mengkhawatirkan tentang membuat hal-hal asinkron, mengubah ukuran, caching gambar untuk Anda. Sangat mudah untuk mengintegrasikan dan mengatur. Dalam 5 menit Anda mungkin bisa melakukan apa yang Anda inginkan.

Kode contoh:

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

Ini dapat dengan malas memuat gambar, menyesuaikannya dengan ukuran imageView yang memperlihatkan gambar placeholder saat memuat, dan menampilkan ikon default jika memuat gagal dan melakukan cache sumber daya.

- Saya juga harus menambahkan bahwa konfigurasi saat ini menjaga rasio aspek gambar, karenanya berlaku untuk pertanyaan awal Anda

rcbevans
sumber
0

Cukup gunakan UniversalImageLoader dan atur

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

dan tidak ada pengaturan skala pada ImageView

Artem
sumber
0

Saya punya masalah serupa, saya menemukan alasan untuk ini adalah karena Anda perlu menghitung dp. Android studio menghitung ImageViewkapan Anda memuatnya dari drawable, tetapi ketika Anda menggunakan metode lain, seperti memuat dari bitmap tersebut dptidak secara otomatis menyumbang,

Inilah xml saya

<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" />

Saya menggunakan Kotlin, dan memuat yang dapat ditarik dari file aset, inilah cara saya menghitung ini

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
sumber
-1

Gunakan picasso yang mudah digunakan ..

Di Adaptor Anda ..

@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/ masukkan deskripsi gambar di sini

chiragkyada
sumber