Android: Skala gambar yang Dapat Digambar atau latar belakang?

134

Pada tata letak saya ingin skala gambar latar belakang (menjaga rasio aspeknya) ke ruang yang dialokasikan ketika halaman dibuat. Adakah yang punya ide bagaimana melakukan ini?

Saya menggunakan layout.setBackgroundDrawable()dan BitmapDrawable()untuk mengatur gravitykliping dan mengisi, tetapi tidak melihat opsi untuk penskalaan.

finnmglas
sumber
Saya mengalami masalah ini dan mengikuti rekomendasi Dweebo dengan perubahan yang disarankan oleh Anke: mengubah fitXY menjadi fitCenter. Bekerja dengan baik.

Jawaban:

90

Untuk menyesuaikan penskalaan gambar latar belakang buat sumber daya seperti ini:

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

Maka akan berpusat pada tampilan jika digunakan sebagai latar belakang. Ada juga flag lain: http://developer.android.com/guide/topics/resources/drawable-resource.html

Aleks N.
sumber
6
Saya mencoba menggunakan bitmap tetapi gambarnya hanya terpusat, tidak diskalakan seperti yang diminta Sam. Ketika saya menggunakan ImageView, ia bekerja dengan indah, tidak ada masalah. (nb: Saya juga tidak menggulir dalam kasus saya.) Apa yang akan saya tambahkan bitmap untuk skala gambar?
Joe D'Andrea
7
Nilai yang mungkin adalah: "atas" | "bawah" | "kiri" | "benar" | "center_vertical" | "fill_vertical" | | "center_horizontal" | "fill_horizontal" | "tengah" | "isi" | "clip_vertical" | "clip_horizontal"
Aleks N.
30
tidak satu pun dari parameter ini yang menskalakan bitmap, dengan menjaga aspek rasionya, jadi jawabannya salah.
Malachiasz
7
Dear Malachiasz, 'center' menghargai aspek rasio. Tapi itu tidak akan menurunkan skala gambar. Yang berarti fitur ini agak berbahaya untuk digunakan. Tergantung pada resolusi target Anda tidak pernah tahu seberapa besar hal itu akan keluar dengan tepat. Solusi setengah-setengah lainnya oleh Google.
2
punya cara untuk menambahkan atribut scaleType?
ademar111190
57

Belum mencoba melakukan persis apa yang Anda inginkan, tetapi Anda dapat skala menggunakan ImageView android:scaleType="fitXY"
dan itu akan berukuran sesuai dengan ukuran apa pun yang Anda berikan ImageView.

Jadi Anda bisa membuat FrameLayout untuk tata letak Anda, letakkan ImageView di dalamnya, dan kemudian konten apa pun yang Anda butuhkan di FrameLayout juga dengan latar belakang transparan.

<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>
dweebo
sumber
1
Apakah mungkin untuk melakukan ini pada Permukaan. Jika tidak, Dapatkah saya menampilkan pratinjau (untuk aplikasi perekam) menggunakan FrameLayout?
Namratha
1
@dweebo: Sudahkah Anda mencoba ini? Sepertinya tidak berfungsi untuk saya.
speedplane
3
Turun karena menggunakan dua komponen UI dan bukan satu adalah ide yang buruk terutama untuk komponen sibuk seperti ListView. Kemungkinan besar Anda akan mendapatkan masalah saat menggulir. Solusi yang tepat: stackoverflow.com/a/9362168/145046
Aleks N.
Saya mencoba scaleType dari fitCenter dan centerCrop (bersama dengan beberapa variasi gambar / gambar untuk kepadatan yang berbeda) dan berhasil! Baik tipe skala masuk akal bagi saya - lebih merupakan preferensi pribadi berdasarkan gambar, saya kira. Perhatikan bahwa saya menggunakan gabungan alih-alih FrameLayout, dan tidak ada pengguliran dalam kasus khusus saya.
Joe D'Andrea
2
centerInside layak, tidak fitXY, karena fitXY tidak menjaga aspek rasio gambar
Malachiasz
35

Ada cara mudah untuk melakukan ini dari 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>

Satu-satunya downside adalah bahwa jika tidak ada cukup ruang, gambar Anda tidak akan sepenuhnya ditampilkan, tetapi akan terpotong, saya tidak bisa menemukan cara untuk melakukan ini secara langsung dari yang dapat digambar. Tetapi dari pengujian saya berhasil dengan cukup baik, dan itu tidak memotong banyak gambar. Anda bisa bermain lebih banyak dengan opsi gravitasi.

Cara lain adalah dengan hanya membuat tata letak, di mana Anda akan menggunakan ImageViewdan mengatur scaleTypeuntuk fitCenter.

Ionut Negru
sumber
2
Solusi ini pasti yang saya cari untuk waktu yang lama untuk menggantikan properti latar belakang tema saya dan menghindari skala gambar yang berbeda ketika tampilan memiliki Tab atau tidak. Terima kasih telah berbagi ini.
Bibu
1
Ini menunjukkan kesalahan:error: <item> must have a 'name' attribute.
Dmitry
thx @lonutNegru, +1 untuk menyelamatkan hari saya :)
Ravi Vaniya
18

Untuk mempertahankan rasio aspek yang harus Anda gunakan android:scaleType=fitCenteratau fitStartdll. Menggunakan fitXYtidak akan menjaga rasio aspek asli gambar!

Perhatikan ini hanya berfungsi untuk gambar dengan srcatribut, bukan untuk gambar latar belakang.

Anke
sumber
18

Gunakan gambar sebagai ukuran latar belakang untuk tata letak:

<?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>
Konstantin Konopko
sumber
Terima kasih! Juga android:scaleType="centerCrop"berfungsi.
CoolMind
5

Saat Anda mengatur Drawable of ImageView dengan menggunakan setBackgroundDrawablemetode ini, gambar akan selalu diskalakan. Parameter sebagai adjustViewBoundsatau berbeda ScaleTypeshanya akan diabaikan. Satu-satunya solusi untuk menjaga aspek rasio yang saya temukan, adalah mengubah ukuran ImageViewsetelah memuat gambar Anda. Berikut ini cuplikan kode yang saya gunakan:

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

Dalam cuplikan kode di atas adalah bmp objek Bitmap yang akan ditampilkan dan imageView adalah ImageViewobjek

Yang penting untuk diperhatikan adalah perubahan parameter tata letak . Ini perlu karena setMaxHeightdan setMaxWidthhanya akan membuat perbedaan jika lebar dan tinggi didefinisikan untuk membungkus konten, bukan untuk mengisi induknya. Isi induk di sisi lain adalah pengaturan yang diinginkan di awal, karena jika tidak containerWidthdan containerHeightkeduanya akan memiliki nilai sama dengan 0. Jadi, dalam file tata letak Anda, Anda akan memiliki sesuatu seperti ini untuk ImageView Anda:

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

Ini bukan solusi yang paling performan, tetapi sebagai seseorang menyarankan daripada latar belakang Anda dapat membuat FrameLayout atau RelativeLayout dan menggunakan ImageView sebagai latar belakang semu - elemen lain akan berada tepat di atasnya:

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

Masalah dengan ImageView adalah bahwa hanya scaleTypes yang tersedia adalah: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html )

dan untuk "skala gambar latar (menjaga rasio aspeknya)" dalam beberapa kasus, ketika Anda ingin gambar untuk mengisi seluruh layar (misalnya gambar latar belakang) dan rasio aspek layar berbeda dari gambar, skala yang diperlukanTipe dari TOP_CROP, karena:

CENTER_CROP menempatkan gambar berskala tengah alih-alih menyejajarkan tepi atas ke tepi atas tampilan gambar dan FIT_START sesuai dengan ketinggian layar dan tidak memenuhi lebar. Dan seperti yang diketahui pengguna, FIT_XY tidak mempertahankan rasio aspek.

Dengan senang hati seseorang telah memperluas ImageView untuk mendukung 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

Sekarang IMHO akan sempurna jika seseorang menulis Drawable kustom yang skala gambarnya seperti itu. Kemudian bisa digunakan sebagai parameter latar belakang.


Reflog menyarankan untuk mengatur skala drawable sebelum menggunakannya. Berikut ini instruksi cara melakukannya: Java (Android): Bagaimana skala gambar yang dapat digambar tanpa Bitmap? Meskipun memiliki kekurangan, drawable / bitmap yang ditingkatkan akan menggunakan lebih banyak RAM, sementara scaling on the fly yang digunakan oleh ImageView tidak memerlukan lebih banyak memori. Keuntungannya bisa lebih sedikit beban prosesor.

Malachiasz
sumber
Yang bagus! Melakukan apa yang saya inginkan: gambar latar belakang untuk mengisi seluruh layar dengan meningkatkan lebar secara proporsional dan memotong bagian bawah gambar.
CMash
4

Kode di bawah ini membuat bitmap sempurna dengan ukuran imageview yang sama. Dapatkan tinggi dan lebar gambar bitmap lalu hitung tinggi dan lebar baru dengan bantuan parameter imageview. Itu memberi Anda gambar yang diperlukan dengan rasio aspek terbaik.

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)
Naresh Sharma
sumber
2

Apa yang Dweebo usulkan berhasil. Tetapi menurut pendapat saya yang sederhana itu tidak perlu. Latar belakang dapat digambar dengan baik dengan sendirinya. Tampilan harus memiliki lebar dan tinggi tetap, seperti dalam contoh berikut:

 < 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>
Yar
sumber
2
Pemahaman saya adalah bahwa latar belakang akan diperluas untuk mengisi tampilan, tetapi tidak menyusut.
Edward Falk
1

Satu opsi untuk dicoba adalah dengan meletakkan gambar di drawable-nodpi folder dan mengatur latar belakang tata letak ke id sumber daya yang dapat digambar.

Ini pasti bekerja dengan scaling down, saya belum menguji dengan scaling up sekalipun.

Sababado
sumber
0

Kotlin:

Jika Anda perlu menggambar bitmap di Tampilan, diskalakan ke FIT.

Anda dapat melakukan perhitungan yang tepat untuk mengatur bm tinggi sama dengan wadah dan menyesuaikan lebar, dalam hal bm rasio lebar terhadap tinggi kurang dari rasio lebar kontainer terhadap tinggi, atau kebalikan dalam skenario yang berlawanan.

Gambar-gambar:

Contoh Gambar 1

Contoh Gambar 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())
}
danitinez
sumber
-4

Anda harus melakukan pra-skala gambar yang dapat digambar sebelum menggunakannya sebagai latar belakang

reflog
sumber
Apakah ini satu-satunya pilihan? Apakah tidak ada jenis wadah untuk menempatkan ImageView di mana dapat menentukan ukuran tampilan?
GrkEngineer
Saya juga mengalami masalah dengan SurfaceView
AZ_
3
Tidak ada yang seperti aspek fit, aspek fill, kiri atas, atas dll seperti di iOS? Itu menyebalkan ..
Maciej Swic
Seperti @Alexej katakan, jawaban yang tidak benar.
pelajar
-5

Anda dapat menggunakan salah satu dari berikut ini:

android: gravity = "fill_horizontal | clip_vertical"

Atau

android: gravity = "fill_vertical | clip_horizontal"

Eli Baynesay
sumber