Android: En boy oranını korurken bir görüntüyü ekran genişliğine nasıl uzatabilirim?


118

Herhangi bir ekran boyutunda (bilinmeyen boyutta, ancak her zaman kabaca kare olan) bir görüntüyü indirmek ve ekranı yatay olarak dolduracak ve görüntünün en boy oranını korumak için dikey olarak genişleyecek şekilde görüntülemek istiyorum. İşte (çalışmayan) kodum. Görüntüyü yatay olarak uzatır, ancak dikey olarak uzatmaz, bu nedenle sıkıştırılır ...

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

tüm Android cihazlar tam olarak aynı genişlik / yükseklik oranına sahip mi? Aksi takdirde, orijinal oranı korurken bir görüntüyü tüm genişliğe / yüksekliğe sığacak şekilde ölçeklemek imkansızdır ...
NoozNooz42

4
Hayır, görüntünün ekranı doldurmasını istemiyorum, sadece ekran genişliğine ölçeklendirmek için, görüntü doğru oranlarda olduğu sürece görüntünün dikey olarak ekranın ne kadarını kapladığı umurumda değil.
fredley


1
'AdjustViewBounds' çalışmadı mı?
Darpan

Yanıtlar:


132

Bunu özel bir görünümle başardım. Layout_width = "fill_parent" ve layout_height = "wrap_content" ayarını yapın ve onu uygun çekilebilirliğe işaret edin:

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

12
Teşekkür ederim!! Bu doğru cevap olmalı! :) Bunu bu yazıda biraz daha esneklik için ayarladım: stackoverflow.com/questions/4677269/… Orada bir ImageView kullanıyorum, böylece biri XML'deki çekilebilirliği ayarlayabilir veya görüntüyü ayarlamak için kodda normal ImageView gibi kullanabilir. Harika çalışıyor!
Patrick Boos

1
Korkunç dahi! bir şampiyon gibi çalıştı! Teşekkürler, sorunumu tüm ekran boyutlarında değil üstte bir resim afişiyle çözdü! Çok teşekkür ederim!
JPM

1
Logonun boş olmasına dikkat edin (İnternet'ten içerik indiriyorsanız). Aksi takdirde, bu harika çalışıyor, Patrick'in XML pasajı için gönderisini takip edin.
Artem Russakovskii

44
Android'de iOS ve Windows Phone'da çok kolay olan şeyler yapmanın ne kadar zor olduğuna inanamıyorum.
mxcl

1
Bazı değişiklikler yaptım, ancak bunları başka bir yere göndermek yerine, bu yanıtı bir topluluk wikisine dönüştürmek mümkün olabilir mi? Değişikliklerim, burada bahsedilen özel durumların yanı sıra düzen tanımı yoluyla hangi tarafın yeniden boyutlandırılacağını seçme yeteneğidir.
Steve Pomeroy

24

Sonunda boyutları manuel olarak oluşturdum ve bu harika çalışıyor:

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

Sorunumu düzeltmek için bu tür bir çözüm arıyordum.Bu hesaplamanın yardımıyla, en boy oranını düşürmeden başarılı bir şekilde görüntüyü yükledim.
Steve

21

Sadece kaynak kodunu okudum ve ImageViewbu iş parçacığındaki alt sınıflandırma çözümlerini kullanmadan bu temelde imkansız. Gelen ImageView.onMeasurebiz bu satırları olsun:

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

Görüntünün boyutları nerede hve nerede ve dolgu.wp*

Ve sonra:

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

Eğer var ise bir layout_height="wrap_content"o ayarlayacaktır widthSize = w + pleft + prightveya başka bir deyişle, görüntü genişliğine eşittir genişliği maksimum.

Bu, kesin bir boyut belirlemediğiniz sürece görüntülerin ASLA büyütülmeyeceği anlamına gelir . Bunun bir hata olduğunu düşünüyorum, ancak Google'ın bunu fark etmesi veya düzeltmesi için iyi şanslar. Düzenleme: Kendi sözlerimi yiyerek bir hata raporu gönderdim ve gelecekteki bir sürümde düzeltildiğini söylediler!

Başka bir çözüm

İşte başka Subclassed geçici çözüm olmakla birlikte, gereken (teoride, gerçekten çok test etmedim!) Her yere kullanmak mümkün ImageView. Kullanmak için ayarlayın layout_width="match_parent"ve layout_height="wrap_content". Kabul edilen çözümden çok daha geneldir. Örneğin, yüksekliğe sığdırmanın yanı sıra genişliğe sığdır da yapabilirsiniz.

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

Bir hata raporu gönderdim . Göz ardı edildiği gibi izleyin!
Timmmm

3
Görünüşe göre sözlerimi yemem gerekiyor - gelecekteki bir sürümde düzeltildiğini söylüyorlar!
Timmmm

Çok teşekkür ederim Tim. Bu son doğru cevap olmalıdır. Çok zaman aradım ve hiçbir şey sizin çözümünüzden daha iyi değil.!
tainy

Yukarıdaki StretchyImageView kullanırken maxHeight değerini ayarlamam gerekirse ne olur? Bunun için herhangi bir çözüm bulamadım.
tainy

17

AdjustViewBounds'u true olarak ayarlamak ve bir LinearLayout görünüm grubu kullanmak benim için çok iyi çalıştı. Alt sınıfa veya cihaz ölçümlerini sormaya gerek yok:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);

1
... veya ImageView XML özelliğini kullanarak - android: AdjustViewBounds = "true"
Anatoliy

Mükemmel! Bu çok kolaydı ve görüntüyü mükemmel bir şekilde uzatıyor. Bu cevap doğru değil mi?
CustomView

13

Yaşlar için bu sorunla şu ya da bu şekilde mücadele ediyorum, teşekkürler, Teşekkürler, TEŞEKKÜRLER .... :)

