Mengapa penskalaan vector drawable saya tidak seperti yang diharapkan?

97

Saya mencoba menggunakan vector drawable di aplikasi Android saya. Dari http://developer.android.com/training/material/drawables.html (penekanan saya):

Di Android 5.0 (API Level 21) dan yang lebih baru, Anda bisa menentukan vector drawable, yang menskalakan tanpa kehilangan definisi.

Menggunakan drawable ini:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/colorPrimary" android:pathData="M14,20A2,2 0 0,1 12,22A2,2 0 0,1 10,20H14M12,2A1,1 0 0,1 13,3V4.08C15.84,4.56 18,7.03 18,10V16L21,19H3L6,16V10C6,7.03 8.16,4.56 11,4.08V3A1,1 0 0,1 12,2Z" />

dan ImageView ini:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    android:src="@drawable/icon_bell"/>

menghasilkan gambar buram ini saat mencoba menampilkan ikon pada 400dp (pada perangkat seluler beresolusi tinggi yang besar sekitar tahun 2015 yang menjalankan lollipop):

blurryBellIcon

Mengubah lebar dan tinggi dalam definisi drawable vektor menjadi 200dp secara signifikan meningkatkan situasi pada ukuran yang dirender 400dp. Namun, menyetel ini sebagai drawable untuk elemen TextView (yaitu ikon di sebelah kiri teks) sekarang membuat ikon besar.

Pertanyaan saya:

1) Mengapa ada spesifikasi lebar / tinggi pada drawable vektor? Saya pikir intinya adalah bahwa skala naik dan turun tanpa kerugian membuat lebar dan tinggi menjadi tidak berarti dalam definisinya?

2) Apakah mungkin untuk menggunakan satu vector drawable yang berfungsi sebagai drawable 24dp pada TextView tetapi dapat diskalakan dengan baik untuk menggunakan gambar yang jauh lebih besar juga? Misalnya, bagaimana cara menghindari pembuatan beberapa vektor yang dapat digambar dengan ukuran berbeda dan sebagai gantinya menggunakan salah satu skala yang sesuai dengan persyaratan yang saya berikan?

3) Bagaimana cara saya menggunakan atribut lebar / tinggi secara efektif dan apa perbedaannya dengan viewportWidth / Height?

Detil tambahan:

  • Perangkat menjalankan API 22
  • Menggunakan Android Studio v1.5.1 dengan Gradle versi 1.5.0
  • Manifes adalah kompilasi dan target level 23, min level 15. Saya juga mencoba memindahkan min level ke 21, tapi ini tidak ada bedanya.
  • Mendekompilasi APK (dengan set level minimal ke 21) menunjukkan satu sumber daya XML dalam folder drawable. Tidak ada gambar raster yang dihasilkan.
Chris Knight
sumber
Hanya untuk memperjelas. Anda menggunakan studio Android? Versi Android Studio berapa dan plugin Gradle versi berapa? Saat saya mengklik kanan folder drawable di Android Studio dan memilihnya New -> Vector Asset, XML gambar vektor akan jatuh ke folder drawable saya. Namun jika saya menggunakan apktool untuk membongkar APK yang dibangun, saya melihat bahwa file XML ada drawable-anydpi-v21dan menskalakan dengan benar pada perangkat API 21+. File raster ditempatkan di drawable-<mdpi/hdpi/etc>-v4folder dan tidak digunakan pada perangkat API 21+ (berdasarkan fakta bahwa skalanya dengan benar)
Cory Charlton
@Cory Charl Ingin tahu. Saya menggunakan Studio 1.5.1 dengan Gradle 1.5.0. Setelah mengubah min level ke 21, satu-satunya tempat gambar muncul di APK adalah drawablefoldernya. Lebar / tinggi dalam file xml drawable vektor adalah 24 dp. Menentukan ImageView dengan tinggi / lebar 400dp pasti akan menghasilkan gambar dengan skala yang buruk.
Chris Knight
Itulah yang saya harapkan. Satu-satunya alasan saya berakhir dengan drawable-anydpi-v21adalah karena my minSdkVersionkurang dari 21. Adakah perubahan perilaku jika Anda menyetel minSdkVersionke kurang dari 21? Bagaimana dengan memindahkan XML ke drawable-anydpi? Saya tidak berharap ada perubahan tetapi saya juga berharap gambar vektor Anda diskalakan dengan benar ...
Cory Charlton
Mengubah versi min kembali ke 15 menghasilkan hasil yang sama seperti Anda. Satu file xml drawable-anydpi-v21dengan berbagai gambar raster di mdi / hdpi / etc. folder. Tidak ada perubahan pada hasil akhir yang diberikan.
Chris Knight
Sangat aneh ... Saya memodifikasi salah satu aplikasi saya menggunakan gambar Anda dan berfungsi dengan baik (lihat jawaban saya yang diedit)
Cory Charlton

