Uygulamanız kullanılırken yeni bilgiler ekranda görünür ve eski bilgiler kaldırılır. Ekranda gösterilen içeriği hemen değiştirmek rahatsız edici olabilir ve kullanıcılar aniden karşınıza çıkan yeni içeriği kaçırabilir. Animasyonlar değişiklikleri yavaşlatır ve hareketlerle kullanıcının gözünü çizerek güncellemelerin daha belirgin olmasını sağlar.
Bir görünümü göstermek veya gizlemek için kullanabileceğiniz üç genel animasyon vardır: animasyonları gösterme, animasyonlara çapraz geçiş yapma ve kart çevirme animasyonları.
Çapraz geçiş animasyonu oluşturma
Çözme olarak da bilinen çapraz geçiş animasyonu, bir View
veya ViewGroup
efekti kademeli olarak şeffaflaştırılırken bir diğerinde yavaş yavaş kaybolur. Bu animasyon, uygulamanızdaki içerikleri veya görünümleri değiştirmek istediğiniz durumlarda faydalıdır. Burada gösterilen çapraz geçiş animasyonu, Android 3.1 (API düzeyi 12) ve sonraki sürümlerde kullanılabilen ViewPropertyAnimator
biçimini kullanır.
Burada, bir ilerleme göstergesinden metin içeriğine geçişe bir örnek verilmiştir:
Görünümleri oluşturma
Çapraz geçiş yapmak istediğiniz iki görünümü oluşturun. Aşağıdaki örnekte bir ilerleme göstergesi ve kaydırılabilir metin görünümü bulunmaktadır:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:padding="16dp" />
</ScrollView>
<ProgressBar android:id="@+id/loading_spinner"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
Çapraz geçiş animasyonunu ayarlama
Çapraz geçiş animasyonunu ayarlamak için aşağıdakileri yapın:
- Geçiş yapmak istediğiniz görünümler için üye değişkenleri oluşturun. Daha sonra animasyon sırasında görünümleri değiştirirken bu referanslara ihtiyacınız olacaktır.
- Soluklaştırılan görünümün görünürlüğünü
GONE
olarak ayarlayın. Bu, görünümün düzen alanını kullanmasını engeller ve bunu düzen hesaplamalarına dahil etmez. Bu da, işlemeyi hızlandırır. - Bir üye değişkeninde
config_shortAnimTime
sistem özelliğini önbelleğe alın. Bu özellik, animasyon için standart bir "kısa" süre tanımlar. Bu süre, sık yapılan animasyonlar veya animasyonlar için idealdir.config_longAnimTime
veconfig_mediumAnimTime
seçenekleri de kullanılabilir.
Önceki kod snippet'indeki düzenin etkinlik içerik görünümü olarak kullanıldığı bir örneği burada bulabilirsiniz:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_crossfade) contentView = findViewById(R.id.content) loadingView = findViewById(R.id.loading_spinner) // Initially hide the content view. contentView.visibility = View.GONE // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime) } ... }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_crossfade); contentView = findViewById(R.id.content); loadingView = findViewById(R.id.loading_spinner); // Initially hide the content view. contentView.setVisibility(View.GONE); // Retrieve and cache the system's default "short" animation time. shortAnimationDuration = getResources().getInteger( android.R.integer.config_shortAnimTime); } ... }
Görüntüler arasında geçiş yapın
Görünümler düzgün bir şekilde oluşturulduğunda, aşağıdakileri yaparak görüntülerin geçişini artırın:
- Kaybolan görünüm için alfa değerini 0, görünürlüğü ise
GONE
olan ilk ayarındanVISIBLE
olarak ayarlayın. Bu, görünümü görünür ancak şeffaf hale getirir. - Kaybolan görünümün 0 ile 1 arasındaki alfa değerini canlandırın. Kaybolan görünüm için 1'den 0'a alfa değerine animasyon uygulayın.
Animator.AnimatorListener
içindeonAnimationEnd()
kullanarak azalan görünümün görünürlüğünüGONE
olarak ayarlayın. Alfa değeri 0 olsa bile, görünümün görünürlüğünüGONE
olarak ayarlamak, görünümün düzen alanını kullanmasını engeller ve düzen hesaplamalarına dahil edilmez. Bu da işlemeyi hızlandırır.
Aşağıdaki yöntemde, bunun nasıl yapılacağına dair bir örnek gösterilmektedir:
Kotlin
class CrossfadeActivity : Activity() { private lateinit var contentView: View private lateinit var loadingView: View private var shortAnimationDuration: Int = 0 ... private fun crossfade() { contentView.apply { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. alpha = 0f visibility = View.VISIBLE // Animate the content view to 100% opacity and clear any animation // listener set on the view. animate() .alpha(1f) .setDuration(shortAnimationDuration.toLong()) .setListener(null) } // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration.toLong()) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { loadingView.visibility = View.GONE } }) } }
Java
public class CrossfadeActivity extends Activity { private View contentView; private View loadingView; private int shortAnimationDuration; ... private void crossfade() { // Set the content view to 0% opacity but visible, so that it is // visible but fully transparent during the animation. contentView.setAlpha(0f); contentView.setVisibility(View.VISIBLE); // Animate the content view to 100% opacity and clear any animation // listener set on the view. contentView.animate() .alpha(1f) .setDuration(shortAnimationDuration) .setListener(null); // Animate the loading view to 0% opacity. After the animation ends, // set its visibility to GONE as an optimization step so it doesn't // participate in layout passes. loadingView.animate() .alpha(0f) .setDuration(shortAnimationDuration) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loadingView.setVisibility(View.GONE); } }); } }
Kart çevirme animasyonu oluşturma
Kart; kartın ters çevrilmesini emüle eden bir animasyon göstererek, içerik görünümleri arasında geçiş yapar. Burada gösterilen kart çevirme animasyonu, FragmentTransaction
kullanıyor.
Kart çevirme şu şekilde görünür:
Animatör nesnelerini oluşturma
Kart çevirme animasyonunu oluşturmak için dört animasyona ihtiyacınız vardır. İki animasyoncu, kartın ön tarafının animasyonları, sol tarafın animasyonları ve sol tarafın animasyonları için kullanılır. Diğer iki animatör ise kartın arka tarafının sağ tarafından girilip sağa doğrulandığı ve sağa, dışarıya doğru hareket ettiği durumlar içindir.
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 1. See startOffset. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" />
<!-- Halfway through the rotation, set the alpha to 0. See startOffset. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" />
</set>
Görünümleri oluşturma
Kartın her bir kenarı, istediğiniz herhangi bir içeriği (iki metin görünümü, iki resim veya geçiş yapabileceğiniz görünümlerin herhangi bir kombinasyonu gibi) barındırabilen ayrı bir düzendir. Daha sonra canlandıracağınız parçalarda iki düzeni kullanın. Aşağıdaki düzen, kartın bir tarafında metin oluşturur:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
android:padding="16dp"
android:gravity="bottom">
<TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" />
<TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" />
</LinearLayout>
Bir sonraki düzen, kartın diğer tarafını oluşturur ve bu tarafta bir ImageView
görüntülenir:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" />
Parçaları oluşturma
Kartın önü ve arkası için parça sınıfları oluşturun. Parça sınıflarınızda onCreateView()
yönteminden oluşturduğunuz düzenleri döndürün. Daha sonra, üst etkinlikte kartı göstermek istediğiniz bu parçanın örneklerini oluşturabilirsiniz.
Aşağıdaki örnekte, bunları kullanan üst etkinlik içindeki iç içe yerleştirilmiş parça sınıfları gösterilmektedir:
Kotlin
class CardFlipActivity : FragmentActivity() { ... /** * A fragment representing the front of the card. */ class CardFrontFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_front, container, false) } /** * A fragment representing the back of the card. */ class CardBackFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View = inflater.inflate(R.layout.fragment_card_back, container, false) } }
Java
public class CardFlipActivity extends FragmentActivity { ... /** * A fragment representing the front of the card. */ public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } } /** * A fragment representing the back of the card. */ public class CardBackFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } } }
Kart çevirmeyi canlandır
Parçaları bir üst etkinlik içinde görüntüleyin. Bunun için aktivitenizin
düzenini oluşturun. Aşağıdaki örnek, çalışma zamanında parçalar ekleyebileceğiniz bir FrameLayout
oluşturur:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Etkinlik kodunda, içerik görünümünü, oluşturduğunuz düzen olarak ayarlayın. Etkinlik oluşturulduğunda bir varsayılan parçanın gösterilmesi iyi bir uygulamadır. Aşağıdaki örnek etkinlikte, kartın ön tarafının varsayılan olarak nasıl görüntüleneceği gösterilmektedir:
Kotlin
class CardFlipActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_activity_card_flip) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.container, CardFrontFragment()) .commit() } } ... }
Java
public class CardFlipActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_activity_card_flip); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() .add(R.id.container, new CardFrontFragment()) .commit(); } } ... }
Kartın ön tarafı görüntülendiğinde, çevirme animasyonlu kartın arkasını uygun bir zamanda gösterebilirsiniz. Kartın aşağıdaki işlemleri yapan diğer tarafını göstermek için bir yöntem oluşturun:
- Parça geçişleri için oluşturduğunuz özel animasyonları ayarlar.
- Görüntülenen parçayı yeni bir parçayla değiştirir ve bu etkinliği, oluşturduğunuz özel animasyonlarla canlandırır.
- Daha önce görüntülenen parçayı parça yığınına ekler. Böylece kullanıcı Geri düğmesine dokunduğunda kart tekrar ters döner.
Kotlin
class CardFlipActivity : FragmentActivity() { ... private fun flipCard() { if (showingBack) { supportFragmentManager.popBackStack() return } // Flip to the back. showingBack = true // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. supportFragmentManager.beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is tapped. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out ) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, CardBackFragment()) // Add this transaction to the back stack, letting users press // the Back button to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit() } }
Java
public class CardFlipActivity extends FragmentActivity { ... private void flipCard() { if (showingBack) { getSupportFragmentManager().popBackStack(); return; } // Flip to the back. showingBack = true; // Create and commit a new fragment transaction that adds the fragment // for the back of the card, uses custom animations, and is part of the // fragment manager's back stack. getSupportFragmentManager() .beginTransaction() // Replace the default fragment animations with animator // resources representing rotations when switching to the back // of the card, as well as animator resources representing // rotations when flipping back to the front, such as when the // system Back button is pressed. .setCustomAnimations( R.animator.card_flip_right_in, R.animator.card_flip_right_out, R.animator.card_flip_left_in, R.animator.card_flip_left_out) // Replace any fragments in the container view with a fragment // representing the next page, indicated by the just-incremented // currentPage variable. .replace(R.id.container, new CardBackFragment()) // Add this transaction to the back stack, letting users press // Back to get to the front of the card. .addToBackStack(null) // Commit the transaction. .commit(); } }
Dairesel gösterme animasyonu oluşturun
Gösterme animasyonları, bir kullanıcı arayüzü öğesi grubunu gösterdiğinizde veya gizlediğinizde kullanıcılara görsel devamlılık sağlar. ViewAnimationUtils.createCircularReveal()
yöntemi, bir görünümü göstermek veya gizlemek için kırpma dairesini canlandırmanıza olanak tanır. Bu animasyon, Android 5.0 (API düzeyi 21) ve sonraki sürümlerde kullanılabilen ViewAnimationUtils
sınıfında sağlanır.
Daha önce görünür olmayan bir görünümün nasıl gösterileceğini gösteren bir örneği aşağıda bulabilirsiniz:
Kotlin
// A previously invisible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the final radius for the clipping circle. val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animator for this view. The start radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius) // Make the view visible and start the animation. myView.visibility = View.VISIBLE anim.start() } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.visibility = View.INVISIBLE }
Java
// A previously invisible view. View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the final radius for the clipping circle. float finalRadius = (float) Math.hypot(cx, cy); // Create the animator for this view. The start radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius); // Make the view visible and start the animation. myView.setVisibility(View.VISIBLE); anim.start(); } else { // Set the view to invisible without a circular reveal animation below // Android 5.0. myView.setVisibility(View.INVISIBLE); }
ViewAnimationUtils.createCircularReveal()
animasyonu beş parametre alır.
İlk parametre, ekranda gizlemek veya göstermek istediğiniz görünümdür. Sonraki iki parametre, kırpma dairesinin merkezinin X ve Y koordinatlarıdır. Genellikle bu, görünümün merkezidir, ancak animasyonun seçtiği yerden başlaması için kullanıcının dokunduğu noktayı da kullanabilirsiniz. Dördüncü parametre, kırpma dairesinin başlangıç yarıçapıdır.
Önceki örnekte, başlangıç yarıçapı, görüntülenmekte olan görünümün daire tarafından gizlenmesi için sıfır olarak ayarlanmıştır. Son parametre, çemberin son yarıçapıdır. Bir görünümü görüntülerken, son yarıçapı görünümden daha geniş yapın. Böylece, animasyon tamamlanmadan önce görüntünün tamamen ortaya çıkmasını sağlayabilirsiniz.
Daha önce görünür olan bir görünümü gizlemek için aşağıdakileri yapın:
Kotlin
// A previously visible view. val myView: View = findViewById(R.id.my_view) // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. val cx = myView.width / 2 val cy = myView.height / 2 // Get the initial radius for the clipping circle. val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat() // Create the animation. The final radius is 0. val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f) // Make the view invisible when the animation is done. anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) myView.visibility = View.INVISIBLE } }) // Start the animation. anim.start() } else { // Set the view to visible without a circular reveal animation below // Android 5.0. myView.visibility = View.VISIBLE }
Java
// A previously visible view. final View myView = findViewById(R.id.my_view); // Check whether the runtime version is at least Android 5.0. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Get the center for the clipping circle. int cx = myView.getWidth() / 2; int cy = myView.getHeight() / 2; // Get the initial radius for the clipping circle. float initialRadius = (float) Math.hypot(cx, cy); // Create the animation. The final radius is 0. Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0f); // Make the view invisible when the animation is done. anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); myView.setVisibility(View.INVISIBLE); } }); // Start the animation. anim.start(); } else { // Set the view to visible without a circular reveal animation below Android // 5.0. myView.setVisibility(View.VISIBLE); }
Bu durumda, kırpma dairesinin başlangıç yarıçapı, görünümün animasyon başlamadan önce görünür olması için görünüm kadar büyük olacak şekilde ayarlanır. Son yarıçap, animasyon bittiğinde görünümün gizlenmesi için sıfıra ayarlanır.
Animasyon tamamlandığında görünümün INVISIBLE
olarak ayarlanabilmesi için animasyona bir işleyici ekleyin.
Ek kaynaklar
- Jetpack Compose ile animasyon.
- Jetpack Compose ile Hareketler.