17

Xin chào các lập trình viên tốt về tràn ngăn xếp! Tôi đã dành một tuần tốt với vấn đề này và bây giờ rất tuyệt vọng cho một giải pháp.Các đoạn lồng nhau được chuyển đổi không chính xác


Kịch bản

Tôi đang sử dụng android.app.Fragment không nên nhầm lẫn với các mảnh vỡ hỗ trợ.

Tôi đã mảnh 6 đứa trẻ có tên:

  • FragmentOne
  • FragmentTwo
  • FragmentThree
  • FragmentA
  • FragmentB
  • FragmentC

Tôi đã fragments 2 mẹ tên là:

  • FragmentNumeric
  • FragmentAlpha

Tôi đã 1 hoạt động mang tên:

  • MainActivity

Họ cư xử theo những điều sau đây:

  • mảnh trẻ em là những mảnh vỡ mà chỉ hiển thị một cái nhìn, họ không hiển thị cũng không được chứa các mảnh vỡ.
  • Các đoạn gốc điền vào toàn bộ khung nhìn của chúng với một đoạn con duy nhất. Họ có thể thay thế đoạn con với các mảnh con khác.
  • Hoạt động này sẽ chiếm hầu hết chế độ xem của nó với một đoạn gốc. Nó có thể thay thế nó bằng các đoạn cha khác. Vì vậy, tại bất kỳ thời điểm nào, màn hình chỉ hiển thị một đoạn con.

Như bạn đã có thể đoán,

FragmentNumeric thấy mảnh vỡ con FragmentOne, FragmentTwo, và FragmentThree.

FragmentAlpha hiển thị các đoạn con FragmentA, FragmentBFragmentC.


Vấn đề

Tôi đang cố gắng để chuyển/động mảnh phụ huynh và trẻ em. Các mảnh vỡ trẻ chuyển tiếp suôn sẻ và như mong đợi. Tuy nhiên khi tôi chuyển sang một đoạn phụ huynh mới, nó trông khủng khiếp. Đoạn con trông giống như nó chạy một quá trình chuyển đổi độc lập từ đoạn cha của nó.Và mảnh con trông giống như nó được loại bỏ khỏi đoạn cha mẹ. Một gif của nó có thể được xem ở đây https://imgur.com/kOAotvk. Lưu ý điều gì sẽ xảy ra khi tôi nhấp vào Hiển thị Alpha.

Câu hỏi gần nhất & câu trả lời Tôi có thể tìm thấy ở đây: Nested fragments disappear during transition animation tuy nhiên tất cả các câu trả lời đều là các lỗi không thỏa mãn.


Animator XML file

Tôi có những tác động làm phim hoạt hình sau (thời gian dài cho mục đích thử nghiệm):

fragment_enter.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
     android:duration="4000" 
     android:interpolator="@android:anim/linear_interpolator" 
     android:propertyName="xFraction" 
     android:valueFrom="1.0" 
     android:valueTo="0" /> 
</set> 

fragment_exit.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
     android:duration="4000" 
     android:interpolator="@android:anim/linear_interpolator" 
     android:propertyName="xFraction" 
     android:valueFrom="0" 
     android:valueTo="-1.0" /> 
</set> 

fragment_pop.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
     android:duration="4000" 
     android:interpolator="@android:anim/linear_interpolator" 
     android:propertyName="xFraction" 
     android:valueFrom="0" 
     android:valueTo="1.0" /> 
</set> 

fragment_push.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
     android:duration="4000" 
     android:interpolator="@android:anim/linear_interpolator" 
     android:propertyName="xFraction" 
     android:valueFrom="-1.0" 
     android:valueTo="0" /> 
</set> 

fragment_nothing.xml

<?xml version="1.0" encoding="utf-8"?> 
<set> 
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" 
     android:duration="4000" /> 
</set> 

MainActivity.kt

Những điều cần xem xét: Đoạn cha mẹ đầu tiên, FragmentNumeric, không có nhập các hiệu ứng để nó luôn sẵn sàng với các hoạt động và không có tác dụng thoát vì không có gì là thoát. Tôi cũng đang sử dụng FragmentTransaction#add với nó nơi như FragmentAlpha đang sử dụng FragmentTransaction#replace

class MainActivity : AppCompatActivity { 

    fun showFragmentNumeric(){ 
     this.fragmentManager.beginTransaction() 
       .setCustomAnimations(R.animator.fragment_nothing, 
            R.animator.fragment_nothing, 
            R.animator.fragment_push, 
            R.animator.fragment_pop) 
       .add(this.contentId, FragmentNumeric(), "FragmentNumeric") 
       .addToBackStack("FragmentNumeric") 
       .commit() 
} 

