Menganimasikan perubahan tata letak menggunakan transisi

Framework transisi Android memungkinkan Anda menganimasikan semua jenis gerakan di UI cukup dengan memberikan tata letak awal dan tata letak akhir. Anda dapat memilih jenis animasi apa yang Anda inginkan (seperti memudarkan tampilan masuk/keluar atau mengubah ukuran tampilan) dan framework transisi mencari cara menganimasikan dari tata letak awal hingga tata letak akhir.

Framework transisi mencakup fitur berikut:

  • Animasi tingkat grup: Menerapkan satu atau beberapa efek animasi ke semua tampilan dalam hierarki tampilan.
  • Animasi bawaan: Menggunakan animasi yang telah ditentukan sebelumnya untuk efek umum seperti memudar atau bergerak.
  • Dukungan file resource: Memuat hierarki tampilan dan animasi bawaan dari tata letak file resource.
  • Callback siklus proses: Menerima callback yang memberikan kontrol atas animasi dan proses perubahan hierarki.

Untuk kode contoh yang menganimasi perubahan tata letak, lihat BasicTransition.

Proses dasar untuk memberi animasi antara dua tata letak adalah sebagai berikut:

  1. Buat objek Scene untuk tata letak awal dan tata letak akhir. Namun, scene tata letak awal biasanya ditentukan secara otomatis dari tata letak saat ini.
  2. Buat objek Transition untuk menentukan jenis animasi yang Anda inginkan.
  3. Panggil TransitionManager.go() dan sistem akan menjalankan animasi untuk menukar tata letak.

Diagram pada gambar 1 mengilustrasikan hubungan antara tata letak, scene, transisi, dan animasi akhir.

Gambar 1. Ilustrasi dasar tentang cara framework transisi membuat sebuah animasi

Membuat scene

Scene menyimpan status hierarki tampilan, termasuk semua tampilan dan nilai propertinya. Framework transisi dapat menjalankan animasi antara scene awal dan akhir.

Anda dapat membuat scene dari file resource tata letak atau dari kumpulan tampilan dalam kode Anda. Namun, scene awal untuk transisi Anda biasanya ditentukan secara otomatis dari UI saat ini.

Scene juga dapat menentukan tindakannya sendiri yang berjalan saat Anda membuat perubahan scene. Misalnya, fitur ini berguna untuk membersihkan setelan tampilan setelah Anda bertransisi ke suatu scene.

Catatan: Framework ini dapat menganimasikan perubahan dalam hierarki tampilan tunggal tanpa menggunakan scene, seperti yang dijelaskan dalam Menerapkan transisi tanpa scene. Namun, memahami scene sangat penting agar dapat menangani transisi.

Membuat scene dari resource tata letak

Anda dapat membuat instance Scene langsung dari file resource tata letak. Gunakan teknik ini saat sebagian besar hierarki tampilan dalam file bersifat statis. Scene yang dihasilkan merepresentasikan status hierarki tampilan pada saat Anda membuat instance Scene. Jika mengubah hierarki tampilan, Anda harus membuat ulang scene. Framework membuat scene dari keseluruhan hierarki tampilan dalam file; Anda tidak dapat membuat scene dari bagian file tata letak.

Untuk membuat instance Scene dari file resource tata letak, ambil root scene dari tata letak sebagai instance ViewGroup, lalu panggil fungsi Scene.getSceneForLayout() dengan root scene dan ID resource dari file tata letak yang berisi hierarki tampilan untuk scene.

Menentukan tata letak untuk scene

Cuplikan kode di keseluruhan bagian ini menunjukkan cara membuat dua scene yang berbeda dengan elemen root scene yang sama. Cuplikan berikut juga menunjukkan bahwa Anda dapat memuat beberapa objek Scene tidak terkait tanpa mengimplikasikan bahwa semua objek tersebut saling berkaitan.

