Animasyonları kullanarak parçalar arasında gezinme

Fragment API, hareket efektlerini ve dönüşümlerini kullanmanın iki yolu sunar kullanarak parçaları görsel olarak birbirine bağlayabilirsiniz. Bunlardan biri Animasyon Çerçevesi, her ikisinin de Animation ve Animator. İlgili içeriği oluşturmak için kullanılan diğeri ise Geçiş Çerçevesi'dir. öğe geçişlerini görüntüleyebilirsiniz.

Parçalara giriş ve çıkışlarda ve parçaların sahnesinde parçalar arasında paylaşılan öğelerin geçişlerini kontrol eder.

  • Enter efekti, bir parçanın ekrana nasıl gireceğini belirler. Örneğin, parçayı kenardan içeri doğru kaydıran bir efekt oluşturabilirsiniz. dokunun.
  • Çıkış efekti, bir parçanın ekrandan nasıl çıkacağını belirler. Örneğin, sayfadan ayrılırken parçanın soluklaşmasını sağlayan bir efekt oluşturabilirsiniz bazı ipuçları vereceğim.
  • Paylaşılan öğe geçişi, bir görünümün ne şekilde paylaşılacağını belirler hareket etmesini sağlar. Örneğin, Yeşil Ofis web sitesinde A parçasındaki bir ImageView, B parçasına bir kez B parçasına geçiş yapar görünür hale gelir.

Animasyonları değiştirme

Öncelikle, giriş ve çıkış efektleriniz için çalıştırmasını sağlar. Animasyonları şu şekilde tanımlayabilirsiniz: animasyon kaynakları arasında geçiş yapın. Bu kaynaklar, parçaların nasıl döneceğini, genişletileceğini, solacağını ve ve hareket etmeniz gerekir. Örneğin, mevcut parçanın ve yeni parçanın ekranın sağ kenarından kayarak görünmesini sağlar Şekil 1'de gösterildiği gibidir.

Animasyonlara girme ve animasyonlardan çıkma. Mevcut parça şeffaf hale gelirken
            parça sağdan kaçar.
Şekil 1. Animasyonlara girme ve animasyonlardan çıkma. Geçerli parça kayboluyor.

Bu animasyonlar res/anim dizininde tanımlanabilir:

<!-- res/anim/fade_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromAlpha="1"
    android:toAlpha="0" />
<!-- res/anim/slide_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromXDelta="100%"
    android:toXDelta="0%" />

Çalıştırılan giriş ve çıkış efektleri için animasyonlar da belirtebilirsiniz. pop-up pencere açar. Bu durum, kullanıcı yukarı veya Geri düğmesi. Bunlara popEnter ve popExit animasyonları denir. Örneğin, Örneğin, kullanıcı bir önceki ekrana geri döndüğünde, mevcut parçanın ekranın sağ kenarından ve önceki parçasına sahip.

popEnter ve popExit animasyonları. Geçerli parça kaydırılıyor
            sağa doğru sola kaydırırken bir önceki parça görünür.
Şekil 2. popEnter ve popExit animasyon. Mevcut parça ekrandan dışarı kayıyor yavaşça sağa sola kaydırılır.

Bu animasyonlar aşağıdaki gibi tanımlanabilir:

<!-- res/anim/slide_out.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromXDelta="0%"
    android:toXDelta="100%" />
<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromAlpha="0"
    android:toAlpha="1" />

Animasyonlarınızı tanımladıktan sonra, bunları FragmentTransaction.setCustomAnimations() aşağıdaki örnekte gösterildiği gibi, animasyon kaynaklarınızı kaynak şu örneği inceleyin:

Kotlin

supportFragmentManager.commit {
    setCustomAnimations(
        R.anim.slide_in, // enter
        R.anim.fade_out, // exit
        R.anim.fade_in, // popEnter
        R.anim.slide_out // popExit
    )
    replace(R.id.fragment_container, fragment)
    addToBackStack(null)
}

Java

Fragment fragment = new FragmentB();
getSupportFragmentManager().beginTransaction()
    .setCustomAnimations(
        R.anim.slide_in,  // enter
        R.anim.fade_out,  // exit
        R.anim.fade_in,   // popEnter
        R.anim.slide_out  // popExit
    )
    .replace(R.id.fragment_container, fragment)
    .addToBackStack(null)
    .commit();

Geçişleri ayarlama

Giriş ve çıkış efektlerini tanımlamak için geçişleri de kullanabilirsiniz. Bu geçişler, XML kaynak dosyalarında tanımlanabilir. Örneğin, eğitime mevcut parçanın kaybolmasını ve yeni parçanın da ekranın sağ kenarında. Bu geçişler aşağıdaki gibi tanımlanabilir:

<!-- res/transition/fade.xml -->
<fade xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"/>
<!-- res/transition/slide_right.xml -->
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:slideEdge="right" />

Geçişlerinizi tanımladıktan sonra setEnterTransition() ve setExitTransition() kalan parçayı kullanarak şişirilmiş geçiş kaynaklarınızı aşağıdaki örnekte gösterildiği gibi kaynak kimliklerine göre:

Kotlin

class FragmentA : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val inflater = TransitionInflater.from(requireContext())
        exitTransition = inflater.inflateTransition(R.transition.fade)
    }
}

class FragmentB : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val inflater = TransitionInflater.from(requireContext())
        enterTransition = inflater.inflateTransition(R.transition.slide_right)
    }
}

Java

public class FragmentA extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TransitionInflater inflater = TransitionInflater.from(requireContext());
        setExitTransition(inflater.inflateTransition(R.transition.fade));
    }
}

public class FragmentB extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TransitionInflater inflater = TransitionInflater.from(requireContext());
        setEnterTransition(inflater.inflateTransition(R.transition.slide_right));
    }
}

Parça desteği AndroidX geçişleri. Parçalar aynı zamanda çerçeve geçişlerini hayata geçirmek için API düzeyleri 14'te desteklendiği için AndroidX geçişlerinin kullanılmasını öneririz ve daha yüksek ve eski sürümlerinde bulunmayan hata düzeltmelerini içeren pek çok farklı yolu vardır.

Paylaşılan öğe geçişlerini kullan

Geçiş Çerçevesi'nin kapsamında yer alan paylaşılan öğe geçişleri, karşılık gelen görünümlerin parça geçişi sırasında iki parçaya bölün. Örneğin, ekip arkadaşlarınızın B parçasına geçiş yapmak için A parçasında bir ImageView içinde görüntülenen resim (Şekil 3'te gösterildiği gibi) B görünür olduğunda.

Paylaşılan öğeli parça geçişi.
Şekil 3. Paylaşılan öğeli parça geçişi.

Genel olarak, paylaşılan öğelerle parça geçişinin nasıl yapılacağı aşağıda açıklanmıştır:

  1. Her paylaşılan öğe görünümüne benzersiz bir geçiş adı atayın.
  2. Paylaşılan öğe görünümlerini ve geçiş adlarını FragmentTransaction.
  3. Paylaşılan öğe geçiş animasyonu ayarlayın.

Öncelikle, her paylaşılan öğe görünümüne benzersiz bir geçiş adı atamanız gerekir bir parçadan diğerine eşlenmesine olanak tanır. Ayarlayın: her parça düzenindeki paylaşılan öğelerdeki geçiş adı ViewCompat.setTransitionName() . Örnek olarak, A ve B parçalarındaki bir ImageView için geçiş adı şu şekilde atanabilir:

Kotlin

class FragmentA : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        val itemImageView = view.findViewById<ImageView>(R.id.item_image)
        ViewCompat.setTransitionName(itemImageView, item_image)
    }
}

class FragmentB : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        val heroImageView = view.findViewById<ImageView>(R.id.hero_image)
        ViewCompat.setTransitionName(heroImageView, hero_image)
    }
}

Java

public class FragmentA extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        ImageView itemImageView = view.findViewById(R.id.item_image);
        ViewCompat.setTransitionName(itemImageView, item_image);
    }
}

public class FragmentB extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        ImageView heroImageView = view.findViewById(R.id.hero_image);
        ViewCompat.setTransitionName(heroImageView, hero_image);
    }
}

Paylaşılan öğelerinizi parça geçişine dahil etmek için FragmentTransaction, her bir paylaşılan öğenin görünümlerinin bir öğeden nasıl eşleştiğini bilmelidir bir sonraki parçaya atlar. Paylaşılan öğelerinizin her birini Telefon ederek FragmentTransaction FragmentTransaction.addSharedElement(), ve ilgili görünümün geçiş adını sonraki parçayı içerir:

Kotlin

val fragment = FragmentB()
supportFragmentManager.commit {
    setCustomAnimations(...)
    addSharedElement(itemImageView, hero_image)
    replace(R.id.fragment_container, fragment)
    addToBackStack(null)
}

Java

Fragment fragment = new FragmentB();
getSupportFragmentManager().beginTransaction()
    .setCustomAnimations(...)
    .addSharedElement(itemImageView, hero_image)
    .replace(R.id.fragment_container, fragment)
    .addToBackStack(null)
    .commit();