    fun showFragmentAlpha(){ 
     this.fragmentManager.beginTransaction() 
       .setCustomAnimations(R.animator.fragment_enter, 
            R.animator.fragment_exit, 
            R.animator.fragment_push, 
            R.animator.fragment_pop) 
       .replace(this.contentId, FragmentAlpha(), "FragmentAlpha") 
       .addToBackStack("FragmentAlpha") 
       .commit() 
    } 

    override fun onCreate(savedInstanceState: Bundle?) { 
     super.onCreate(savedInstanceState) 
     if (savedInstanceState == null) { 
      showFragmentNumeric() 
     } 
    } 
} 

FragmentNumeric

Liệu điều tương tự như hoạt động về một cách nhanh chóng cho thấy đoạn đứa con đầu lòng của mình.

class FragmentNumeric : Fragment { 

    fun showFragmentOne(){ 
      this.childFragmentManager.beginTransaction() 
        .setCustomAnimations(R.animator.fragment_nothing, 
             R.animator.fragment_nothing, 
             R.animator.fragment_push, 
             R.animator.fragment_pop) 
        .add(this.contentId, FragmentOne(), "FragmentOne") 
        .addToBackStack("FragmentOne") 
        .commit() 
    } 

    fun showFragmentTwo(){ 
     this.childFragmentManager.beginTransaction() 
       .setCustomAnimations(R.animator.fragment_enter, 
            R.animator.fragment_exit, 
            R.animator.fragment_push, 
            R.animator.fragment_pop) 
       .replace(this.contentId, FragmentTwo(), "FragmentTwo") 
       .addToBackStack("FragmentTwo") 
       .commit() 
    } 


    fun showFragmentThree(){ 
     this.childFragmentManager.beginTransaction() 
       .setCustomAnimations(R.animator.fragment_enter, 
            R.animator.fragment_exit, 
            R.animator.fragment_push, 
            R.animator.fragment_pop) 
       .replace(this.contentId, FragmentThree(), "FragmentThree") 
       .addToBackStack("FragmentThree") 
       .commit() 
    } 

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { 
     super.onViewCreated(view, savedInstanceState) 
     if (savedInstanceState == null) { 
      if (this.childFragmentManager.backStackEntryCount <= 1) { 
       showFragmentOne() 
      } 
     } 
    } 
} 

Những mảnh vỡ khác

FragmentAlpha đang theo cùng một khuôn mẫu như FragmentNumeric, thay thế Những mảnh vỡ Một, Hai và Ba với mảnh vỡ A, B và C tương ứng.

Đoạn con chỉ hiển thị chế độ xem XML sau và đặt trình nghe nhấp vào nút văn bản và nút động để gọi hàm từ đoạn hoặc hoạt động gốc.

view_child_example.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@color/background" 
    android:clickable="true" 
    android:focusable="true" 
    android:orientation="vertical"> 

    <TextView 
     android:id="@+id/view_child_example_header" 
     style="@style/Header" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" /> 


    <Button 
     android:id="@+id/view_child_example_button" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" /> 
</LinearLayout> 

Sử dụng dao găm và một số hợp đồng tôi có các mảnh vỡ con gọi lại để mảnh cha mẹ của họ và các hoạt động lưu trữ bằng cách làm một cái gì đó giống như dưới đây:

FragmentOne đặt người nghe vào nút nhấp chuột để làm:

(parentFragment as FragmentNumeric).showFragmentTwo() 

FragmentTwo đặt người nghe vào nút nhấp chuột để làm:

(parentFragment as FragmentNumeric).showFragmentThree() 

FragmentThree là khác nhau, nó sẽ thiết lập các listener nhấp chuột để làm:

(activity as MainActivity).showFragmentAlpha() 

Có ai có một giải pháp cho vấn đề này?


Cập nhật 1

Tôi đã thêm một dự án ví dụ như yêu cầu: https://github.com/zafrani/NestedFragmentTransitions

Một sự khác biệt trong đó và rằng từ một trong đoạn video ban đầu của tôi là mảnh mẹ không còn sử dụng một cái nhìn với thuộc tính xFraction. Vì vậy, có vẻ như hoạt ảnh nhập không có hiệu ứng chồng chéo đó nữa. Tuy nhiên nó vẫn loại bỏ các mảnh con từ cha mẹ và animate chúng cạnh nhau. Sau khi hoàn thành hoạt ảnh, Fragment Three sẽ thay thế bằng Fragment A ngay lập tức.

Cập nhật 2

Cả chế độ xem đoạn cha và con đều sử dụng thuộc tính xFraction. Điều quan trọng là để ngăn chặn các hình ảnh động của trẻ khi cha mẹ là hoạt hình.