Contoh ini terdiri dari definisi tata letak berikut:

  • Tata letak utama aktivitas dengan label teks dan tata letak turunan.
  • Tata letak relatif untuk scene pertama dengan dua kolom teks.
  • Tata letak relatif untuk scene kedua dengan dua kolom teks yang sama dalam urutan yang berbeda.

Contoh ini dirancang agar semua animasi muncul dalam tata letak turunan dari tata letak utama untuk aktivitas. Label teks dalam tata letak utama selalu statis.

Tata letak utama untuk aktivitas ini didefinisikan sebagai berikut:

res/layout/activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/master_layout">
        <TextView
            android:id="@+id/title"
            ...
            android:text="Title"/>
        <FrameLayout
            android:id="@+id/scene_root">
            <include layout="@layout/a_scene" />
        </FrameLayout>
    </LinearLayout>
    

Definisi tata letak ini berisi kolom teks dan tata letak turunan untuk root scene. Tata letak untuk scene pertama disertakan dalam file tata letak utama. Hal ini memungkinkan aplikasi untuk menampilkannya sebagai bagian dari antarmuka pengguna awal serta memuatnya ke dalam scene, karena framework hanya dapat memuat seluruh file tata letak ke dalam sebuah scene.

Tata letak untuk scene pertama didefinisikan sebagai berikut:

res/layout/a_scene.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/scene_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:id="@+id/text_view1"
            android:text="Text Line 1" />
        <TextView
            android:id="@+id/text_view2"
            android:text="Text Line 2" />
    </RelativeLayout>
    

Tata letak untuk scene kedua berisi dua kolom teks yang sama (dengan ID yang sama) yang ditempatkan dalam urutan yang berbeda dan didefinisikan sebagai berikut:

res/layout/another_scene.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/scene_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <TextView
            android:id="@+id/text_view2"
            android:text="Text Line 2" />
        <TextView
            android:id="@+id/text_view1"
            android:text="Text Line 1" />
    </RelativeLayout>
    

Membuat scene dari tata letak

Setelah membuat definisi untuk dua tata letak relatif, Anda dapat memperoleh scene untuk setiap tata letak. Hal ini memungkinkan Anda bertransisi antar-dua konfigurasi UI nantinya. Untuk memperoleh scene, Anda perlu referensi ke root scene dan ID resource tata letak.

Cuplikan kode berikut menunjukkan cara mendapatkan referensi untuk root scene dan membuat dua objek Scene dari file tata letak:

Kotlin

    val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
    val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
    val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

    

Java

    Scene aScene;
    Scene anotherScene;

    // Create the scene root for the scenes in this app
    sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

    // Create the scenes
    aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
    anotherScene =
        Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

    

Di aplikasi, kini ada dua objek Scene berdasarkan hierarki tampilan. Kedua scene menggunakan root scene yang didefinisikan oleh elemen FrameLayout dalam res/layout/activity_main.xml.

Membuat scene dalam kode Anda

Anda juga dapat membuat instance Scene di kode dari objek ViewGroup. Gunakan teknik ini saat memodifikasi hierarki tampilan langsung dalam kode atau saat Anda membuatnya secara dinamis.

Untuk membuat scene dari hierarki tampilan dalam kode Anda, gunakan konstruktor Scene(sceneRoot, viewHierarchy). Memanggil konstruktor ini sama dengan memanggil fungsi Scene.getSceneForLayout() saat Anda telah meluaskan file tata letak.

Cuplikan kode berikut menunjukkan cara membuat instance Scene dari elemen root scene dan hierarki tampilan untuk scene dalam kode Anda:

Kotlin

    val sceneRoot = someLayoutElement as ViewGroup
    val viewHierarchy = someOtherLayoutElement as ViewGroup
    val scene: Scene = Scene(sceneRoot, mViewHierarchy)

    