Paylaşılan öğelerinizin bir parçadan diğerine nasıl geçiş yapacağını belirtmek için olan parçada bir enter geçişi ayarlamanız gerekir emin olmanız gerekir. Telefonla arama Fragment.setSharedElementEnterTransition() öğesini, aşağıdaki örnekte gösterildiği gibi parçanın onCreate() yönteminde değiştirebilirsiniz:

Kotlin

class FragmentB : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedElementEnterTransition = TransitionInflater.from(requireContext())
             .inflateTransition(R.transition.shared_image)
    }
}

Java

public class FragmentB extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Transition transition = TransitionInflater.from(requireContext())
            .inflateTransition(R.transition.shared_image);
        setSharedElementEnterTransition(transition);
    }
}

shared_image geçişi aşağıdaki şekilde tanımlanır:

<!-- res/transition/shared_image.xml -->
<transitionSet>
    <changeImageTransform />
</transitionSet>

Tüm Transition alt sınıfları, paylaşılan öğe geçişleri olarak desteklenir. Eğer özel bir Transition oluşturmak istiyorsanız, Özel geçiş animasyonu oluşturun. Önceki örnekte kullanılan changeImageTransform, kullanılabilir durumdaki çeviriler hazırlamaya devam edeceğiz. Ek Transition bulabilirsiniz şunun API referansındaki alt sınıfları: Transition sınıfı.

Varsayılan olarak, paylaşılan öğe giriş geçişi aynı zamanda Paylaşılan öğeler için return geçişi. İade geçişi, parçası olduğunda, paylaşılan öğeler önceki parçaya geri işlem arka yığından çıkarılır. Farklı bir değer belirtmek istiyorsanız bunu kendiliğinden Fragment.setSharedElementReturnTransition() parçasının onCreate() yöntemidir.

Tahmine dayalı geri uyumluluk

Tahmine dayalı arka planı, hepsi olmasa da birçok çapraz parça animasyonda kullanabilirsiniz. Tahmine dayalı geri yaklaşımı uygularken aşağıdaki noktaları göz önünde bulundurun:

  • Transitions 1.5.0 veya sonraki sürümleri ve Fragments 1.7.0 ya da sonraki sürümleri içe aktarın.
  • Animator sınıfı, alt sınıflar ve AndroidX Transition kitaplığı desteklenir.
  • Animation sınıfı ve Transition çerçevesi kitaplığı desteklenmiyor.
  • Tahmine dayalı parça animasyonları yalnızca Android 14 veya sonraki sürümleri çalıştıran cihazlarda çalışır. daha yüksek.
  • setCustomAnimations, setEnterTransition, setExitTransition, setReenterTransition, setReturnTransition, setSharedElementEnterTransition ve setSharedElementReturnTransition tahmine dayalı geri dönüşüyle desteklenir.

Daha fazla bilgi edinmek için bkz. Tahmine dayalı geri animasyonlar için destek ekleyin.

Geçişleri erteleme

Bazı durumlarda, parça geçişinizi bir teslim etmek anlamına gelir. Örneğin, tüm görüntülemelerin elde edilmesini kalan parçanın ölçülmesi ve yerleştirilmesi, Android'in başlangıç ve bitiş durumlarını doğru şekilde yakalayabilir.

Ayrıca, geçişinizin belirli bir süre, ve gerekli veriler yüklendi. Örneğin, projeyle ilgili belirli bir süre boyunca resim paylaşılan öğeler için yüklendi. Aksi takdirde, geçiş Geçiş sırasında veya sonrasında bir resmin yüklenmesi biterse rahatsız edici olabilir.

Bir geçişi ertelemek için önce parçanın işlemi, parça durumu değişikliklerinin yeniden sıralanmasına olanak tanır. Yeniden sıralamaya izin vermek için parça durumu değişiklikleri, çağrı FragmentTransaction.setReorderingAllowed() aşağıdaki örnekte gösterildiği gibi:

Kotlin

val fragment = FragmentB()
supportFragmentManager.commit {
    setReorderingAllowed(true)
    setCustomAnimation(...)
    addSharedElement(view, view.transitionName)
    replace(R.id.fragment_container, fragment)
    addToBackStack(null)
}

Java

Fragment fragment = new FragmentB();
getSupportFragmentManager().beginTransaction()
    .setReorderingAllowed(true)
    .setCustomAnimations(...)
    .addSharedElement(view, view.getTransitionName())
    .replace(R.id.fragment_container, fragment)
    .addToBackStack(null)
    .commit();

Giriş geçişini ertelemek için şunu arayın: Fragment.postponeEnterTransition() :onViewCreated()

Kotlin

class FragmentB : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        postponeEnterTransition()
    }
}

Java

