Android: Làm thế nào để kéo dài hình ảnh theo chiều rộng màn hình trong khi vẫn duy trì tỷ lệ khung hình?


118

Tôi muốn tải xuống một hình ảnh (có kích thước không xác định, nhưng luôn luôn là hình vuông) và hiển thị nó sao cho nó lấp đầy màn hình theo chiều ngang và kéo dài theo chiều dọc để duy trì tỷ lệ co của hình ảnh, trên bất kỳ kích thước màn hình nào. Đây là mã (không hoạt động) của tôi. Nó kéo dài hình ảnh theo chiều ngang, chứ không phải theo chiều dọc, vì vậy nó bị bóp méo ...

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

có phải tất cả các thiết bị Android đều có cùng tỷ lệ chiều rộng / chiều cao không? Nếu không, nó chỉ đơn giản là không thể mở rộng quy mô một hình ảnh để phù hợp với chiều rộng toàn bộ / chiều cao trong khi vẫn giữ tỉ lệ ban đầu ...
NoozNooz42

4
Không, tôi không muốn hình ảnh lấp đầy màn hình, chỉ để điều chỉnh theo chiều rộng màn hình, tôi không quan tâm hình ảnh chiếm bao nhiêu phần trăm theo chiều dọc của màn hình, miễn là hình ảnh theo đúng tỷ lệ.
fredley

1
Câu hỏi tương tự, câu trả lời hay: stackoverflow.com/questions/4677269/…
Pēteris Caune,

1
'AdjustViewBounds' không hoạt động?
Darpan

Câu trả lời:


132

Tôi đã hoàn thành điều này với một chế độ xem tùy chỉnh. Đặt layout_width = "fill_parent" và layout_height = "wrap_content", và trỏ nó đến tệp có thể vẽ thích hợp:

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
Cảm ơn bạn!! Đây phải là câu trả lời chính xác! :) Tôi đã điều chỉnh điều này để linh hoạt hơn một chút trong bài đăng này: stackoverflow.com/questions/4677269/… Ở đó, tôi sử dụng ImageView để người ta có thể đặt có thể vẽ trong XML hoặc sử dụng nó như ImageView bình thường trong mã để đặt hình ảnh. Hoạt động tuyệt vời!
Patrick Boos

1
Thiên tài Freakin! hoạt động như một nhà vô địch! Cảm ơn nó đã giải quyết vấn đề của tôi với một biểu ngữ hình ảnh trên cùng không đúng ở mọi kích thước màn hình! Cảm ơn bạn rất nhiều!
JPM

1
Chú ý biểu trưng bị rỗng (nếu bạn đang tải nội dung từ Internet). Nếu không, điều này hoạt động tốt, hãy làm theo bài đăng của Patrick cho đoạn mã XML.
Artem Russakovskii

44
Tôi không thể tin rằng làm những việc trên Android mà lại dễ dàng như trên iOS và Windows Phone lại khó đến mức nào.
mxcl

1
Tôi đã thực hiện một số thay đổi, nhưng thay vì đăng chúng ở nơi khác, liệu có thể chuyển câu trả lời này thành wiki cộng đồng không? Những thay đổi của tôi là tất cả những thứ xử lý các trường hợp đặc biệt được đề cập ở đây, cũng như khả năng chọn bên nào được thay đổi kích thước theo định nghĩa bố cục.
Steve Pomeroy vào

24

Cuối cùng, tôi đã tạo các thứ nguyên theo cách thủ công, điều này hoạt động tốt:

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

Tôi đã tìm kiếm loại giải pháp này để khắc phục sự cố của mình. Với sự trợ giúp của phép tính này, tôi đã tải thành công hình ảnh mà không giảm tỷ lệ khung hình.
Steve

21

Tôi chỉ đọc mã nguồn ImageViewvà về cơ bản là không thể nếu không sử dụng các giải pháp phân lớp con trong chủ đề này. Trong ImageView.onMeasurechúng ta có những dòng sau:

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

Kích thước của hình ảnh ở đâu hvà ở đâu w, và p*là phần đệm.

Và sau đó:

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

Vì vậy, nếu bạn có, layout_height="wrap_content"nó sẽ đặt widthSize = w + pleft + pright, hay nói cách khác, chiều rộng tối đa bằng chiều rộng hình ảnh.

Điều này có nghĩa là trừ khi bạn đặt kích thước chính xác, hình ảnh KHÔNG BAO GIỜ được phóng to . Tôi coi đây là một lỗi, nhưng may mắn được Google chú ý hoặc sửa chữa nó. Chỉnh sửa: Ăn theo lời của tôi, tôi đã gửi một báo cáo lỗi và họ nói rằng nó đã được sửa trong một bản phát hành trong tương lai!