Java

    Scene mScene;

    // Obtain the scene root element
    sceneRoot = (ViewGroup) someLayoutElement;

    // Obtain the view hierarchy to add as a child of
    // the scene root when this scene is entered
    viewHierarchy = (ViewGroup) someOtherLayoutElement;

    // Create a scene
    mScene = new Scene(sceneRoot, mViewHierarchy);

    

Membuat tindakan scene

Framework ini memungkinkan Anda mendefinisikan tindakan scene kustom yang dijalankan sistem saat memasuki atau keluar dari scene. Biasanya, Anda tidak perlu menentukan tindakan scene kustom, karena framework menganimasi perubahan antar-scene secara otomatis.

Tindakan scene berguna untuk menangani kasus berikut:

  • Menganimasikan tampilan yang tidak berada dalam hierarki yang sama. Anda dapat menganimasikan tampilan untuk scene awal dan akhir menggunakan tindakan scene keluar dan scene masuk.
  • Menganimasikan tampilan bahwasanya framework transisi tidak dapat dianimasikan secara otomatis, seperti objek ListView. Untuk informasi selengkapnya, lihat Batasan.

Untuk memberikan tindakan scene kustom, tentukan tindakan Anda sebagai objek Runnable dan teruskan ke fungsi Scene.setExitAction() atau Scene.setEnterAction(). Framework ini memanggil fungsi setExitAction() pada scene awal sebelum menjalankan animasi transisi dan fungsi setEnterAction() pada scene akhir setelah menjalankan animasi transisi.

Catatan: Jangan gunakan tindakan scene untuk meneruskan data di antara tampilan pada scene awal dan akhir. Untuk informasi selengkapnya, lihat Menentukan callback siklus proses transisi.

Menerapkan transisi

Framework transisi merepresentasikan gaya animasi antara scene dengan objek Transition. Anda dapat membuat instance Transition menggunakan beberapa subclass bawaan, seperti AutoTransition dan Fade, atau menentukan transisi Anda sendiri. Kemudian, Anda dapat menjalankan animasi antar-scene dengan meneruskan Scene akhir dan Transition ke TransitionManager.go().

Siklus proses transisi mirip dengan siklus proses aktivitas, dan merepresentasikan status transisi yang dipantau framework antara animasi awal dan akhir. Saat memasuki status siklus proses yang penting, framework memanggil fungsi callback yang dapat Anda terapkan untuk melakukan penyesuaian pada antarmuka pengguna pada fase transisi yang berbeda.

Membuat transisi

Pada bagian sebelumnya, Anda telah mempelajari cara membuat scene yang merepresentasikan status hierarki tampilan yang berbeda. Setelah menentukan scene awal dan scene akhir yang ingin diubah, Anda harus membuat objek Transition yang mendefinisikan animasi. Framework ini memungkinkan Anda menentukan transisi yang sudah ada dalam file resource dan mengupgrade-nya dalam kode Anda atau membuat instance transisi bawaan langsung dalam kode Anda.

Tabel 1. Jenis transisi bawaan.

Class Tag Atribut Efek
AutoTransition <autoTransition/> - Transisi default. Memudarkan, memindahkan dan mengubah ukuran, serta memperjelas tampilan, dalam urutan tersebut.
Fade <fade/> android:fadingMode="[fade_in |
fade_out |
fade_in_out]"
fade_in memperjelas tampilan
fade_out memudarkan tampilan
fade_in_out (default) melakukan fade_out diikuti oleh fade_in.
ChangeBounds <changeBounds/> - Memindahkan dan mengubah ukuran tampilan.

Membuat instance transisi dari file resource

Teknik ini memungkinkan Anda mengubah definisi transisi tanpa harus mengubah kode aktivitas. Teknik ini juga berguna untuk memisahkan definisi transisi yang kompleks dari kode aplikasi Anda, seperti yang ditunjukkan dalam Menentukan beberapa transisi.

Untuk menentukan transisi bawaan dalam file resource, ikuti langkah-langkah berikut:

  1. Tambahkan direktori res/transition/ ke project Anda.
  2. Buat file resource XML baru di dalam direktori ini.
  3. Tambahkan node XML untuk salah satu transisi bawaan.

