Android: Thu nhỏ hình ảnh có thể vẽ hoặc hình nền?


134

Trên một bố cục tôi muốn chia tỷ lệ hình nền (giữ tỷ lệ khung hình của nó) theo không gian được phân bổ khi trang được tạo. Có ai có một ý tưởng làm thế nào để làm điều này?

Tôi đang sử dụng layout.setBackgroundDrawable()và một BitmapDrawable()để thiết lập gravityđể cắt và điền, nhưng không thấy bất kỳ tùy chọn nào để nhân rộng.


Tôi đã gặp vấn đề này và làm theo khuyến nghị của Dweebo với thay đổi được đề xuất bởi Anke: đã thay đổi fitXY thành fitCenter. Làm việc tốt.

Câu trả lời:


90

Để tùy chỉnh tỷ lệ hình ảnh nền, tạo một tài nguyên như thế này:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

Sau đó, nó sẽ được căn giữa trong chế độ xem nếu được sử dụng làm nền. Ngoài ra còn có các cờ khác: http://developer.android.com/guide/topics/resource/drawable-resource.html


6
Tôi đã thử sử dụng bitmap nhưng hình ảnh chỉ đơn giản là chính giữa, không được thu nhỏ như Sam đang yêu cầu. Khi tôi sử dụng ImageView, nó hoạt động rất đẹp, không gây ồn ào. (nb: Tôi cũng không cuộn trong trường hợp của mình.) Tôi sẽ tăng cường bitmap bằng cách nào để thu nhỏ hình ảnh?
Joe D'Andrea

7
Các giá trị có thể là: "top" | "dưới cùng" | "trái" | "đúng" | "centre_vertical" | "fill_vertical" | "trung tâm_horizontal" | "fill_horizontal" | "trung tâm" | "điền" | "clip_vertical" | "clip_horizontal"
Aleks N.

30
không có tham số nào trong số này chia tỷ lệ bitmap, bằng cách giữ tỷ lệ khung hình của nó, vì vậy câu trả lời là sai.
Malachiasz

7
Malachiasz thân mến, 'trung tâm' không tôn trọng tỷ lệ khung hình. Nhưng nó sẽ không thu nhỏ hình ảnh xuống. Điều đó có nghĩa là nó là một tính năng nguy hiểm để sử dụng. Tùy thuộc vào độ phân giải của mục tiêu mà bạn không bao giờ biết tỷ lệ chính xác sẽ xuất hiện như thế nào. Một giải pháp nửa khẳng định khác của Google.

2
có cách nào để thêm thuộc tính scaleType không?
ademar111190

57

Không cố gắng thực hiện chính xác những gì bạn muốn, nhưng bạn có thể chia tỷ lệ ImageView bằng cách sử dụng android:scaleType="fitXY"
và nó sẽ có kích thước phù hợp với bất kỳ kích thước nào bạn cung cấp cho ImageView.

Vì vậy, bạn có thể tạo FrameLayout cho bố cục của mình, đặt ImageView bên trong nó và sau đó bất kỳ nội dung nào khác bạn cần trong FrameLayout cũng như một nền trong suốt.

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>

1
Có thể làm điều này trên Surface. Nếu không, tôi có thể hiển thị bản xem trước (cho ứng dụng ghi âm) bằng FrameLayout không?
Namratha

1
@dweebo: Bạn đã thử cái này chưa? Nó dường như không làm việc cho tôi.
bay tốc độ

3
Từ chối sử dụng hai thành phần UI thay vì một thành phần là một ý tưởng tồi, đặc biệt đối với các thành phần bận rộn như ListView. Nhiều khả năng bạn sẽ gặp rắc rối trong khi cuộn. Giải pháp phù hợp: stackoverflow.com/a/9362168/145046
Aleks N.

Tôi đã thử một scaleType của fitCenter và centreCrop (cùng với một vài biến thể của hình vẽ / hình ảnh cho các mật độ khác nhau) và nó đã hoạt động! Hoặc là loại tỷ lệ có ý nghĩa đối với tôi - nhiều hơn một sở thích cá nhân dựa trên hình ảnh, tôi nghi ngờ. Lưu ý rằng tôi đã sử dụng hợp nhất thay vì FrameLayout và không có cuộn trong trường hợp cụ thể của tôi.
Joe D'Andrea

2
centreInside là phù hợp, không phù hợp vớiXX, vì fitXY không giữ tỷ lệ khung hình của hình ảnh
Malachiasz

35

Có một cách dễ dàng để làm điều này từ drawable:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

Nhược điểm duy nhất là nếu không có đủ không gian, hình ảnh của bạn sẽ không được hiển thị đầy đủ, nhưng nó sẽ bị cắt bớt, tôi không thể tìm ra cách để làm điều này trực tiếp từ một bức vẽ. Nhưng từ các thử nghiệm tôi đã làm nó hoạt động khá tốt, và nó không cắt được nhiều hình ảnh. Bạn có thể chơi nhiều hơn với các tùy chọn trọng lực.

Một cách khác sẽ chỉ tạo ra một bố trí, nơi bạn sẽ sử dụng một ImageViewvà thiết lập scaleTypeđể fitCenter.


2
Giải pháp này chắc chắn là những gì tôi đã tìm kiếm trong một thời gian dài để thay thế thuộc tính nền của chủ đề của tôi và tránh tỷ lệ hình ảnh khác nhau khi chế độ xem có Tab hay không. Cảm ơn đã chia sẻ điều này.
Bibu

1
Nó hiển thị lỗi:error: <item> must have a 'name' attribute.
Dmitry

thx @lonutNegru, +1 để cứu ngày của tôi :)
Ravi Vaniya

18

Để giữ tỷ lệ khung hình bạn phải sử dụng android:scaleType=fitCenterhoặc fitStartvvfitXY sẽ không giữ tỷ lệ khung hình gốc của hình ảnh!

Lưu ý điều này chỉ hoạt động đối với hình ảnh có srcthuộc tính, không dành cho hình nền.


18

Sử dụng hình ảnh làm kích thước nền để bố trí:

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

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >


    </LinearLayout>

</FrameLayout>

Cảm ơn! Cũng android:scaleType="centerCrop"hoạt động.
CoolMind

5

Khi bạn đặt Drawable của ImageView bằng cách sử dụng setBackgroundDrawablephương thức, hình ảnh sẽ luôn được thu nhỏ. Các thông số như adjustViewBoundshoặc khác nhau ScaleTypessẽ chỉ bị bỏ qua. Giải pháp duy nhất để giữ tỷ lệ khung hình tôi tìm thấy, là thay đổi kích thước ImageViewsau khi tải drawable của bạn. Đây là đoạn mã tôi đã sử dụng:

// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;

if (h2w > 1) {
    // height is greater than width
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth * h2w;
    } else {
        newContainerHeight = (float) containerHeight;
        newContainerWidth = newContainerHeight / h2w;
    }
} else {
    // width is greater than height
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth / h2w; 
    } else {
        newContainerWidth = (float) containerHeight;
        newContainerHeight = newContainerWidth * h2w;       
    }
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);

Trong đoạn mã trên là bmp , đối tượng Bitmap sẽ được hiển thị và imageViewImageView đối tượng

Một điều quan trọng cần lưu ý là sự thay đổi của các tham số bố trí . Điều này là cần thiết bởi vì setMaxHeightsetMaxWidthsẽ chỉ tạo ra sự khác biệt nếu chiều rộng và chiều cao được xác định để bọc nội dung, không điền vào phần gốc. Mặt khác, điền vào cha mẹ là cài đặt mong muốn, bởi vì nếu không containerWidthcontainerHeightcả hai sẽ có các giá trị bằng 0. Vì vậy, trong tệp bố cục của bạn, bạn sẽ có một cái gì đó như thế này cho ImageView của bạn:

...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...

4

Đây không phải là giải pháp hiệu quả nhất, nhưng như ai đó đã đề xuất thay vì nền, bạn có thể tạo FrameLayout hoặc RelativeLayout và sử dụng ImageView làm nền giả - các yếu tố khác sẽ được đặt ở trên nó:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitStart"
        android:src="@drawable/menu_icon_exit" />

    <Button
        android:id="@+id/bSomeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="61dp"
        android:layout_marginTop="122dp"
        android:text="Button" />

</RelativeLayout>

Vấn đề với ImageView là chỉ các thang đo có sẵn là: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imag- )

và để "chia tỷ lệ hình nền (giữ tỷ lệ khung hình)" trong một số trường hợp, khi bạn muốn một hình ảnh lấp đầy toàn bộ màn hình (ví dụ hình ảnh nền) và tỷ lệ khung hình của màn hình khác với hình ảnh, scaleType cần thiết là loại của TOP_CROP, vì:

CENTER_CROP căn giữa hình ảnh được chia tỷ lệ thay vì căn chỉnh cạnh trên với cạnh trên của chế độ xem hình ảnh và FIT_START phù hợp với chiều cao màn hình và không lấp đầy chiều rộng. Và như người dùng Anke nhận thấy FIT_XY không giữ tỷ lệ khung hình.

Rất vui khi ai đó đã mở rộng ImageView để hỗ trợ TOP_CROP

public class ImageViewScaleTypeTopCrop extends ImageView {
    public ImageViewScaleTypeTopCrop(Context context) {
        super(context);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup() {
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {

        float frameWidth = frameRight - frameLeft;
        float frameHeight = frameBottom - frameTop;

        if (getDrawable() != null) {

            Matrix matrix = getImageMatrix();
            float scaleFactor, scaleFactorWidth, scaleFactorHeight;

            scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
            scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();

            if (scaleFactorHeight > scaleFactorWidth) {
                scaleFactor = scaleFactorHeight;
            } else {
                scaleFactor = scaleFactorWidth;
            }

            matrix.setScale(scaleFactor, scaleFactor, 0, 0);
            setImageMatrix(matrix);
        }

        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

https://stackoverflow.com/a/14815588/2075875

Bây giờ IMHO sẽ hoàn hảo nếu ai đó viết tùy chỉnh Drawable có tỷ lệ hình ảnh như thế. Sau đó, nó có thể được sử dụng làm tham số nền.


Reflog gợi ý để đặt trước drawable trước khi sử dụng nó. Dưới đây là hướng dẫn cách thực hiện: Java (Android): Làm thế nào để chia tỷ lệ có thể vẽ mà không cần Bitmap? Mặc dù nó có nhược điểm, nhưng bản vẽ / bitmap được nâng cấp sẽ sử dụng nhiều RAM hơn, trong khi việc mở rộng khi sử dụng ImageView không cần nhiều bộ nhớ hơn. Ưu điểm có thể là tải bộ xử lý ít hơn.


Đẹp quá Liệu chính xác những gì tôi đã có sau: hình nền để lấp đầy toàn bộ màn hình bằng cách tăng tỷ lệ chiều rộng theo tỷ lệ và cắt xén phần dưới của hình ảnh.
CMash

4

Mã dưới đây làm cho bitmap hoàn hảo với cùng kích thước của hình ảnh. Lấy chiều cao và chiều rộng của hình ảnh bitmap và sau đó tính toán chiều cao và chiều rộng mới với sự trợ giúp của các tham số của hình ảnh. Điều đó cung cấp cho bạn hình ảnh cần thiết với tỷ lệ khung hình tốt nhất.

int bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)

2

Những gì Dweebo đề xuất làm việc. Nhưng theo quan điểm khiêm tốn của tôi thì không cần thiết. Một nền có thể vẽ quy mô tốt của chính nó. Khung nhìn phải có chiều rộng và chiều cao cố định, như trong ví dụ sau:

 < RelativeLayout 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="500dip"
        android:layout_height="450dip"
        android:layout_centerInParent="true"
        android:background="@drawable/my_drawable"
        android:orientation="vertical"
        android:padding="30dip"
        >
        ...
     </LinearLayout>
 < / RelativeLayout>

2
Hiểu biết của tôi là một nền sẽ mở rộng để lấp đầy một khung nhìn, nhưng không co lại.
Edward Falk

1

Một lựa chọn để thử là đưa hình ảnh vào drawable-nodpi thư mục và đặt nền của bố cục thành id tài nguyên có thể vẽ.

Điều này chắc chắn hoạt động với việc thu nhỏ lại, mặc dù tôi chưa thử nghiệm với nhân rộng.


0

Kotlin:

Nếu bạn cần vẽ một bitmap trong Chế độ xem, được chia tỷ lệ thành FIT.

Bạn có thể thực hiện các phép tính thích hợp để đặt bm chiều cao bằng với container và điều chỉnh chiều rộng, trong trường hợp tỷ lệ chiều rộng bm chiều cao nhỏ hơn tỷ lệ chiều rộng của container so với chiều cao hoặc nghịch đảo trong kịch bản ngược lại.

Hình ảnh:

Hình ảnh ví dụ 1

Hình ảnh ví dụ 2

// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()

if (rb < rc) {
    // Bitmap Width to Height ratio is less than Container ratio
    // Means, bitmap should pin top and bottom, and have some space on sides.
    //              _____          ___
    // container = |_____|   bm = |___| 
    val bmHeight = ch - 4 //4 for container border
    val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
    val bmWidth = cw - 4 //4 for container border
    val bmHeight = 1f/rb * cw
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}

-4

bạn sẽ phải chia tỷ lệ có thể vẽ trước khi bạn sử dụng nó làm nền


Đây thực sự là lựa chọn duy nhất? Có loại container nào để đặt ImageView trong đó có thể chỉ định kích thước hiển thị không?
GrkEngineer

Tôi cũng gặp sự cố với SurfaceView
AZ_

3
Không có gì giống như phù hợp với khía cạnh, điền vào khía cạnh, trên cùng bên trái, trên cùng như trên iOS? Thật tệ ..
Maciej Swic

Như @Aleksej đã nói, một câu trả lời sai sự thật.
người học

-5

Bạn có thể sử dụng một trong những điều sau đây:

android: vity = "fill_horizontal | clip_vertical"

Hoặc là

android: vity = "fill_vertical | clip_horizontal"

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.