Sadece View'u genişleterek ve onMeasure'ı geçersiz kılarak Bob Lee'nin yaptıklarından genelleştirilebilir bir çözüm elde edebileceğinizi belirtmek istedim. Bu şekilde, bunu istediğiniz herhangi bir çekilebilir dosya ile kullanabilirsiniz ve resim yoksa kırılmaz:

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

2
Bu soruna yönelik pek çok çözüm arasında, diğerlerinin her zaman dezavantajı olduğu (bir kodu yeniden yazmadan çalışma zamanında görüntüyü değiştirememek gibi), yalnızca açılan bir çözüm olan ilk çözüm budur. Teşekkür ederim!
MacD

Bu saf bir harika - bir süredir bununla mücadele ediyor ve xml özelliklerini kullanan tüm bariz çözümler hiç işe yaramadı. Bununla, ImageView'umu genişletilmiş görünümle değiştirebilirdim ve her şey beklendiği gibi çalıştı!
slott

Bu, bu soruna kesinlikle en iyi çözümdür.
erlando

Bu harika bir cevap. Bu sınıfı xml dosyasında şu şekilde endişelenmeden kullanabilirsiniz: <com.yourpackagename.CardImageView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: background = "@ drawable / your_photo" />. Harika!
arniotaki

11

Bazı durumlarda bu sihirli formül sorunu güzel bir şekilde çözer.

Başka bir platformdan gelen bununla mücadele eden herkes için, "sığdırılacak boyut ve şekil" seçeneği Android'de güzel bir şekilde işleniyor, ancak bulması zor.

Genellikle bu kombinasyonu istersiniz:

  • genişlik eşleme üst öğesi,
  • yükseklik kaydırma içeriği,
  • AdjustViewBounds AÇIK (sic)
  • ölçek fitCenter
  • cropToPadding OFF (sic)

Sonra otomatik ve şaşırtıcı.

Bir iOS geliştiriciyseniz, Android'de bir tablo görünümünde "tamamen dinamik hücre yükseklikleri" yapabilmeniz gerçekten şaşırtıcı .. hata, ListView demek istiyorum. Zevk almak.

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