Contohnya, file resource berikut menentukan transisi Fade:

res/transition/fade_transition.xml

    <fade xmlns:android="http://schemas.android.com/apk/res/android" />
    

Cuplikan kode berikut menunjukkan cara meluaskan instance Transition dalam aktivitas Anda dari file resource:

Kotlin

    var fadeTransition: Transition =
        TransitionInflater.from(this)
                          .inflateTransition(R.transition.fade_transition)

    

Java

    Transition fadeTransition =
            TransitionInflater.from(this).
            inflateTransition(R.transition.fade_transition);

    

Membuat instance transisi dalam kode Anda

Teknik ini berguna untuk membuat objek transisi secara dinamis jika Anda mengubah antarmuka pengguna dalam kode Anda, dan untuk membuat instance transisi bawaan sederhana dengan beberapa atau tanpa parameter sama sekali.

Untuk membuat instance transisi bawaan, gunakan salah satu konstruktor publik dalam subclass dari class Transition. Contohnya, cuplikan kode berikut membuat instance transisi Fade:

Kotlin

    var fadeTransition: Transition = Fade()

    

Java

    Transition fadeTransition = new Fade();

    

Menerapkan transisi

Biasanya Anda menerapkan transisi untuk beralih antar-hierarki tampilan yang berbeda sebagai respons terhadap peristiwa, seperti tindakan pengguna. Misalnya, ambil contoh dari aplikasi penelusuran: saat pengguna memasukkan istilah penelusuran dan mengklik tombol penelusuran, aplikasi akan mengubah scene yang merepresentasikan tata letak hasil saat menerapkan transisi yang memudarkan tombol penelusuran dan memperjelas hasil penelusuran.

Untuk membuat perubahan scene saat menerapkan transisi sebagai respons terhadap beberapa peristiwa dalam aktivitas Anda, panggil fungsi class TransitionManager.go() dengan scene akhir dan instance transisi yang akan digunakan untuk animasi, seperti yang ditunjukkan dalam cuplikan kode berikut:

Kotlin

    TransitionManager.go(endingScene, fadeTransition)

    

Java

    TransitionManager.go(endingScene, fadeTransition);

    

Framework mengubah hierarki tampilan di dalam root scene dengan hierarki tampilan dari scene akhir saat menjalankan animasi yang ditentukan oleh instance transisi. Scene awal adalah scene akhir dari transisi terakhir. Jika tidak ada transisi sebelumnya, scene awal akan ditentukan secara otomatis dari status antarmuka pengguna saat ini.

Jika Anda tidak menentukan instance transisi, pengelola transisi dapat menerapkan transisi otomatis yang melakukan transisi yang pantas untuk sebagian besar situasi. Untuk informasi selengkapnya, lihat referensi API untuk class TransitionManager.

Memilih tampilan target khusus

Framework ini menerapkan transisi ke semua tampilan dalam scene awal dan akhir secara default. Dalam beberapa kasus, Anda mungkin hanya ingin menerapkan animasi ke subset tampilan dalam sebuah scene. Misalnya, framework tidak mendukung perubahan animasi pada objek ListView, jadi Anda tidak perlu mencoba untuk menganimasikannya selama transisi. Framework ini memungkinkan Anda memilih tampilan tertentu yang ingin Anda animasikan.

Setiap tampilan yang dianimasikan oleh transisi disebut target. Anda hanya dapat memilih target yang merupakan bagian dari hierarki tampilan yang berkaitan dengan scene.

Untuk menghapus satu atau beberapa tampilan dari daftar target, panggil metode removeTarget() sebelum memulai transisi. Untuk menambahkan hanya tampilan yang Anda tentukan ke daftar target, panggil fungsi addTarget(). Untuk informasi selengkapnya, lihat referensi API untuk class Transition.

Menentukan beberapa transisi