Giải pháp khác

Đây là một giải pháp thay thế lớp con khác, nhưng bạn nên (về lý thuyết, tôi chưa thực sự thử nghiệm nó nhiều!) Có thể sử dụng nó ở bất cứ đâu ImageView. Để sử dụng nó thiết lập layout_width="match_parent", và layout_height="wrap_content". Nó cũng tổng quát hơn nhiều so với giải pháp được chấp nhận. Ví dụ: bạn có thể làm vừa với chiều cao cũng như vừa với chiều rộng.

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

Tôi đã gửi một báo cáo lỗi . Xem, vì nó bị bỏ qua!
Timmmm

3
Rõ ràng là tôi phải ăn lời của mình - họ nói rằng nó đã được sửa trong một bản phát hành trong tương lai!
Timmmm

Cảm ơn Tim rất nhiều. Đây sẽ là câu trả lời đúng cuối cùng. Tôi đã tìm kiếm rất nhiều thời gian và không có gì tốt hơn giải pháp của bạn.!
tainy

điều gì sẽ xảy ra nếu tôi cần đặt maxHeight khi sử dụng StretchyImageView ở trên. Tôi không tìm thấy bất kỳ giải pháp nào cho điều đó.
tainy

17

Cài đặt AdjustViewBounds thành true và sử dụng nhóm chế độ xem LinearLayout hoạt động rất tốt đối với tôi. Không cần phân lớp hoặc yêu cầu số liệu thiết bị:

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

1
... hoặc sử dụng thuộc tính ImageView XML - android: AdjustViewBounds = "true"
Anatoliy Shuba

Hoàn hảo! Điều này rất dễ dàng và nó kéo dài hình ảnh hoàn hảo. Cách trả lời này không phải là một trong những chính xác? Câu trả lời với CustomView không làm việc cho tôi (một số lỗi xml lạ)
user3800924

13

Tôi đã phải vật lộn với vấn đề này bằng hình thức này hay hình thức khác cho TUỔI, cảm ơn bạn, cảm ơn bạn, cảm ơn bạn .... :)

Tôi chỉ muốn chỉ ra rằng bạn có thể nhận được một giải pháp tổng quát từ những gì Bob Lee đã thực hiện bằng cách mở rộng Chế độ xem và ghi đè onMeasure. Bằng cách đó, bạn có thể sử dụng điều này với bất kỳ đồ vật có thể vẽ nào bạn muốn và nó sẽ không bị vỡ nếu không có hình ảnh:

    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
Trong số rất nhiều giải pháp cho vấn đề này, đây là giải pháp đầu tiên chỉ là giải pháp thả vào, trong đó các giải pháp khác luôn có nhược điểm (như không thể thay đổi hình ảnh trong thời gian chạy mà không cần viết lại mã). Cảm ơn bạn!
MacD

Đây là sự tuyệt vời thuần túy - đã phải vật lộn với điều này một thời gian và tất cả các giải pháp rõ ràng bằng cách sử dụng các thuộc tính xml không bao giờ hoạt động. Với điều này, tôi chỉ có thể thay thế ImageView của mình bằng chế độ xem mở rộng và mọi thứ hoạt động như mong đợi!
slott

Đây hoàn toàn là giải pháp tốt nhất cho vấn đề này.
erlando

Đó là một câu trả lời tuyệt vời. Bạn có thể sử dụng lớp này trong tệp xml mà không cần lo lắng như vậy: <com.yourpackagename.CardImageView android: layout_width = "fill_parent" android: layout_height = "wrap_content" android: background = "@ drawable / your_photo" />. Tuyệt quá!
arniotaki

11

Trong một số trường hợp, công thức kỳ diệu này giải quyết vấn đề một cách tuyệt vời.

Đối với bất kỳ ai đang vật lộn với điều này đến từ một nền tảng khác, "kích thước và hình dạng để vừa vặn" tùy chọn được xử lý tuyệt vời trong Android, nhưng rất khó tìm.

Bạn thường muốn kết hợp này:

  • chiều rộng đối sánh gốc,
  • nội dung bao bọc chiều cao,
  • điều chỉnhViewBounds được BẬT (sic)
  • scale fitCenter
  • cropToPadding TẮT (sic)

Sau đó, nó tự động và tuyệt vời.

Nếu bạn là một nhà phát triển iOS, thật tuyệt vời khi đơn giản là trong Android, bạn có thể thực hiện "chiều cao ô hoàn toàn động" trong chế độ xem bảng .. ờ, ý tôi là ListView. Thưởng thức.

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