+3

Câu hỏi đã được siêu rõ ràng, nhưng vì bạn dường như có một dự án sạch sẽ hiển thị vấn đề, nó sẽ được mát mẻ nếu bạn có thể tải nó ở đâu đó. – natario

+0

Vui lòng cập nhật Gifs, có vẻ như nó bị thiếu. UPD. Xin lỗi, xấu của tôi, tìm thấy hình ảnh. – GensaGames

+0

@natario Tôi đã thêm liên kết github. – zafrani

Trả lời

4

Tôi nghĩ rằng tôi đã tìm được cách để giải quyết vấn đề này bằng cách sử dụng Fragment # onCreateAnimator. Một gif của quá trình chuyển đổi có thể được xem tại đây: https://imgur.com/94AvrW4.

Tôi đã thực hiện PR để kiểm tra, cho đến nay nó hoạt động như tôi mong đợi và tồn tại các thay đổi cấu hình và hỗ trợ nút quay lại.Dưới đây là liên kết https://github.com/zafrani/NestedFragmentTransitions/pull/1/files#diff-c120dd82b93c862b01c2548bdcafcb20R25

Các BaseFragment cho cả các mảnh vỡ mẹ và trẻ em được làm điều này cho onCreateAnimator()

override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator { 
    if (isConfigChange) { 
     resetStates() 
     return nothingAnim() 
    } 

    if (parentFragment is ParentFragment) { 
     if ((parentFragment as BaseFragment).isPopping) { 
      return nothingAnim() 
     } 
    } 

    if (parentFragment != null && parentFragment.isRemoving) { 
     return nothingAnim() 
    } 

    if (enter) { 
     if (isPopping) { 
      resetStates() 
      return pushAnim() 
     } 
     if (isSuppressing) { 
      resetStates() 
      return nothingAnim() 
     } 
     return enterAnim() 
    } 

    if (isPopping) { 
     resetStates() 
     return popAnim() 
    } 

    if (isSuppressing) { 
     resetStates() 
     return nothingAnim() 
    } 

    return exitAnim() 
} 

Các phép toán đang được đặt trong tình huống khác nhau mà dễ nhìn thấy trong PR.

Các chức năng hoạt hình là:

private fun enterAnim(): Animator { 
     return AnimatorInflater.loadAnimator(activity, R.animator.fragment_enter) 
    } 

    private fun exitAnim(): Animator { 
     return AnimatorInflater.loadAnimator(activity, R.animator.fragment_exit) 
    } 

    private fun pushAnim(): Animator { 
     return AnimatorInflater.loadAnimator(activity, R.animator.fragment_push) 
    } 

    private fun popAnim(): Animator { 
     return AnimatorInflater.loadAnimator(activity, R.animator.fragment_pop) 
    } 

    private fun nothingAnim(): Animator { 
     return AnimatorInflater.loadAnimator(activity, R.animator.fragment_nothing) 
    } 

sẽ rời khỏi câu hỏi mở trong trường hợp ai đó tìm thấy một cách tốt hơn.

0

Bạn đang nhận được kết quả có dây, vì bạn đang sử dụng hoạt ảnh xuất cảnh cho Phân đoạn của mình. Về cơ bản, chúng tôi có các vấn đề như vậy với hoạt ảnh Fragment mỗi lần, và cuối cùng đã chuyển từ hoạt ảnh Fragment nguồn, để sử dụng riêng, mỗi khi chúng tôi thực hiện chuyển tiếp.

1) Để xác minh hành vi này, bạn chỉ có thể xóa hoạt ảnh thoát cho đoạn, và mọi thứ sẽ ổn. Trong hầu hết các trường hợp, nó nên là đủ, nguyên nhân xuất cảnh hoạt hình rất cụ thể và chỉ sử dụng cho quản lý mảnh duy nhất (không bạn trường hợp, với Childs)

getFragmentManager().beginTransaction() 
       .setCustomAnimations(R.animator.enter_anim_frag1, 
            0, 
            R.animator.enter_anim_frag2, 
            0) 
       .replace(xxx, Xxx1, Xxx2) 
       .addToBackStack(null) 
       .commit() 

2) Một lựa chọn khác, mà có thể phù hợp với, đó là suy nghĩ về cấu trúc ứng dụng và cố gắng tránh thay thế Phân đoạn bằng hoạt ảnh. Trong trường hợp bạn cần thay thế, không sử dụng hoạt ảnh và bạn có thể thêm bất kỳ hoạt ảnh nào (bao gồm cả nhập và thoát), nhưng chỉ với việc thêm.

Các vấn đề liên quan