Untuk mengoptimalkan dampak animasi, Anda harus mencocokkannya dengan jenis perubahan yang terjadi antara scene. Misalnya, jika Anda menghapus beberapa tampilan dan menambahkan tampilan lain di antara scene, animasi fade out/fade in memberikan indikasi yang jelas bahwa beberapa tampilan tidak lagi tersedia. Jika Anda memindahkan tampilan ke titik yang berbeda di layar, sebaiknya animasikan gerakan sehingga pengguna mengetahui lokasi baru tampilan.

Anda tidak harus memilih satu animasi saja, karena framework transisi memungkinkan Anda menggabungkan efek animasi dalam kumpulan transisi yang berisi grup transisi individual bawaan atau kustom.

Untuk menentukan kumpulan transisi dari koleksi transisi dalam XML, buat file resource di direktori res/transitions/ dan cantumkan transisi pada elemen transitionSet. Misalnya, cuplikan berikut menunjukkan cara menentukan kumpulan transisi yang memiliki perilaku yang sama seperti class AutoTransition:

    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
        android:transitionOrdering="sequential">
        <fade android:fadingMode="fade_out" />
        <changeBounds />
        <fade android:fadingMode="fade_in" />
    </transitionSet>
    

Untuk meluaskan transisi yang ditetapkan ke objek TransitionSet dalam kode Anda, panggil fungsi TransitionInflater.from() dalam aktivitas Anda. Class TransitionSet meluas dari class Transition, sehingga Anda dapat menggunakannya dengan pengelola transisi seperti instance Transition lainnya.

Menerapkan transisi tanpa scene

Mengubah hierarki tampilan bukanlah satu-satunya cara untuk mengubah antarmuka pengguna Anda. Anda juga dapat membuat perubahan dengan menambahkan, mengubah, dan menghapus tampilan turunan dalam hierarki saat ini. Misalnya, Anda dapat menerapkan interaksi penelusuran hanya dengan satu tata letak saja. Mulailah dengan tata letak yang menampilkan kolom entri penelusuran dan ikon penelusuran. Untuk mengubah antarmuka pengguna guna menampilkan hasil, hapus tombol penelusuran saat pengguna mengkliknya dengan memanggil fungsi ViewGroup.removeView(), lalu tambahkan hasil penelusuran dengan memanggil fungsi ViewGroup.addView().

Anda dapat menggunakan pendekatan ini jika alternatifnya memiliki dua hierarki yang hampir identik. Daripada membuat dan mengelola dua file tata letak yang terpisah karena adanya sedikit perbedaan pada antarmuka pengguna, Anda dapat memiliki satu file tata letak yang berisi hierarki tampilan yang dimodifikasi dalam kode.

Jika Anda membuat perubahan dalam hierarki tampilan saat ini dengan cara ini, Anda tidak perlu membuat scene. Sebagai gantinya, Anda dapat membuat dan menerapkan transisi antara dua status hierarki tampilan menggunakan transisi yang tertunda. Fitur framework transisi ini dimulai dengan status hierarki tampilan saat ini, mencatat perubahan yang Anda buat pada tampilannya, dan menerapkan transisi yang menganimasi perubahan saat sistem menggambar ulang antarmuka pengguna.

Untuk membuat transisi yang tertunda dalam hierarki tampilan tunggal, ikuti langkah-langkah berikut:

  1. Saat peristiwa yang memicu transisi terjadi, panggil fungsi TransitionManager.beginDelayedTransition() yang memberikan tampilan induk dari semua tampilan yang ingin diubah dan transisi yang akan digunakan. Framework menyimpan status saat ini dari tampilan turunan beserta nilai propertinya.
  2. Buat perubahan pada tampilan turunan seperti yang diminta oleh kasus penggunaan Anda. Framework mencatat perubahan yang Anda buat pada tampilan turunan dan propertinya.
  3. Saat sistem menggambar ulang antarmuka pengguna sesuai dengan perubahan Anda, framework akan menganimasikan perubahan antara status asli dan status baru.

