ไปยังส่วนต่างๆ ระหว่างส่วนย่อยโดยใช้ภาพเคลื่อนไหว

Fragment API มีวิธีการใช้เอฟเฟกต์การเคลื่อนไหวและการเปลี่ยนรูปแบบ 2 วิธี เพื่อเชื่อมต่อส่วนย่อยด้วยภาพระหว่างการนำทาง หนึ่งในเครื่องมือเหล่านี้คือ เฟรมเวิร์กภาพเคลื่อนไหวซึ่งใช้ทั้ง Animation และ Animator อื่นๆ คือกรอบการเปลี่ยน ซึ่งประกอบด้วย การเปลี่ยนองค์ประกอบที่แชร์

คุณสามารถระบุเอฟเฟกต์ที่กำหนดเองเพื่อเข้าและออกจาก Fragment และสำหรับ การเปลี่ยนผ่านขององค์ประกอบที่แชร์ระหว่างส่วนย่อย

  • เอฟเฟกต์ enter กําหนดวิธีที่ส่วนย่อยเข้าสู่หน้าจอ ตัวอย่างเช่น คุณจะสร้างเอฟเฟกต์เพื่อเลื่อนส่วนย่อยจากขอบของ หน้าจอเมื่อคุณไปยังส่วนต่างๆ
  • เอฟเฟกต์การออกจะกำหนดวิธีที่ส่วนย่อยออกจากหน้าจอ ตัวอย่างเช่น คุณจะสร้างเอฟเฟกต์เพื่อทำให้ส่วน จางลงเมื่อออกจากหน้าไปได้ จากนั้น
  • การเปลี่ยนองค์ประกอบที่แชร์จะกำหนดวิธีการแชร์ข้อมูลพร็อพเพอร์ตี้ระหว่าง ส่วนย่อยสองส่วนสลับไปมาระหว่างส่วนต่างๆ ตัวอย่างเช่น รูปภาพที่แสดง ใน ImageView ในส่วนย่อย A เปลี่ยนเป็นส่วนย่อย B เมื่อ B มองเห็นได้

ตั้งค่าภาพเคลื่อนไหว

ขั้นแรก คุณต้องสร้างภาพเคลื่อนไหวสำหรับเอฟเฟ็กต์เข้าและออก เมื่อไปยังส่วนย่อยใหม่ คุณกำหนดภาพเคลื่อนไหวเป็น แหล่งข้อมูลเกี่ยวกับภาพเคลื่อนไหวเบื้องต้น แหล่งข้อมูลเหล่านี้ให้คุณกำหนดวิธีหมุน ยืด จางลง และเคลื่อนไหวระหว่างภาพเคลื่อนไหว เช่น คุณอาจต้องการส่วนปัจจุบัน เพื่อค่อยๆ จางลงและส่วนย่อยใหม่ให้เลื่อนเข้ามาจากขอบด้านขวาของหน้าจอ ดังที่แสดงในรูปที่ 1

วันที่ ป้อนและออกจากภาพเคลื่อนไหว ส่วนย่อยปัจจุบันจะค่อยๆ เลือนหายไป
            ส่วนย่อยถัดไปจะเลื่อนเข้ามาจากด้านขวา
รูปที่ 1 ป้อนและออกจากภาพเคลื่อนไหว ส่วนย่อยปัจจุบัน และค่อยๆ เลือนหายไปเมื่อส่วนย่อยถัดไปเลื่อนเข้ามาจากด้านขวา

คุณกำหนดภาพเคลื่อนไหวเหล่านี้ได้ในไดเรกทอรี res/anim ดังนี้

<!-- 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%" />

คุณสามารถระบุภาพเคลื่อนไหวสำหรับเอฟเฟกต์เข้าและออกที่เรียกใช้ได้ เมื่อเปิดแถบด้านหลังซึ่งอาจเกิดขึ้นเมื่อผู้ใช้แตะลูกศรขึ้นหรือ ปุ่มย้อนกลับ ภาพเคลื่อนไหวเหล่านี้เรียกว่าภาพเคลื่อนไหว popEnter และ popExit สำหรับ ตัวอย่างเช่น เมื่อผู้ใช้กลับไปที่หน้าจอก่อนหน้า คุณอาจต้องการ ส่วนย่อยปัจจุบันเพื่อเลื่อนออกจากขอบด้านขวาของหน้าจอและส่วนก่อนหน้า ที่จะค่อยๆ ปรากฏขึ้น

วันที่ ภาพเคลื่อนไหว popEnter และ popExit ส่วนย่อยปัจจุบันเลื่อนออก
            หน้าจอไปทางขวาขณะที่ส่วนย่อยก่อนหน้าค่อยๆ เลือนหายไป