nhập mô tả hình ảnh ở đây


6

Tôi đã cố gắng đạt được điều này chỉ bằng cách sử dụng mã XML này. Có thể xảy ra trường hợp nhật thực không hiển thị chiều cao để cho thấy nó mở rộng để vừa vặn; tuy nhiên, khi bạn thực sự chạy điều này trên một thiết bị, nó hiển thị đúng cách và cung cấp kết quả mong muốn. (Vâng, ít nhất đối với tôi)

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

Xin lỗi, điều này không hoạt động. Nó chia tỷ lệ chiều rộng, nhưng centerCrop không duy trì tỷ lệ khung hình.
ricosrealm

1
Sau nhiều giờ vật lộn với các lớp tùy chỉnh mở rộng ImageView (như được viết trong nhiều câu trả lời và bài báo) và có một ngoại lệ như "Không thể tìm thấy các lớp sau", tôi đã xóa mã đó. Đột nhiên tôi nhận thấy một vấn đề trong ImageView của tôi là thuộc tính nền! Đã thay đổi nó thành srcvà ImageView trở nên tương xứng.
CoolMind vào

5

Tôi đã làm điều đó với các giá trị này trong LinearLayout:

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

Bạn có thể đưa ra một ví dụ thực tế? Tôi đặt các giá trị này ở đâu trong tệp xml của mình?
John Smith Tùy chọn

5

Mọi người đều đang làm chương trình này một cách thuần thục nên tôi nghĩ câu trả lời này sẽ hoàn toàn phù hợp ở đây. Mã này làm việc cho tôi trong xml. Tôi CHƯA nghĩ về tỷ lệ, nhưng vẫn muốn đặt câu trả lời này nếu nó có ích cho bất kỳ ai.

android:adjustViewBounds="true"

Chúc mừng ..


3

Một giải pháp rất đơn giản là chỉ cần sử dụng các tính năng được cung cấp bởi RelativeLayout .

Đây là xml làm cho nó khả thi với Android tiêu chuẩn 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>

Bí quyết là bạn đặt ImageViewđể lấp đầy màn hình nhưng nó phải ở trên các bố cục khác. Bằng cách này, bạn đạt được mọi thứ bạn cần.


2

Vấn đề đơn giản về cài đặt adjustViewBounds="true"scaleType="fitCenter"trong tệp XML cho ImageView!

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

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

Lưu ý: layout_widthđược đặt thànhmatch_parent


1

Bạn đang đặt ScaleType thành ScaleType.FIT_XY. Theo javadocs , điều này sẽ kéo giãn hình ảnh để vừa với toàn bộ khu vực, thay đổi tỷ lệ khung hình nếu cần. Điều đó sẽ giải thích hành vi bạn đang thấy.

Để có được hành vi bạn muốn ... FIT_CENTER, FIT_START hoặc FIT_END gần giống nhau, nhưng nếu hình ảnh hẹp hơn chiều cao, hình ảnh sẽ không bắt đầu lấp đầy chiều rộng. Mặc dù vậy, bạn có thể xem những điều đó được triển khai như thế nào và có thể bạn sẽ tìm ra cách điều chỉnh nó cho mục đích của mình.


2
Tôi đã thử FIT_CENTER, nhưng không phải những người khác. Thật không may, chúng cũng không hoạt động, cả với setAdjustViewBounds được đặt thành true và false. Có cách nào để lấy chiều rộng pixel của màn hình thiết bị, cũng như chiều rộng / chiều cao của hình ảnh đã tải xuống và đặt kích thước theo cách thủ công không?
fredley

1

ScaleType.CENTER_CROP sẽ làm những gì bạn muốn: kéo dài đến hết chiều rộng và chia tỷ lệ chiều cao cho phù hợp. nếu chiều cao được chia tỷ lệ vượt quá giới hạn màn hình, hình ảnh sẽ bị cắt.


1

Hãy xem có một giải pháp dễ dàng hơn nhiều cho vấn đề của bạn:

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

Bạn có thể sử dụng StretchableImageView của tôi để duy trì tỷ lệ co (theo chiều rộng hoặc chiều cao) tùy thuộc vào chiều rộng và chiều cao của có thể vẽ:

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

Đối với tôi, android: scaleType = "centerCrop" không giải quyết được sự cố của tôi. Nó thực sự mở rộng hình ảnh theo cách nhiều hơn. Vì vậy, tôi đã thử với android: scaleType = "fitXY" và nó hoạt động xuất sắc.


0

Điều này hoạt động tốt theo yêu cầu của tôi

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.