Contoh berikut menunjukkan cara menganimasikan penambahan tampilan teks ke hierarki tampilan menggunakan transisi yang tertunda. Cuplikan pertama menunjukkan file definisi tata letak:

res/layout/activity_main.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/mainLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <EditText
            android:id="@+id/inputText"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        ...
    </RelativeLayout>
    

Cuplikan berikutnya menunjukkan kode yang menganimasi penambahan tampilan teks:

MainActivity

Kotlin

    setContentView(R.layout.activity_main)
    val labelText = TextView(this).apply {
        text = "Label"
        id = R.id.text
    }
    val rootView: ViewGroup = findViewById(R.id.mainLayout)
    val fade: Fade = Fade(Fade.IN)
    TransitionManager.beginDelayedTransition(rootView, mFade)
    rootView.addView(labelText)

    

Java

    private TextView labelText;
    private Fade mFade;
    private ViewGroup rootView;
    ...

    // Load the layout
    setContentView(R.layout.activity_main);
    ...

    // Create a new TextView and set some View properties
    labelText = new TextView(this);
    labelText.setText("Label");
    labelText.setId(R.id.text);

    // Get the root view and create a transition
    rootView = (ViewGroup) findViewById(R.id.mainLayout);
    mFade = new Fade(Fade.IN);

    // Start recording changes to the view hierarchy
    TransitionManager.beginDelayedTransition(rootView, mFade);

    // Add the new TextView to the view hierarchy
    rootView.addView(labelText);

    // When the system redraws the screen to show this update,
    // the framework will animate the addition as a fade in

    

Mendefinisikan callback siklus proses transisi

Siklus proses transisi mirip dengan siklus proses aktivitas. Siklus proses transisi merepresentasikan status transisi yang dipantau framework selama waktu pemanggilan ke fungsi TransitionManager.go() dan penyelesaian animasi. Saat memasuki status siklus proses yang penting, framework memanggil callback yang ditentukan oleh antarmuka TransitionListener.

Callback siklus proses transisi berguna, misalnya, untuk menyalin nilai properti tampilan dari hierarki tampilan awal ke hierarki tampilan akhir selama perubahan scene. Anda tidak bisa langsung menyalin nilai dari tampilan awalnya ke tampilan dalam hierarki tampilan akhir, karena hierarki tampilan akhir tidak meluas sampai transisi selesai. Sebagai gantinya, Anda harus menyimpan nilai dalam variabel lalu menyalinnya ke dalam hierarki tampilan akhir saat framework telah menyelesaikan transisi. Agar mendapatkan notifikasi saat transisi selesai, Anda dapat mengimplementasikan fungsi TransitionListener.onTransitionEnd() dalam aktivitas Anda.

Untuk informasi selengkapnya, lihat referensi API untuk class TransitionListener.

Batasan

Bagian ini mencantumkan beberapa batasan framework transisi umum:

  • Animasi yang diterapkan ke SurfaceView mungkin tidak muncul dengan benar. Instance SurfaceView diperbarui dari thread non-UI, sehingga pembaruan mungkin tidak sinkron dengan animasi tampilan lain.
  • Beberapa jenis transisi tertentu mungkin tidak menghasilkan efek animasi yang diinginkan saat diterapkan ke TextureView.
  • Class yang memperluas AdapterView, seperti ListView, mengelola tampilan turunannya dengan cara yang tidak sesuai dengan framework transisi. Jika Anda mencoba menganimasikan tampilan berdasarkan AdapterView, tampilan perangkat mungkin mengalami hang.
  • Jika Anda mencoba mengubah ukuran TextView dengan animasi, teks akan muncul ke lokasi baru sebelum ukuran objek diubah sepenuhnya. Untuk menghindari masalah ini, jangan menganimasikan perubahan ukuran tampilan yang berisi teks.