Android: Bagaimana cara meregangkan gambar ke lebar layar sambil mempertahankan rasio aspek?

118

Saya ingin mengunduh gambar (dengan ukuran tidak diketahui, tetapi selalu berbentuk persegi) dan menampilkannya sehingga memenuhi layar secara horizontal, dan membentang secara vertikal untuk mempertahankan rasio aspek gambar, pada ukuran layar apa pun. Ini kode saya (tidak berfungsi). Ini meregangkan gambar secara horizontal, tetapi tidak secara vertikal, sehingga terjepit ...

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));
fredley
sumber
Apakah semua perangkat Android memiliki rasio lebar / tinggi yang sama persis? Jika tidak, sangat tidak mungkin untuk menskalakan gambar agar sesuai dengan lebar / tinggi keseluruhan sambil mempertahankan rasio aslinya ...
NoozNooz42
4
Tidak, saya tidak ingin gambar memenuhi layar, hanya untuk menyesuaikan dengan lebar layar, saya tidak peduli berapa banyak layar yang diambil gambar secara vertikal, selama gambar tersebut dalam proporsi yang benar.
fredley
1
Pertanyaan serupa, jawaban bagus: stackoverflow.com/questions/4677269/…
Pēteris Caune
1
apakah 'adjustViewBounds' tidak berfungsi?
Darpan

Jawaban:

132

Saya menyelesaikan ini dengan tampilan kustom. Setel layout_width = "fill_parent" dan layout_height = "wrap_content", dan arahkan ke drawable yang sesuai:

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);
  }
}
Bob Lee
sumber
12
Terima kasih!! Ini harus menjadi jawaban yang benar! :) Saya menyesuaikan ini untuk sedikit lebih fleksibel dalam posting ini: stackoverflow.com/questions/4677269/… Di sana saya menggunakan ImageView sehingga seseorang dapat mengatur drawable dalam XML atau menggunakannya seperti ImageView biasa dalam kode untuk mengatur gambar. Bekerja dengan baik!
Patrick Boos
1
Sangat jenius! itu bekerja seperti seorang juara! Terima kasih itu menyelesaikan masalah saya dengan spanduk gambar di bagian atas, tidak tepat di semua ukuran layar! Terima kasih banyak!
JPM
1
Hati-hati dengan logo menjadi null (jika Anda mengunduh konten dari Internet). Jika tidak, ini berfungsi dengan baik, ikuti posting Patrick untuk potongan XML.
Artem Russakovskii
44
Saya tidak percaya betapa sulitnya melakukan hal-hal di Android yang sangat mudah di iOS dan Windows Phone.
mxcl
1
Saya telah membuat beberapa perubahan, tetapi alih-alih mempostingnya di tempat lain, apakah mungkin mengubah jawaban ini menjadi wiki komunitas? Perubahan saya adalah semua hal yang menangani kasus khusus yang disebutkan di sini, serta kemampuan untuk memilih sisi mana yang akan diubah ukurannya melalui definisi tata letak.
Steve Pomeroy
24

Pada akhirnya, saya membuat dimensi secara manual, yang berfungsi dengan baik:

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));
fredley
sumber
Saya telah mencari solusi semacam ini untuk memperbaiki masalah saya. Dengan bantuan perhitungan ini, saya berhasil memuat gambar tanpa mengurangi rasio aspek.
Steve
21

Saya baru saja membaca kode sumbernya ImageViewdan pada dasarnya tidak mungkin tanpa menggunakan solusi subclassing di utas ini. Di ImageView.onMeasurekita sampai ke baris ini:

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

Dimana hdan wapakah dimensi gambar, dan p*merupakan paddingnya.

Lalu:

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

Jadi jika sudah layout_height="wrap_content"akan di atur widthSize = w + pleft + pright, atau dengan kata lain lebar maksimum sama dengan lebar gambar.

Artinya, kecuali Anda menyetel ukuran yang tepat, gambar TIDAK PERNAH diperbesar . Saya menganggap ini sebagai bug, tapi semoga berhasil membuat Google memperhatikan atau memperbaikinya. Edit: Makan kata-kata saya sendiri, saya serahkan laporan bug dan mereka mengatakan itu telah diperbaiki di rilis mendatang!

Solusi lain