public class FragmentB extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        postponeEnterTransition();
    }
}

Verileri yükledikten sonra geçişi başlatmaya hazır olduğunuzda Fragment.startPostponedEnterTransition(). Aşağıdaki örnekte Resmi yüklemek için kaydırma kitaplığı görüntüye kadar ilgili geçişi erteleyerek paylaşılan bir ImageView içinde yükleme tamamlandı.

Kotlin

class FragmentB : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        Glide.with(this)
            .load(url)
            .listener(object : RequestListener<Drawable> {
                override fun onLoadFailed(...): Boolean {
                    startPostponedEnterTransition()
                    return false
                }

                override fun onResourceReady(...): Boolean {
                    startPostponedEnterTransition()
                    return false
                }
            })
            .into(headerImage)
    }
}

Java

public class FragmentB extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        Glide.with(this)
            .load(url)
            .listener(new RequestListener<Drawable>() {
                @Override
                public boolean onLoadFailed(...) {
                    startPostponedEnterTransition();
                    return false;
                }

                @Override
                public boolean onResourceReady(...) {
                    startPostponedEnterTransition();
                    return false;
                }
            })
            .into(headerImage)
    }
}

Bir kullanıcının yavaş internet bağlantısı gibi durumlarla uğraşırken, ertelenmiş geçişin belirli bir süre sonra başlaması yüklenmesini beklemekten daha kolay olur. Bu tür durumlarda arama Fragment.postponeEnterTransition(long, TimeUnit) girilen parçanın onViewCreated() yönteminde, ve zaman birimidir. Ertelenen görev, belirtilen süre geçti.

RecyclerView ile paylaşılan öğe geçişlerini kullan

Ertelenen giriş geçişleri, giriş bölümündeki tüm görüntülemeler gerçekleşene kadar başlamamalıdır ölçülmesi ve yerleştirilmesi gerekir. RecyclerView, beklemeniz gerekiyor ve RecyclerView öğenin çizilmeye hazır olması için inceleyin. Aşağıda bununla ilgili bir örnek verilmiştir:

Kotlin

class FragmentA : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        postponeEnterTransition()

        // Wait for the data to load
        viewModel.data.observe(viewLifecycleOwner) {
            // Set the data on the RecyclerView adapter
            adapter.setData(it)
            // Start the transition once all views have been
            // measured and laid out
            (view.parent as? ViewGroup)?.doOnPreDraw {
                startPostponedEnterTransition()
            }
        }
    }
}

Java

public class FragmentA extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        postponeEnterTransition();

        final ViewGroup parentView = (ViewGroup) view.getParent();
        // Wait for the data to load
        viewModel.getData()
            .observe(getViewLifecycleOwner(), new Observer<List<String>>() {
                @Override
                public void onChanged(List<String> list) {
                    // Set the data on the RecyclerView adapter
                    adapter.setData(it);
                    // Start the transition once all views have been
                    // measured and laid out
                    parentView.getViewTreeObserver()
                        .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw(){
                                parentView.getViewTreeObserver()
                                        .removeOnPreDrawListener(this);
                                startPostponedEnterTransition();
                                return true;
                            }
                    });
                }
        });
    }
}

Not: ViewTreeObserver.OnPreDrawListener parça görünümünün üst öğesinde ayarlanır. Bu şekilde tüm proje gerekliliklerinin parçasının görünümleri ölçülmüş ve yerleştirilmiştir ve dolayısıyla başlamadan önce çizilir.

Paylaşılan öğe geçişlerini tek bir öğede RecyclerView, geçiş adını Rastgele sayıda öğe paylaşıldığı için RecyclerView öğenin XML düzeni düzene sokmak. geçiş animasyonu doğru görünümü kullanır.

Her öğenin paylaşılan öğesine benzersiz bir geçiş adı verebilirsiniz. ViewHolder bağlı olduğunda atanacaklar. Örneğin, her öğe benzersiz bir kimlik içerir. Bu kimlik, geçiş adı olarak kullanılabilir aşağıdaki örnekte gösterilmektedir:

Kotlin

class ExampleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val image = itemView.findViewById<ImageView>(R.id.item_image)

    fun bind(id: String) {
        ViewCompat.setTransitionName(image, id)
        ...
    }
}

Java

public class ExampleViewHolder extends RecyclerView.ViewHolder {
    private final ImageView image;

    ExampleViewHolder(View itemView) {
        super(itemView);
        image = itemView.findViewById(R.id.item_image);
    }

    public void bind(String id) {
        ViewCompat.setTransitionName(image, id);
        ...
    }
}

Ek kaynaklar

Parça geçişleri hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklar.

Örnekler

Blog yayınları