รูปที่ 2 popEnter และ ภาพเคลื่อนไหว popExit ภาพ ส่วนย่อยปัจจุบันเลื่อนออกจากหน้าจอ ทางด้านขวาขณะที่ส่วนย่อยก่อนหน้าค่อยๆ ปรากฏขึ้น

ภาพเคลื่อนไหวเหล่านี้สามารถกำหนดได้ดังนี้

<!-- 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" />

เมื่อกำหนดภาพเคลื่อนไหวแล้ว ให้ใช้โดยการเรียก FragmentTransaction.setCustomAnimations() การส่งผ่านทรัพยากรภาพเคลื่อนไหวด้วยรหัสทรัพยากร ดังที่แสดงใน ตัวอย่างต่อไปนี้

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();

ตั้งค่าทรานซิชัน

คุณยังสามารถใช้การเปลี่ยนเพื่อกำหนดเอฟเฟกต์การเข้าและออก เหล่านี้ คุณสามารถกำหนดการเปลี่ยนในไฟล์ทรัพยากร XML ได้ ตัวอย่างเช่น คุณอาจ ต้องการให้ส่วนย่อยปัจจุบันจางลงและส่วนย่อยใหม่สามารถเลื่อนเข้ามาจาก ที่ขอบด้านขวาของหน้าจอ การเปลี่ยนเหล่านี้สามารถกำหนดได้ดังนี้

<!-- 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" />

เมื่อกําหนดการเปลี่ยนแปลงแล้ว ให้ใช้การเปลี่ยนแปลงด้วยการเรียกใช้ setEnterTransition() ในส่วนการป้อนแฟรกเมนต์และ setExitTransition() บนส่วนออก โดยส่งผ่านในทรัพยากรการเปลี่ยนที่ขยายตัวสูงเกินจริง ตามรหัสทรัพยากร ดังที่ปรากฏในตัวอย่างต่อไปนี้

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));
    }
}

การรองรับ Fragments การเปลี่ยน AndroidX ขณะที่ Fragment ยังรองรับ การเปลี่ยนเฟรมเวิร์ก แนะนำให้ใช้การเปลี่ยนของ AndroidX เนื่องจาก API ระดับ 14 รองรับการเปลี่ยน และสูงกว่า รวมทั้งมีการแก้ไขข้อบกพร่องที่ไม่มีในเวอร์ชันเก่าของ การเปลี่ยนผ่านเฟรมเวิร์ก

ใช้การเปลี่ยนองค์ประกอบที่แชร์

ส่วนหนึ่งของกรอบการเปลี่ยนแปลง การเปลี่ยนองค์ประกอบที่แชร์จะกำหนดวิธีการเคลื่อนที่ของมุมมองที่สอดคล้องกัน 2 ส่วนย่อยในระหว่างการเปลี่ยนส่วนย่อย ตัวอย่างเช่น คุณอาจต้องการ รูปภาพที่แสดงใน ImageView บนส่วนย่อย A เพื่อเปลี่ยนเป็นส่วนย่อย B เมื่อ B ปรากฏให้เห็น ดังที่แสดงในรูปที่ 3

วันที่ การเปลี่ยนส่วนย่อยที่มีองค์ประกอบที่แชร์
รูปที่ 3 การเปลี่ยนส่วนย่อยที่มีองค์ประกอบที่แชร์

วิธีเปลี่ยนส่วนย่อยด้วยองค์ประกอบที่แชร์ในระดับสูงมีดังนี้

  1. กำหนดชื่อการเปลี่ยนที่ไม่ซ้ำกันให้กับมุมมององค์ประกอบที่แชร์แต่ละรายการ
  2. เพิ่มมุมมององค์ประกอบที่แชร์และชื่อการเปลี่ยนไปยัง FragmentTransaction
  3. ตั้งค่าภาพเคลื่อนไหวการเปลี่ยนองค์ประกอบที่แชร์

ก่อนอื่น คุณต้องกำหนดชื่อการเปลี่ยนที่ไม่ซ้ำกันให้กับมุมมององค์ประกอบที่แชร์แต่ละรายการ เพื่อให้สามารถแมปมุมมองจากส่วนย่อยหนึ่งไปยังอีกส่วนหนึ่งได้ ตั้ง ชื่อการเปลี่ยนบนองค์ประกอบที่แชร์ในการจัดวาง Fragment แต่ละรายการโดยใช้ ViewCompat.setTransitionName() ซึ่งให้ความเข้ากันได้สำหรับ API ระดับ 14 ขึ้นไป ตัวอย่างเช่น ชื่อการเปลี่ยนสำหรับ ImageView ในส่วนย่อย A และ B สามารถกำหนดได้ดังนี้

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);
    }
}

หากต้องการรวมองค์ประกอบที่แชร์ไว้ในส่วนการเปลี่ยนส่วนย่อย FragmentTransaction ต้องทราบว่ามุมมองขององค์ประกอบที่แชร์แต่ละรายการจาก ส่วนย่อยถัดไป เพิ่มองค์ประกอบที่แชร์แต่ละรายการลงใน FragmentTransactionโดยการโทร FragmentTransaction.addSharedElement(), ผ่านมุมมองและชื่อการเปลี่ยนของมุมมองที่เกี่ยวข้องใน ส่วนย่อยถัดไป ดังที่ปรากฏในตัวอย่างต่อไปนี้

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();

หากต้องการระบุวิธีเปลี่ยนองค์ประกอบที่แชร์จากส่วนย่อยหนึ่งไปยังอีกส่วนหนึ่ง คุณต้องตั้งค่าการเปลี่ยน Enter ในส่วนย่อยที่ ที่มีการนำทางไป โทร Fragment.setSharedElementEnterTransition() ในเมธอด onCreate() ของส่วนย่อย ดังที่ปรากฏในตัวอย่างต่อไปนี้:

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 มีการกำหนดไว้ดังนี้

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

คลาสย่อยทั้งหมดของ Transition ได้รับการสนับสนุนเป็นการเปลี่ยนองค์ประกอบที่แชร์ ถ้า คุณต้องการสร้าง Transition ที่กำหนดเอง โปรดดู สร้างภาพเคลื่อนไหวการเปลี่ยนที่กำหนดเอง changeImageTransform ที่ใช้ในตัวอย่างก่อนหน้านี้เป็นหนึ่งใน คำแปลที่สร้างไว้ล่วงหน้า และคุณสามารถใช้ได้ คุณสามารถค้นหา Transition เพิ่มเติม คลาสย่อยในการอ้างอิง API สำหรับ Transition ชั้นเรียน

โดยค่าเริ่มต้น การเปลี่ยนสำหรับการป้อนองค์ประกอบที่แชร์ยังใช้เป็น return การเปลี่ยนสำหรับองค์ประกอบที่แชร์ การเปลี่ยนการคืนสินค้าจะกำหนดวิธี องค์ประกอบที่แชร์จะเปลี่ยนกลับไปยังส่วนย่อยก่อนหน้าเมื่อส่วนย่อย ธุรกรรมถูกดึงออกจากแบ็กสแต็ก ถ้าคุณต้องการระบุ การเปลี่ยนกลับ คุณสามารถทำได้โดยใช้ Fragment.setSharedElementReturnTransition() ในเมธอด onCreate() ของส่วนย่อย

ความเข้ากันได้ของการย้อนกลับที่คาดการณ์ไว้

คุณสามารถใช้การย้อนกลับแบบคาดเดากับภาพเคลื่อนไหวแบบข้ามส่วนย่อยได้ แต่ไม่ใช่ทั้งหมด เมื่อใช้การคาดการณ์ย้อนหลัง โปรดคำนึงถึงข้อควรพิจารณาต่อไปนี้

  • นําเข้า Transitions 1.5.0 ขึ้นไปและ Fragments 1.7.0 ขึ้นไป
  • คลาสและคลาสย่อย Animator และไลบรารีการเปลี่ยนของ AndroidX คือ ที่รองรับ
  • ไม่รองรับไลบรารี Animation คลาสและเฟรมเวิร์ก Transition
  • ภาพเคลื่อนไหวส่วนย่อยแบบคาดการณ์ใช้ได้เฉพาะกับอุปกรณ์ที่ใช้ Android 14 หรือ สูงขึ้น
  • setCustomAnimations setEnterTransition setExitTransition setReenterTransition setReturnTransition setSharedElementEnterTransition และ setSharedElementReturnTransition โดยใช้การย้อนกลับที่คาดการณ์ได้

ดูข้อมูลเพิ่มเติมได้ที่ เพิ่มการรองรับภาพเคลื่อนไหวย้อนกลับแบบคาดเดา

เลื่อนการเปลี่ยน

ในบางกรณี คุณอาจต้องเลื่อนการเปลี่ยน Fragment ออกไปก่อน ระยะเวลาสั้นๆ เช่น คุณอาจต้องรอจนกว่าจะดูทั้งหมด ในส่วนที่ป้อนนั้นได้รับการวัดและวาง เพื่อให้ Android สามารถบันทึกสถานะเริ่มต้นและสิ้นสุดได้อย่างถูกต้องสําหรับการเปลี่ยน

นอกจากนี้ การเปลี่ยนของคุณอาจต้องเลื่อนออกไปจนกว่า โหลดข้อมูลที่จำเป็นแล้ว ตัวอย่างเช่น คุณอาจต้องรอจนกว่า โหลดรูปภาพสำหรับองค์ประกอบที่ใช้ร่วมกันแล้ว มิฉะนั้น การเปลี่ยน ภาพสั่นเมื่อรูปภาพโหลดเสร็จสิ้นระหว่างหรือหลังการเปลี่ยน