Berikut adalah solusi subclass lain, tetapi Anda harus (secara teori, saya belum benar-benar mengujinya!) Dapat menggunakannya di mana pun Anda ImageView. Untuk menggunakannya, set layout_width="match_parent", dan layout_height="wrap_content". Ini jauh lebih umum daripada solusi yang diterima juga. Misalnya Anda bisa melakukan fit-to-height serta fit-to-width.

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);
        }
    }
}
Timmmm
sumber
Saya mengirimkan laporan bug . Perhatikan, karena diabaikan!
Timmmm
3
Rupanya saya harus makan kata-kata saya - mereka mengatakan itu telah diperbaiki di masa mendatang!
Timmmm
Terima kasih banyak, Tim. Ini harus menjadi jawaban terakhir yang benar. Saya telah mencari banyak waktu dan tidak ada yang lebih baik dari solusi Anda.!
tainy
bagaimana jika saya perlu menyetel maxHeight saat menggunakan StretchyImageView di atas. Saya tidak menemukan solusi untuk itu.
tainy
17

Menyetel adjustViewBounds ke true dan menggunakan grup tampilan LinearLayout bekerja dengan sangat baik untuk saya. Tidak perlu membuat subkelas atau meminta metrik perangkat:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);
Matt Stoker
sumber
1
... atau menggunakan properti XML ImageView - android: adjustViewBounds = "true"
Anatoliy Shuba
Sempurna! Ini sangat mudah dan ini meregangkan gambar dengan sempurna. Bagaimana jawaban ini tidak benar? Jawaban dengan CustomView tidak berhasil untuk saya (beberapa kesalahan xml yang aneh)
pengguna3800924
13

Saya telah berjuang dengan masalah ini dalam satu bentuk atau lainnya selama USIA, terima kasih, Terima Kasih, TERIMA KASIH .... :)

Saya hanya ingin menunjukkan bahwa Anda bisa mendapatkan solusi yang dapat digeneralisasi dari apa yang dilakukan Bob Lee hanya dengan memperluas View dan menimpa onMeasure. Dengan begitu, Anda dapat menggunakan ini dengan drawable apa pun yang Anda inginkan, dan tidak akan rusak jika tidak ada gambar:

    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);
            }
        }
    }
duhrer
sumber
2
Dari sekian banyak, banyak solusi untuk masalah ini, ini adalah yang pertama yang hanya merupakan solusi drop-in, di mana solusi lainnya selalu memiliki kekurangan (seperti tidak dapat mengubah gambar saat runtime tanpa menulis ulang kode). Terima kasih!
MacD
Ini benar-benar luar biasa - telah berjuang dengan ini selama beberapa waktu sekarang dan semua solusi yang jelas menggunakan atribut xml tidak pernah berhasil. Dengan ini saya bisa mengganti ImageView saya dengan tampilan yang diperluas dan semuanya bekerja seperti yang diharapkan!
slott
Ini benar-benar solusi terbaik untuk masalah ini.
erlando
Itu jawaban yang bagus. Anda dapat menggunakan kelas ini dalam file xml tanpa khawatir seperti ini: <com.yourpackagename.CardImageView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: background = "@ drawable / your_photo" />. Bagus!
arniotaki
11

Dalam beberapa kasus, formula ajaib ini memecahkan masalah dengan indah.

Bagi siapa pun yang berjuang dengan ini yang datang dari platform lain, "ukuran dan bentuk agar pas" ditangani dengan indah di Android, tetapi sulit ditemukan.

Anda biasanya menginginkan kombinasi ini:

  • lebar cocok dengan induk,
  • tinggi bungkus konten,
  • AdjustViewBounds DIAKTIFKAN (sic)
  • skala fitCenter
  • cropToPadding OFF (sic)

Kemudian otomatis dan luar biasa.

Jika Anda seorang pengembang iOS, sungguh menakjubkan betapa sederhananya, di Android, Anda dapat melakukan "ketinggian sel yang benar-benar dinamis" dalam tampilan tabel .. err, maksud saya ListView. Nikmati.

<com.parse.ParseImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/post_image"
    android:src="@drawable/icon_192"
    android:layout_margin="0dp"
    android:cropToPadding="false"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
    android:background="#eff2eb"/>

masukkan deskripsi gambar di sini

Gendut
sumber
6

Saya telah berhasil mencapai ini hanya dengan menggunakan kode XML ini. Mungkin kasus gerhana tidak membuat ketinggian untuk menunjukkan itu meluas agar sesuai; Namun, ketika Anda benar-benar menjalankan ini di perangkat, itu membuat dengan benar dan memberikan hasil yang diinginkan. (baik setidaknya untuk saya)

<FrameLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content">

     <ImageView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:adjustViewBounds="true"
          android:scaleType="centerCrop"
          android:src="@drawable/whatever" />
</FrameLayout>
NeilDA
sumber
Maaf ini tidak berhasil. Ini menskalakan lebar, tetapi centerCrop tidak mempertahankan rasio aspek.
ricosrealm
1
Setelah berjam-jam berjuang dengan kelas kustom yang memperluas ImageView (seperti yang ditulis dalam banyak jawaban dan artikel) dan memiliki pengecualian seperti "Kelas-kelas berikut tidak dapat ditemukan", saya menghapus kode itu. Tiba-tiba saya melihat masalah di ImageView saya adalah di atribut background! Mengubahnya menjadi srcdan ImageView menjadi proporsional.
CoolMind
5

Saya melakukannya dengan nilai-nilai ini dalam LinearLayout:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
Holduel
sumber
Bisakah Anda memberikan contoh nyata? Di mana saya meletakkan nilai-nilai ini di file xml saya?
John Smith Opsional
5

Semua orang melakukan ini dengan program jadi saya pikir jawaban ini akan sangat cocok di sini. Kode ini bekerja untuk saya di xml. Saya BELUM memikirkan tentang rasio, tetapi masih ingin menempatkan jawaban ini jika itu akan membantu siapa pun.

android:adjustViewBounds="true"

Bersulang..

Sindri Þór
sumber
3

Solusi yang sangat sederhana adalah dengan hanya menggunakan fitur yang disediakan oleh RelativeLayout .

Berikut adalah xml yang memungkinkan dengan Android standar Views:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <LinearLayout
            android:id="@+id/button_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_alignParentBottom="true"
            >
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:text="button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <ImageView 
            android:src="@drawable/cat"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:layout_above="@id/button_container"/>
    </RelativeLayout>
</ScrollView>

Triknya adalah Anda mengatur ImageViewuntuk mengisi layar tetapi harus berada di atas tata letak lainnya. Dengan cara ini Anda mencapai semua yang Anda butuhkan.

Warpzit
sumber
2

Ini masalah sederhana dalam pengaturan adjustViewBounds="true"dan scaleType="fitCenter"file XML untuk ImageView!

<ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/image"

        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        />

Catatan: layout_widthdisetel kematch_parent

Kaushik NP
sumber
1

Anda mengatur ScaleType ke ScaleType.FIT_XY. Menurut javadocs , ini akan meregangkan gambar agar pas dengan seluruh area, mengubah rasio aspek jika perlu. Itu akan menjelaskan perilaku yang Anda lihat.

Untuk mendapatkan perilaku yang Anda inginkan ... FIT_CENTER, FIT_START, atau FIT_END hampir sama, tetapi jika gambar lebih sempit daripada tingginya, gambar tidak akan mulai mengisi lebarnya. Anda dapat melihat bagaimana penerapannya, dan Anda mungkin dapat mengetahui bagaimana menyesuaikannya untuk tujuan Anda.

Cheryl Simon
sumber
2
Saya telah mencoba FIT_CENTER, tetapi yang lain belum. Sayangnya mereka juga tidak berfungsi, baik dengan setAdjustViewBounds disetel ke true dan false. Apakah ada cara untuk mendapatkan lebar piksel layar perangkat, serta lebar / tinggi gambar yang diunduh dan mengatur ukurannya secara manual?
fredley
1

ScaleType.CENTER_CROP akan melakukan apa yang Anda inginkan: melebarkan hingga lebar penuh, dan menskalakan tingginya sesuai keinginan. jika ketinggian yang diskalakan melebihi batas layar, gambar akan dipotong.

P. Melch
sumber
1

Lihat, ada solusi yang jauh lebih mudah untuk masalah Anda:

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);
}
pengguna3673209
sumber
1

Anda dapat menggunakan StretchableImageView saya dengan mempertahankan rasio aspek (menurut lebar atau tinggi) bergantung pada lebar dan tinggi drawable:

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);
            }
        }
    }
}
valerybodak.dll
sumber
0

Bagi saya, android: scaleType = "centerCrop" tidak menyelesaikan masalah saya. Itu sebenarnya memperluas citra jauh lebih banyak. Jadi saya mencoba dengan android: scaleType = "fitXY" dan berhasil dengan sangat baik.

Ricardo
sumber
0

Ini bekerja dengan baik sesuai kebutuhan saya

<ImageView android:id="@+id/imgIssue" android:layout_width="fill_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitXY"/>

Anand Savjani
sumber