görüntü açıklamasını buraya girin


6

Bunu yalnızca bu XML kodunu kullanarak başardım. Tutulma, sığması için genişlediğini göstermek için yüksekliği vermiyor olabilir; ancak, bunu bir aygıtta gerçekten çalıştırdığınızda, istenen sonucu düzgün şekilde işler ve sağlar. (Peki, en azından benim için)

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

Maalesef bu işe yaramıyor. Genişliği ölçeklendirir, ancak centerCrop en boy oranını korumaz.
ricosrealm

1
ImageView'i genişleten özel sınıflarla (birçok cevapta ve makalede yazıldığı gibi) ve "Aşağıdaki sınıflar bulunamadı" gibi bir istisna yaşadıktan sonra, bu kodu kaldırdım. Aniden ImageView'umda arka plan özniteliğinde bir sorun olduğunu fark ettim! Olarak değiştirildi srcve ImageView orantılı hale geldi.
CoolMind

5

Bunu bir LinearLayout içinde şu değerlerle yaptım:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent

Gerçek bir örnek verebilir misin? Bu değerleri xml dosyamda nereye koyarım?
John Smith İsteğe Bağlı

5

Herkes bunu programla yapıyor, bu yüzden bu cevabın buraya tam olarak uyacağını düşündüm. Bu kod xml'de benim için çalıştı. Henüz oran hakkında düşünmüyorum, ama yine de birine yardımcı olacaksa bu cevabı vermek istedim.

android:adjustViewBounds="true"

Şerefe ..


3

Çok basit bir çözüm, sadece tarafından sağlanan özellikleri kullanmaktır RelativeLayout.

Standart Android ile bunu mümkün kılan xml 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>

İşin püf noktası ImageView, ekranı dolduracak şekilde ayarlamanız, ancak diğer düzenlerin üzerinde olması gerektiğidir. Bu şekilde ihtiyacınız olan her şeyi elde edersiniz.


2

ImageView için XML dosyasında adjustViewBounds="true"ve ayarının basit meselesi scaleType="fitCenter"!

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

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

Not: layout_widthayarlandımatch_parent


1

ScaleType'ı ScaleType.FIT_XY olarak ayarlıyorsunuz. Javadocs'a göre bu, görüntüyü tüm alana sığacak şekilde uzatacak ve gerekirse en boy oranını değiştirecektir. Bu gördüğünüz davranışı açıklar.

İstediğiniz davranışı elde etmek için ... FIT_CENTER, FIT_START veya FIT_END yakındır, ancak görüntü uzun olduğundan daha darsa, genişliği doldurmaya başlamaz. Yine de bunların nasıl uygulandığına bakabilirsiniz ve muhtemelen amacınız için nasıl ayarlayacağınızı çözebilmelisiniz.


2
FIT_CENTER'ı denedim ama diğerlerini denemedim. Ne yazık ki ikisi de setAdjustViewBounds true ve false olarak ayarlandığında çalışmazlar. Cihazın ekranının piksel genişliğinin yanı sıra indirilen görüntünün genişlik / yüksekliğini elde etmek ve boyutu manuel olarak ayarlamak için bir yol var mı?
fredley

1

ScaleType.CENTER_CROP istediğinizi yapacak: tam genişliğe kadar uzatın ve yüksekliği buna göre ölçekleyin. ölçeklenen yükseklik ekran sınırlarını aşarsa, görüntü kırpılır.


1

Bakın, sorununuz için çok daha kolay bir çözüm var:

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

1

Çekilebilir ürünün genişliğine ve yüksekliğine bağlı olarak en boy oranını (genişliğe veya yüksekliğe göre) koruyarak StretchableImageView'ımı kullanabilirsiniz :

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

0

Benim için android: scaleType = "centerCrop" sorunumu çözmedi. Aslında görüntüyü daha da genişletti. Bu yüzden android: scaleType = "fitXY" ile denedim ve mükemmel çalıştı.


0

Bu benim ihtiyacıma göre iyi çalışıyor

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.