หากต้องการเลื่อนการเปลี่ยนระบบ คุณต้องตรวจสอบก่อนว่าส่วนย่อย ธุรกรรมช่วยให้เรียงลำดับการเปลี่ยนแปลงสถานะส่วนย่อยใหม่ได้ หากต้องการอนุญาตให้เรียงลำดับใหม่ การเปลี่ยนแปลงสถานะส่วนย่อย, การเรียก FragmentTransaction.setReorderingAllowed() ดังที่ปรากฏในตัวอย่างต่อไปนี้

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();

หากต้องการเลื่อนการเปลี่ยนเข้าสู่การเข้าร่วม โปรดโทร 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();
    }
}

เมื่อคุณโหลดข้อมูลและพร้อมที่จะเริ่มต้นการเปลี่ยนแล้ว โทร Fragment.startPostponedEnterTransition() ตัวอย่างต่อไปนี้ใช้ คลังภาพแบบเลื่อนผ่านเพื่อโหลดรูปภาพ เป็น ImageView ที่แชร์ เลื่อนการเปลี่ยนแปลงที่เกี่ยวข้องออกไปจนกว่าจะมีรูปภาพ การโหลดเสร็จสมบูรณ์แล้ว

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)
    }
}

เมื่อต้องรับมือกับกรณีต่างๆ เช่น การเชื่อมต่ออินเทอร์เน็ตช้าของผู้ใช้ คุณอาจ การเปลี่ยนแปลงที่เลื่อนออกไปเพื่อเริ่มต้นหลังจากที่ผ่านไประยะเวลาหนึ่งแทน แทนที่จะรอข้อมูลทั้งหมดโหลด สำหรับสถานการณ์เหล่านี้ คุณสามารถ โทรแทน Fragment.postponeEnterTransition(long, TimeUnit) ในเมธอด onViewCreated() ของแฟรกเมนต์การป้อนผ่าน ในช่วงเวลา และหน่วยเวลา ซึ่งเลื่อนออกไปจะเริ่มต้นขึ้นโดยอัตโนมัติเมื่อ เวลาที่ระบุผ่านไปแล้ว

ใช้การเปลี่ยนองค์ประกอบแบบแชร์กับ RecyclerView

การเปลี่ยนการเข้าที่ถูกเลื่อนไม่ควรเริ่มต้นจนกว่าการดูทั้งหมดในการป้อน ได้รับการวัดและวางส่วนย่อยแล้ว เมื่อใช้ RecyclerView คุณต้องรอ เพื่อโหลดข้อมูล และเพื่อให้ RecyclerView รายการพร้อมสำหรับการวาด ก่อนจะเริ่มการเปลี่ยนแปลง ตัวอย่างเช่น

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;
                            }
                    });
                }
        });
    }
}

โปรดสังเกตว่า ViewTreeObserver.OnPreDrawListener จะตั้งค่าไว้บนระดับบนสุดของมุมมองแฟรกเมนต์ ทั้งนี้เพื่อให้มั่นใจว่า มีการวัดและวางมุมมองของส่วนย่อยแล้ว ดังนั้นจึงพร้อมที่จะ ก่อนจะเริ่มการเปลี่ยน Enter ที่เลื่อนออกไป

อีกประเด็นหนึ่งที่ต้องพิจารณาเมื่อใช้การเปลี่ยนองค์ประกอบแบบแชร์กับ RecyclerView คือคุณไม่สามารถตั้งชื่อการเปลี่ยนใน เลย์เอาต์ XML ของรายการ RecyclerView เนื่องจากมีการแชร์จำนวนรายการที่กำหนดเอง เลย์เอาต์นั้น ต้องกำหนดชื่อการเปลี่ยนที่ไม่ซ้ำกันเพื่อให้ฟิลด์ ภาพเคลื่อนไหวการเปลี่ยนใช้มุมมองที่ถูกต้อง

คุณสามารถตั้งชื่อการเปลี่ยนที่ไม่ซ้ำกันให้กับองค์ประกอบที่ใช้ร่วมกันของแต่ละรายการได้โดย มอบหมายเมื่อมีการผูก ViewHolder ตัวอย่างเช่น หากข้อมูลสำหรับ แต่ละรายการจะมีรหัสที่ไม่ซ้ำกัน ซึ่งใช้เป็นชื่อการเปลี่ยนแปลงได้ ที่แสดงในตัวอย่างต่อไปนี้

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);
        ...
    }
}

แหล่งข้อมูลเพิ่มเติม

หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนส่วนย่อย โปรดดูที่หัวข้อต่อไปนี้ ที่ไม่ซับซ้อน

ตัวอย่าง

บล็อกโพสต์