簡體   English   中英

Android:如何在保持縱橫比的同時將圖像拉伸到屏幕寬度?

[英]Android: How to stretch an image to the screen width while maintaining aspect ratio?

我想下載一個圖像(大小未知,但始終大致為正方形)並顯示它,使其水平填充屏幕,並在任何屏幕尺寸上垂直拉伸以保持圖像的縱橫比。 這是我的(非工作)代碼。 它水平拉伸圖像,而不是垂直拉伸,所以它被壓扁了......

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

我使用自定義視圖完成了此操作。 設置 layout_width="fill_parent" 和 layout_height="wrap_content",並將其指向適當的可繪制對象:

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

最后,我手動生成了尺寸,效果很好:

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

我剛剛閱讀了ImageView的源代碼,如果不使用該線程中的子類化解決方案,基本上是不可能的。 ImageView.onMeasure我們得到這些行:

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

其中hw是圖像的尺寸, p*是填充。

進而:

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

因此,如果您有layout_height="wrap_content"它將設置widthSize = w + pleft + pright ,或者換句話說,最大寬度等於圖像寬度。

這意味着除非您設置精確尺寸,否則圖像永遠不會放大 我認為這是一個錯誤,但祝 Google 注意到或修復它好運。 編輯:吃我自己的話,我提交了一個錯誤報告,他們說它已在未來的版本中修復!

另一種解決方案

這是另一種子類化的解決方法,但您應該(理論上,我還沒有真正測試過它!)能夠在ImageView任何地方使用它。 要使用它,請設置layout_width="match_parent"layout_height="wrap_content" 它也比公認的解決方案更通用。 例如,您可以進行適合高度以及適合寬度的操作。

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

將 adjustViewBounds 設置為 true 並使用 LinearLayout 視圖組對我來說效果很好。 無需子類化或詢問設備指標:

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

我一直在以一種或另一種形式為這個問題苦苦掙扎 AGES,謝謝,謝謝,謝謝....:)

我只是想指出,您可以通過擴展 View 和覆蓋 onMeasure 來從 Bob Lee 所做的事情中獲得一個通用的解決方案。 這樣你就可以將它與你想要的任何可繪制對象一起使用,如果沒有圖像,它也不會中斷:

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

在某些情況下,這個神奇的公式可以很好地解決問題。

對於來自另一個平台的任何人來說, “適合的大小和形狀”選項在 Android 中處理得很好,但很難找到。

您通常需要這種組合:

  • 寬度匹配父級,
  • 高度包裹內容,
  • 調整視圖邊界打開(原文如此)
  • 比例擬合中心
  • cropToPadding 關閉(原文如此)

然后它是自動的和驚人的。

如果您是 iOS 開發人員,那么在 Android 中,您可以在表格視圖中實現“完全動態的單元格高度”,這真是太神奇了……呃,我的意思是 ListView。 享受。

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

在此處輸入圖片說明

我僅使用此 XML 代碼就設法實現了這一點。 可能是 Eclipse 沒有渲染高度以顯示它擴展以適應; 但是,當您在設備上實際運行它時,它會正確呈現並提供所需的結果。 (至少對我來說)

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

每個人都在以編程方式執行此操作,所以我認為這個答案非常適合這里。 這段代碼在 xml 中對我有用。 我還沒有考慮比率,但如果它對任何人有幫助,仍然想放置這個答案。

android:adjustViewBounds="true"

干杯..

我在 LinearLayout 中使用這些值做到了:

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

一個非常簡單的解決方案是只使用RelativeLayout提供的功能。

這是使標准 Android Views成為可能的 xml:

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

訣竅是您將ImageView設置為填充屏幕,但它必須高於其他布局。 通過這種方式,您可以實現所需的一切。

在 ImageView 的 XML 文件中設置adjustViewBounds="true"scaleType="fitCenter"很簡單!

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

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

注意: layout_width設置為match_parent

您正在將 ScaleType 設置為 ScaleType.FIT_XY。 根據javadocs ,這將拉伸圖像以適應整個區域,必要時更改縱橫比。 這將解釋您所看到的行為。

要獲得您想要的行為... FIT_CENTER、FIT_START 或 FIT_END 很接近,但如果圖像窄於高,則不會開始填充寬度。 你可以看看這些是如何實現的,你應該能夠弄清楚如何根據你的目的進行調整。

看看你的問題有一個更簡單的解決方案:

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

您可以使用我的StretchableImageView保留縱橫比(按寬度或按高度),具體取決於可繪制的寬度和高度:

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

ScaleType.CENTER_CROP 會做你想做的事:拉伸到全寬,並相應地縮放高度。 如果縮放高度超過屏幕限制,圖像將被裁剪。

對我來說 android:scaleType="centerCrop" 沒有解決我的問題。 它實際上更多地擴展了圖像。 所以我嘗試使用 android:scaleType="fitXY" 並且效果很好。

這按照我的要求工作正常

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM