Angka animasi di Android

UI yang cantik dan menarik itu penting. Oleh karena itu, untuk Android, ada sejumlah besar perpustakaan untuk tampilan elemen desain yang indah. Seringkali dalam aplikasi Anda perlu menunjukkan bidang dengan nomor atau semacam penghitung. Misalnya, penghitung untuk jumlah item daftar yang dipilih atau jumlah pengeluaran selama sebulan. Tentu saja, tugas seperti itu dapat dengan mudah diselesaikan dengan bantuan TextView biasa, tetapi Anda dapat menyelesaikannya dengan elegan dan menambahkan animasi mengubah nomor:


demo


Video demo tersedia di YouTube.


Artikel ini akan berbicara tentang bagaimana menerapkan semua ini.


Satu digit statis


Untuk masing-masing angka ada gambar vektor, misalnya, untuk 8 itu adalah res/drawable/viv_vd_pathmorph_digits_eight.xml :


 <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="@dimen/viv_digit_size" android:height="@dimen/viv_digit_size" android:viewportHeight="1" android:viewportWidth="1"> <group android:translateX="@dimen/viv_digit_translateX" android:translateY="@dimen/viv_digit_translateY"> <path android:name="iconPath" android:pathData="@string/viv_path_eight" android:strokeColor="@color/viv_digit_color_default" android:strokeWidth="@dimen/viv_digit_strokewidth"/> </group> </vector> 

Selain angka 0-9, gambar tanda minus ( viv_vd_pathmorph_digits_minus.xml ) dan gambar kosong ( viv_vd_pathmorph_digits_nth.xml ), yang akan menyimbolkan hilangnya angka nomor selama animasi, juga diperlukan.
File gambar XML hanya berbeda di android:pathData . Semua atribut lainnya diatur untuk kenyamanan melalui sumber daya yang terpisah dan sama untuk semua gambar vektor.
Gambar untuk angka 0-9 diambil di sini .


Animasi Transisi


Gambar vektor yang dijelaskan adalah gambar statis. Untuk animasi, Anda perlu menambahkan gambar vektor animasi ( <animated-vector> ). Misalnya, untuk menghidupkan angka 2, tambahkan file res/drawable/viv_avd_pathmorph_digits_2_to_5.xml ke angka 5:


 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:drawable="@drawable/viv_vd_pathmorph_digits_zero"> <target android:name="iconPath"> <aapt:attr name="android:animation"> <objectAnimator android:duration="@integer/viv_animation_duration" android:propertyName="pathData" android:valueFrom="@string/viv_path_two" android:valueTo="@string/viv_path_five" android:valueType="pathType"/> </aapt:attr> </target> </animated-vector> 

Di sini, untuk kenyamanan, kami mengatur durasi animasi melalui sumber yang terpisah. Secara total, kami memiliki 12 gambar statis (0 - 9 + "minus" + "void"), masing-masing dapat dianimasikan pada gambar lainnya. Ternyata untuk kelengkapan, 12 * 11 = 132 file animasi diperlukan. Mereka hanya akan android:valueFrom dalam atribut android:valueFrom dan android:valueTo , dan membuatnya secara manual bukanlah suatu pilihan. Karena itu, kami akan menulis generator sederhana:


Pembuat File Animasi
 import java.io.File import java.io.FileWriter fun main(args: Array<String>) { val names = arrayOf( "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "nth", "minus" ) fun getLetter(i: Int) = when (i) { in 0..9 -> i.toString() 10 -> "n" 11 -> "m" else -> null!! } val dirName = "viv_out" File(dirName).mkdir() for (from in 0..11) { for (to in 0..11) { if (from == to) continue FileWriter(File(dirName, "viv_avd_pathmorph_digits_${getLetter(from)}_to_${getLetter(to)}.xml")).use { it.write(""" <?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" android:drawable="@drawable/viv_vd_pathmorph_digits_zero"> <target android:name="iconPath"> <aapt:attr name="android:animation"> <objectAnimator android:duration="@integer/viv_animation_duration" android:propertyName="pathData" android:valueFrom="@string/viv_path_${names[from]}" android:valueTo="@string/viv_path_${names[to]}" android:valueType="pathType"/> </aapt:attr> </target> </animated-vector> """.trimIndent()) } } } } 

Semuanya bersama


Sekarang Anda perlu menghubungkan gambar vektor statis dan animasi transisi dalam satu file <animated-selector> , yang, seperti <selector> , menampilkan salah satu gambar tergantung pada kondisi saat ini. Sumber daya yang dapat res/drawable/viv_asl_pathmorph_digits.xml ini ( res/drawable/viv_asl_pathmorph_digits.xml ) berisi deklarasi status gambar dan transisi di antara mereka.


Negara ditetapkan menggunakan <item> dengan gambar dan atribut status (dalam kasus ini, app:viv_state_three ) yang mendefinisikan gambar ini. Setiap negara memiliki id , yang digunakan untuk menentukan animasi transisi yang diinginkan:


 <item android:id="@+id/three" android:drawable="@drawable/viv_vd_pathmorph_digits_three" app:viv_state_three="true" /> 

Atribut state diatur dalam file res/values/attrs.xml :


 <resources> <declare-styleable name="viv_DigitState"> <attr name="viv_state_zero" format="boolean" /> <attr name="viv_state_one" format="boolean" /> <attr name="viv_state_two" format="boolean" /> <attr name="viv_state_three" format="boolean" /> <attr name="viv_state_four" format="boolean" /> <attr name="viv_state_five" format="boolean" /> <attr name="viv_state_six" format="boolean" /> <attr name="viv_state_seven" format="boolean" /> <attr name="viv_state_eight" format="boolean" /> <attr name="viv_state_nine" format="boolean" /> <attr name="viv_state_nth" format="boolean" /> <attr name="viv_state_minus" format="boolean" /> </declare-styleable> </resources> 

Animasi transisi antar negara diatur oleh <transition> dengan indikasi <animated-vector> , yang melambangkan transisi, serta id awal dan akhir:


 <transition android:drawable="@drawable/viv_avd_pathmorph_digits_6_to_2" android:fromId="@id/six" android:toId="@id/two" /> 

Isi res/drawable/viv_asl_pathmorph_digits.xml hampir sama, dan generator juga digunakan untuk membuatnya. Sumber daya yang dapat digambar ini terdiri dari 12 negara bagian dan 132 transisi di antaranya.


Customview


Sekarang kita memiliki drawable yang memungkinkan kita untuk menampilkan satu digit dan menganimasikan perubahannya, kita perlu membuat VectorIntegerView yang akan berisi sejumlah beberapa digit dan mengendalikan animasi. RecyclerView dipilih sebagai dasar, karena jumlah digit dalam angka adalah nilai variabel, dan RecyclerView adalah cara terbaik di Android untuk menampilkan sejumlah variabel elemen (angka) dalam satu baris. Selain itu, RecyclerView memungkinkan Anda untuk mengontrol animasi item melalui ItemAnimator .


DigitAdapter dan DigitViewHolder


Anda harus mulai dengan membuat DigitViewHolder mengandung satu digit. View DigitViewHolder akan terdiri dari satu ImageView dengan android:src="@drawable/viv_asl_pathmorph_digits" . Untuk menampilkan digit yang diinginkan di ImageView , metode mImageView.setImageState(state, true); digunakan mImageView.setImageState(state, true); . Array state state dibentuk berdasarkan digit yang ditampilkan menggunakan atribut status viv_DigitState ditentukan di atas.


Tampilkan angka yang diinginkan di `ImageView`
 private static final int[] ATTRS = { R.attr.viv_state_zero, R.attr.viv_state_one, R.attr.viv_state_two, R.attr.viv_state_three, R.attr.viv_state_four, R.attr.viv_state_five, R.attr.viv_state_six, R.attr.viv_state_seven, R.attr.viv_state_eight, R.attr.viv_state_nine, R.attr.viv_state_nth, R.attr.viv_state_minus, }; void setDigit(@IntRange(from = 0, to = VectorIntegerView.MAX_DIGIT) int digit) { int[] state = new int[ATTRS.length]; for (int i = 0; i < ATTRS.length; i++) { if (i == digit) { state[i] = ATTRS[i]; } else { state[i] = -ATTRS[i]; } } mImageView.setImageState(state, true); } 

Adaptor DigitAdapter bertanggung jawab untuk membuat DigitViewHolder dan untuk menampilkan digit yang diinginkan di DigitViewHolder diinginkan.


Untuk animasi yang benar untuk mengubah satu nomor menjadi yang lain, DiffUtil digunakan. Dengan itu, pangkat puluhan dianimasikan ke pangkat puluhan, ratusan hingga ratusan, puluhan juta hingga puluhan juta, dan seterusnya. Simbol minus selalu tetap dengan sendirinya dan hanya bisa muncul atau menghilang, berubah menjadi gambar kosong ( viv_vd_pathmorph_digits_nth.xml ).


Untuk melakukan ini, DiffUtil.Callback dalam metode DiffUtil.Callback mengembalikan true hanya jika digit angka yang identik dibandingkan. Minus adalah kategori khusus, dan minus dari angka sebelumnya sama dengan minus dari nomor baru.


Metode areContentsTheSame membandingkan karakter pada posisi tertentu di angka sebelumnya dan yang baru. Implementasinya sendiri dapat dilihat di DigitAdapter .


DigitItemAnimator


Animasi perubahan angka, yaitu transformasi, tampilan, dan hilangnya angka, akan dikontrol oleh animator khusus untuk RecyclerView - DigitItemAnimator . Untuk menentukan durasi animasi, sumber integer sama digunakan seperti pada <animated-vector> dijelaskan di atas:


 private final int animationDuration; DigitItemAnimator(@NonNull Resources resources) { animationDuration = resources.getInteger(R.integer.viv_animation_duration); } @Override public long getMoveDuration() { return animationDuration; } @Override public long getAddDuration() { return animationDuration; } @Override public long getRemoveDuration() { return animationDuration; } @Override public long getChangeDuration() { return animationDuration; } 

Sebagian besar DigitItemAnimator menimpa metode aminasi. Animasi penampilan digit (metode animateAdd ) dilakukan sebagai transisi dari gambar kosong ke digit yang diinginkan atau tanda minus. Animasi penghilangan (metode animateRemove ) dilakukan sebagai transisi dari digit yang ditampilkan atau tanda minus ke gambar kosong.


Untuk melakukan animasi perubahan digit, informasi pada digit yang ditampilkan sebelumnya disimpan terlebih dahulu dengan recordPreLayoutInformation metode recordPreLayoutInformation . Kemudian, dalam metode animateChange , transisi dari digit yang ditampilkan sebelumnya ke yang baru dilakukan.


RecyclerView.ItemAnimator mensyaratkan bahwa metode animasi utama harus menggunakan metode yang melambangkan akhir animasi. Oleh karena itu, di setiap metode animateAdd , animateRemove dan animateChange , ada panggilan ke metode yang sesuai dengan penundaan yang sama dengan durasi animasi. Misalnya, metode animateAdd memanggil metode dispatchAddFinished dengan penundaan yang sama dengan @integer/viv_animation_duration :


 @Override public boolean animateAdd(final RecyclerView.ViewHolder holder) { final DigitAdapter.DigitViewHolder digitViewHolder = (DigitAdapter.DigitViewHolder) holder; int a = digitViewHolder.d; digitViewHolder.setDigit(VectorIntegerView.DIGIT_NTH); digitViewHolder.setDigit(a); holder.itemView.postDelayed(new Runnable() { @Override public void run() { dispatchAddFinished(holder); } }, animationDuration); return false; } 

VectorIntegerView


Sebelum membuat CustomView, Anda perlu mendefinisikan atribut xml-nya. Untuk melakukan ini, tambahkan <declare-styleable> ke file res/values/attrs.xml :


 <declare-styleable name="VectorIntegerView"> <attr name="viv_vector_integer" format="integer" /> <attr name="viv_digit_color" format="color" /> </declare-styleable> 

VectorIntegerView dibuat akan memiliki 2 atribut xml untuk penyesuaian:


  • viv_vector_integer angka yang ditampilkan saat membuat tampilan (0 secara default).
  • viv_digit_color warna angka (hitam secara default).

Parameter VectorIntegerView lainnya dapat diubah dengan menimpa sumber daya dalam aplikasi (seperti yang dilakukan dalam aplikasi demo ):


  • @integer/viv_animation_duration menentukan durasi animasi (400ms secara default).
  • @dimen/viv_digit_size menentukan ukuran satu digit ( 24dp secara default).
  • @dimen/viv_digit_translateX berlaku untuk semua gambar vektor digit untuk menyelaraskannya secara horizontal.
  • @dimen/viv_digit_translateY diterapkan ke semua gambar vektor digit untuk menyelaraskannya secara vertikal.
  • @dimen/viv_digit_strokewidth berlaku untuk semua gambar vektor digit.
  • @dimen/viv_digit_margin_horizontal berlaku untuk semua digit tampilan ( DigitViewHolder ) ( -3dp secara default). Ini diperlukan untuk membuat jarak antar angka lebih kecil, karena gambar vektor dari angka tersebut berbentuk persegi.

Sumber daya yang diganti akan diterapkan ke semua VectorIntegerView dalam aplikasi.


Semua parameter ini ditetapkan melalui sumber daya, karena mengubah ukuran VectorDrawable atau durasi animasi AnimatedVectorDrawable melalui kode tidak mungkin.


Menambahkan VectorIntegerView ke markup XML terlihat seperti ini:


 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.qwert2603.vector_integer_view.VectorIntegerView android:id="@+id/vectorIntegerView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" app:viv_digit_color="#ff8000" app:viv_vector_integer="14" /> </FrameLayout> 

Selanjutnya, Anda dapat mengubah nomor yang ditampilkan dalam kode dengan mengirimkan BigInteger :


 final VectorIntegerView vectorIntegerView = findViewById(R.id.vectorIntegerView); vectorIntegerView.setInteger( vectorIntegerView.getInteger().add(BigInteger.ONE), /* animated = */ true ); 

Demi kenyamanan, ada metode untuk mentransmisikan sejumlah tipe long :


 vectorIntegerView.setInteger(1918L, false); 

Jika false dilewatkan sebagai animated , maka metode notifyDataSetChanged akan dipanggil untuk notifyDataSetChanged , dan nomor baru akan ditampilkan tanpa animasi.


Saat Anda membuat kembali VectorIntegerView ditampilkan disimpan menggunakan metode onSaveInstanceState dan onRestoreInstanceState .


Kode sumber


Kode sumber tersedia di github (direktori perpustakaan). Ada juga aplikasi demo menggunakan VectorIntegerView (direktori aplikasi).


Ada juga apk demo ( minSdkVersion 21 ).

Source: https://habr.com/ru/post/id420919/


All Articles