Jawaban:

101

Ada info baru tentang masalah ini di sini:

https://code.google.com/p/android/issues/detail?id=202019

Sepertinya penggunaan android:scaleType="fitXY"akan membuatnya berskala dengan benar di Lollipop.

Dari seorang insinyur Google:

Hai, Beri tahu saya jika scaleType = 'fitXY' bisa menjadi solusi untuk Anda, agar gambar terlihat tajam.

Marshmallow Vs Lollipop disebabkan oleh perawatan penskalaan khusus yang ditambahkan ke dalam marshmallow.

Juga, untuk komentar Anda: "Perilaku yang benar: Vector drawable harus diskalakan tanpa kehilangan kualitas. Jadi jika kita ingin menggunakan aset yang sama dalam 3 ukuran berbeda dalam aplikasi kita, kita tidak perlu menduplikasi vector_drawable.xml 3 kali dengan perbedaan ukuran hardcode. "

Walaupun saya sangat setuju hal ini yang seharusnya terjadi, pada kenyataannya platform Android memiliki masalah kinerja sehingga kita belum mencapai dunia yang ideal. Jadi sebenarnya disarankan untuk menggunakan 3 vector_drawable.xml berbeda untuk kinerja yang lebih baik jika Anda yakin ingin menggambar 3 ukuran berbeda di layar pada waktu yang bersamaan.

Detail teknisnya pada dasarnya kami menggunakan bitmap di bawah pengait untuk menyimpan cache rendering jalur yang kompleks, sehingga kami bisa mendapatkan kinerja penggambaran ulang terbaik, setara dengan menggambar ulang bitmap drawable.

Taman Taehyun
sumber
27
Oke Google, sungguh mengerikan bahwa kita harus menyalin-menempelkan drawable yang identik dengan viewports yang sama dan jalur yang hanya memiliki ukuran berbeda.
Miha_x64
Ini memperbaikinya pada perangkat yang menunjukkan logo saya pixelated, tetapi di unit lain, di mana semuanya baik-baik saja sebelumnya, sekarang rasio aspeknya salah. Saya tidak mengerti mengapa ini menjadi masalah, ini adalah vektor! Bukan piksel! : P
tplive
@Harish Gyanani, android: scaleType = "fitXY" memecahkan masalah, tetapi bagaimana dengan ikon dinamis dan bukan persegi?
Manuela
29

1) Mengapa ada spesifikasi lebar / tinggi pada drawable vektor? Saya pikir intinya adalah bahwa skala naik dan turun tanpa kerugian membuat lebar dan tinggi tidak berarti dalam definisinya?

Untuk versi SDK yang kurang dari 21 yang sistem build perlu menghasilkan gambar raster dan sebagai ukuran default jika Anda tidak menentukan lebar / tingginya.

2) Apakah mungkin menggunakan satu vector drawable yang berfungsi sebagai drawable 24dp pada TextView serta gambar lebar dekat layar yang besar?

Saya tidak percaya ini mungkin jika Anda juga perlu menargetkan SDK kurang dari 21.

3) Bagaimana cara saya menggunakan atribut lebar / tinggi secara efektif dan apa perbedaannya dengan viewportWidth / Height?

Dokumentasi: (sebenarnya tidak terlalu berguna sekarang setelah saya membacanya kembali ...)

android:width

Digunakan untuk menentukan lebar intrinsik drawable. Ini mendukung semua unit dimensi, biasanya ditentukan dengan dp.

android:height

Digunakan untuk menentukan tinggi intrinsik drawable. Ini mendukung semua unit dimensi, biasanya ditentukan dengan dp.

android:viewportWidth

Digunakan untuk menentukan lebar ruang viewport. Area pandang pada dasarnya adalah kanvas virtual tempat jalur digambar.

android:viewportHeight

Digunakan untuk menentukan ketinggian ruang viewport. Area pandang pada dasarnya adalah kanvas virtual tempat jalur digambar.

Dokumentasi lainnya:

Android 4.4 (API level 20) dan yang lebih rendah tidak mendukung vector drawable. Jika level API minimum Anda disetel di salah satu level API ini, Vector Asset Studio juga mengarahkan Gradle untuk membuat gambar raster drawable vektor untuk kompatibilitas dengan versi sebelumnya. Anda dapat merujuk ke aset vektor seperti Drawabledalam kode Java atau @drawabledalam kode XML; saat aplikasi Anda berjalan, vektor atau gambar raster yang sesuai ditampilkan secara otomatis bergantung pada level API.


Sunting: Sesuatu yang aneh sedang terjadi. Berikut hasil saya di emulator SDK versi 23 (perangkat uji Lollipop + mati sekarang ...):

Lonceng yang bagus di 6.x

Dan di SDK emulator versi 21:

Lonceng jelek di 5.x

Cory Charlton
sumber
1
Terima kasih Cory, saya telah melihat dokumentasi itu dan juga merasa tidak terlalu berguna. Perangkat saya adalah Lollipop (v22), namun saya tidak bisa mendapatkan skala grafik dengan baik, di atas 24dp yang ditentukan dalam drawable vektor, terlepas dari apakah tinggi / berat ditentukan secara tepat dalam ImageView atau tidak. Saya telah menguji sebuah manifes dengan target dan mengkompilasi level 23 dan min level 21 namun itu tidak akan dengan mulus meningkatkan vector drawable saya tanpa saya mengubah lebar / tinggi pada drawable itu sendiri. Saya tidak benar-benar ingin membuat beberapa drawable yang satu-satunya perbedaan adalah tinggi / lebar!
Chris Knight
1
Terima kasih atas konfirmasinya dan sangat menghargai bantuan Anda. Seperti yang telah Anda tunjukkan, itu jelas harus bekerja seperti yang saya harapkan. Menggunakan kode persis Anda menghasilkan hasil yang sama seperti yang saya miliki sebelumnya. Membuat frustrasi!
Chris Knight
1
UPDATE: Jalankan kode saya terhadap emulator dengan API 22 dan saya masih mendapatkan hasil yang sama. Jalankan kode saya terhadap emulator dengan API 23 dan, tada !, itu berfungsi. Sepertinya upscaling hanya berfungsi di perangkat API 23+. Mencoba mengubah versi SDK target tetapi tidak ada bedanya.
Chris Knight
Apakah Anda menemukan solusi untuk masalah ini?
dazza5000
@ Corycharlton tidak apa-apa untuk menghapus width height viewport heightdan widthdari xml?
VINNUSAURUS
9

AppCompat 23.2 memperkenalkan vector drawable untuk semua perangkat yang menjalankan Android 2.1 dan yang lebih baru. Gambar diskalakan dengan benar, terlepas dari lebar / tinggi yang ditentukan dalam file XML drawable vektor. Sayangnya, implementasi ini tidak digunakan di API level 21 ke atas (mendukung implementasi asli).

Kabar baiknya adalah kita bisa memaksa AppCompat untuk menggunakan implementasinya pada API level 21 dan 22. Kabar buruknya adalah kita harus menggunakan refleksi untuk melakukan ini.

Pertama-tama, pastikan Anda menggunakan AppCompat versi terbaru, dan Anda telah mengaktifkan implementasi vektor baru:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.2.0'
}

Kemudian, panggil useCompatVectorIfNeeded();onCreate () Aplikasi Anda:

private void useCompatVectorIfNeeded() {
    int sdkInt = Build.VERSION.SDK_INT;
    if (sdkInt == 21 || sdkInt == 22) { //vector drawables scale correctly in API level 23
        try {
            AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
            Class<?> inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate");
            Class<?> vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate");

            Constructor<?> constructor = vdcInflateDelegateClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            Object vdcInflateDelegate = constructor.newInstance();

            Class<?> args[] = {String.class, inflateDelegateClass};
            Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args);
            addDelegate.setAccessible(true);
            addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Terakhir, pastikan Anda menggunakan app:srcCompatalih-alih android:srcmenyetel gambar ImageView Anda:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    app:srcCompat="@drawable/icon_bell"/>

Drawable vektor Anda sekarang harus diskalakan dengan benar!

pengguna3210008
sumber
Pendekatan yang bagus, tetapi, AppCompat 25.4 (dan mungkin sebelumnya) memberikan kesalahan lint "hanya dapat dipanggil dari grup pustaka yang sama". Alat pembangunan yang saya gunakan masih membiarkannya dibangun, tetapi saya tidak yakin apakah ada konsekuensi runtime. Akan menguji.
androidguy
hanya dapat dipanggil dari grup pustaka yang sama, hapus kesalahan ini dengan menambahkan ini: lintOptions {disable 'RestrictedApi'}
Usama Saeed US
6

1) Mengapa ada spesifikasi lebar / tinggi pada drawable vektor? Saya pikir intinya adalah bahwa skala naik dan turun tanpa kerugian membuat lebar dan tinggi tidak berarti dalam definisinya?

Ini hanyalah ukuran default dari vektor jika Anda tidak menentukannya dalam tampilan tata letak. (yaitu Anda menggunakan konten bungkus untuk tinggi dan lebar tampilan gambar Anda)

2) Apakah mungkin menggunakan satu vector drawable yang berfungsi sebagai drawable 24dp pada TextView serta gambar lebar dekat layar yang besar?

Ya, itu mungkin dan saya tidak punya masalah dengan mengubah ukuran selama perangkat yang berjalan menggunakan lollipop atau lebih tinggi. Dalam API sebelumnya, vektor diubah menjadi png untuk berbagai ukuran layar saat Anda membuat aplikasi.

3) Bagaimana cara saya menggunakan atribut lebar / tinggi secara efektif dan apa perbedaannya dengan viewportWidth / Height?

Ini mempengaruhi bagaimana ruang di sekitar vektor Anda digunakan. yaitu Anda dapat menggunakannya untuk mengubah "gravitasi" vektor di dalam viewport, membuat vektor memiliki margin default, meninggalkan bagian tertentu dari vektor di luar viewport, dll ... Biasanya, Anda hanya menyetelnya ke nilai yang sama ukuran.

emirua
sumber
Terima kasih Emiura. Saya tertarik pada bagaimana Anda menyelesaikan beberapa ukuran yang diberikan menggunakan drawable yang sama. Menggunakan drawable 24dp (untuk tujuan pengujian) saya telah mengubah ImageView saya menjadi memiliki lebar dan tinggi yang ditentukan 400dp dan berjalan di perangkat Lollipop saya (v22) namun masih dirender dengan buruk, seperti gambar raster yang ditingkatkan daripada gambar vektor. Juga mengubah manifes saya agar memiliki target dan dikompilasi terhadap v23 dan versi min 21. Tidak ada perbedaan.
Chris Knight
Dalam kasus ini, Anda hanya perlu menggunakan ukuran terbesar dalam drawable vektor, lalu menentukan ukuran 24 dp dalam tampilan teks Anda.
emirua
Saya melihat sekarang Anda mendapatkan gambar buram saat memperbesar dengan perbedaan yang sangat besar antara ukuran drawable vektor dan ukuran tata letak di Lollipop. Namun, ini masih jauh lebih rapi hanya dengan mengatur ukuran terbesar daripada memiliki banyak file untuk ukuran layar yang berbeda;).
emirua
4

Saya menyelesaikan masalah saya hanya dengan mengubah ukuran gambar vektor. Dari awal itu adalah sumber daya seperti:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="300"
    android:viewportWidth="300">

Dan saya telah mengubahnya menjadi 150dp (ukuran ImageView dalam tata letak dengan sumber daya vektor ini) seperti:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="150dp"
    android:height="150dp"
    android:viewportHeight="300"
    android:viewportWidth="300">

Ini bekerja untuk saya.

Djek-Grif
sumber
terima kasih, ini cukup untuk menyelesaikan masalah di tablet saya yang muncul pikselnya.
pengguna2342558
3

Saya mencoba cara-cara ini tetapi sayangnya, tidak ada yang berhasil. Aku mencoba fitXYdi ImageView, saya mencoba menggunakan app:srcCompattapi bahkan tidak bisa menggunakannya. tapi saya menemukan cara trik:

atur Drawable menjadi backgroundmilik Anda ImageView.

Tetapi inti dari cara ini adalah dimensi Tampilan. jika Anda ImageViewmemiliki dimensi yang salah, drawable akan mengalami Stretch. Anda harus mengontrolnya dalam versi pra-lollipop atau Gunakan beberapa Tampilan PercentRelativeLayout.

Semoga bermanfaat.

Mahdi Astanei
sumber
2

Pertama : impor svg ke drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Klik kanan pada folder drawable pilih New -> Vector Asset

masukkan deskripsi gambar di sini

2- Vector Asset Studio memberi Anda opsi untuk memilih ikon Material bawaan atau file svg lokal Anda sendiri. Anda dapat mengganti ukuran default jika diperlukan.

masukkan deskripsi gambar di sini

Kedua : jika Anda menginginkan dukungan dari android API 7+, Anda harus menggunakan Android Support Library menurut catatan (( https://stackoverflow.com/a/44713221/1140304 ))

1- tambahkan

vectorDrawables.useSupportLibrary = true

ke file build.gradle Anda.

2-Gunakan namespace di tata letak Anda yang memiliki imageView:

xmlns:app="http://schemas.android.com/apk/res-auto"

Ketiga : setel imageView dengan android: scaleType = "fitXY" dan gunakan app: srcCompat

 <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        app:srcCompat="@drawable/ic_menu" />

Keempat : jika Anda ingin mengatur svg dalam kode java Anda harus menambahkan baris di bawah ini pada methoed oncreate

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
Milad Ahmadi
sumber
Apakah solusi Anda berfungsi pada API 21 ke atas? user3210008 di atas menyebutkan bahwa implementasi tidak digunakan pada API 21 ke atas.
AJW
2

Bagi saya alasan yang menyebabkan vektor dikonversi menjadi png adalah android:fillType="evenodd"atribut dalam drawable vektor saya. Ketika saya menghapus semua kemunculan atribut ini di drawable saya (untuk itu nonzerojenis isian default yang diterapkan ke vektor), lain kali aplikasi dibangun semuanya baik-baik saja.

Mahozad
sumber