Các đoạn lồng nhau biến mất trong hoạt ảnh chuyển tiếp


100

Đây là kịch bản: Hoạt động chứa đoạn A, do đó sử dụng lần lượt getChildFragmentManager()thêm các mảnh vỡ A1A2trong của nó onCreatenhư sau:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

Cho đến nay, rất tốt, mọi thứ đang chạy như mong đợi.

Sau đó, chúng tôi chạy giao dịch sau trong Hoạt động:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()

Trong quá trình chuyển đổi, enterhoạt ảnh cho phân đoạn Bchạy chính xác nhưng các phân đoạn A1 và A2 biến mất hoàn toàn . Khi chúng tôi hoàn nguyên giao dịch bằng nút Quay lại, chúng sẽ khởi tạo đúng cách và hiển thị bình thường trong quá trình popEnterhoạt ảnh.

Trong thử nghiệm ngắn gọn của tôi, nó trở nên kỳ lạ hơn - nếu tôi đặt hoạt ảnh cho các đoạn con (xem bên dưới), exithoạt ảnh sẽ chạy không liên tục khi chúng tôi thêm đoạnB

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

Hiệu quả mà tôi muốn đạt được rất đơn giản - tôi muốn exit(hoặc nênpopExit ?) Hoạt ảnh trên mảnh A(anim2) để chạy, tạo hoạt ảnh cho toàn bộ vùng chứa, bao gồm cả các con lồng vào nhau của nó.

Có cách nào để đạt được điều đó không?

Chỉnh sửa : Vui lòng tìm một trường hợp thử nghiệm tại đây

Edit2 : Cảm ơn @StevenByle đã thúc đẩy tôi tiếp tục thử với các hoạt ảnh tĩnh. Rõ ràng bạn có thể đặt hoạt ảnh trên cơ sở mỗi lần chọn (không phải toàn cục cho toàn bộ giao dịch), có nghĩa là con cái có thể có một bộ hoạt ảnh tĩnh vô thời hạn, trong khi cha mẹ của chúng có thể có một hoạt ảnh khác và toàn bộ điều có thể được thực hiện trong một giao dịch . Xem thảo luận bên dưới và dự án trường hợp thử nghiệm được cập nhật .


Là gì R.id.fragmentHolderđối với A, A1, A2, vv với?
CommonsWare

pixelHolder là một id trong bố cục của hoạt động, phân mảnh {Một, Hai} Holder nằm trong bố cục của phân đoạn A. Cả ba đều khác biệt. Đoạn A ban đầu đã được thêm vào tronggmentHolder (tức là, đoạn B đang thay thế đoạn A).
Delyan

Tôi đã tạo một dự án mẫu tại đây: github.com/BurntBrunch/NestedFraariesAnimationsTest , cũng có một gói ứng dụng được bao gồm trong kho lưu trữ. Đây là một lỗi thực sự khó chịu và tôi đang tìm cách khắc phục nó (giả sử rằng nó không có trong mã của tôi).
Delyan

Tôi biết thêm một chút về vấn đề này bây giờ. Lý do các mảnh vỡ biến mất là do con cái xử lý các sự kiện vòng đời trước cha mẹ. Về bản chất, A1 và A2 bị loại bỏ trước A và vì chúng không được đặt hoạt ảnh nên chúng biến mất đột ngột. Một cách để giảm thiểu phần nào điều này là loại bỏ rõ ràng A1 và A2 trong giao dịch thay thế A. Bằng cách đó, chúng hoạt ảnh khi chúng thoát, tuy nhiên tốc độ hoạt ảnh của chúng là bình phương, vì vùng chứa mẹ cũng đang hoạt ảnh. Một giải pháp không tạo ra đồ tạo tác này sẽ được đánh giá cao.
Delyan

Thay đổi (thay thế đoạn khởi động) mà bạn đề cập trong câu hỏi là thay đổi thực sự mà bạn muốn làm hay nó chỉ là một ví dụ? Bạn sẽ chỉ gọi changeFragmentphương thức một lần?
Luksprog

Câu trả lời:


36

Để tránh người dùng nhìn thấy các phân đoạn lồng nhau biến mất khi phân đoạn mẹ bị xóa / thay thế trong một giao dịch, bạn có thể "mô phỏng" các phân đoạn đó vẫn còn tồn tại bằng cách cung cấp hình ảnh về chúng, khi chúng xuất hiện trên màn hình. Hình ảnh này sẽ được sử dụng làm nền cho vùng chứa các đoạn lồng nhau, vì vậy ngay cả khi các chế độ xem của đoạn lồng nhau biến mất, ảnh sẽ mô phỏng sự hiện diện của chúng. Ngoài ra, tôi không thấy việc mất tương tác với các chế độ xem của phân đoạn lồng nhau là một vấn đề vì tôi không nghĩ rằng bạn sẽ muốn người dùng hành động với chúng khi họ đang trong quá trình bị xóa (có thể là hành động của người dùng như tốt).

Tôi đã làm một ví dụ nhỏ về thiết lập hình nền (một cái gì đó cơ bản).


1
Tôi đã quyết định trao tiền thưởng cho bạn, vì đó là giải pháp cuối cùng tôi đã sử dụng. Cảm ơn bạn rất nhiều vì thời gian của bạn!
Delyan

16
Chà, thật bẩn thỉu. Độ dài chúng Android các nhà phát triển phải đi để chỉ dành riêng cho một số slickness
Dean hoang dã

1
Tôi có tab Viewpager From Second Tôi đang thay thế phân đoạn khác và khi nhấn lại vào nó, tôi cần hiển thị tab thứ hai của viewpager, nó đang mở nhưng lại hiển thị trang trống. Tôi đã thử những gì bạn đề xuất trong chủ đề trên nhưng nó vẫn giống nhau.
Harish

Vấn đề vẫn xảy ra khi người dùng quay lại như @Harish đã viết
Ewoks

1
Wow nghiêm túc? năm 2018 của nó và đây vẫn là một điều? :(
Archie G. Quiñones

69

Vì vậy, dường như có rất nhiều cách giải quyết khác nhau cho việc này, nhưng dựa trên câu trả lời của @ Jayd16, tôi nghĩ rằng tôi đã tìm thấy một giải pháp tổng hợp khá vững chắc mà vẫn cho phép các hoạt ảnh chuyển tiếp tùy chỉnh trên các đoạn con và không yêu cầu thực hiện một bộ nhớ cache bitmap của bố cục.

Có một BaseFragmentlớp mở rộng Fragmentvà làm cho tất cả các đoạn của bạn mở rộng lớp đó (không chỉ các đoạn con).

Trong BaseFragmentlớp đó , hãy thêm phần sau:

// Arbitrary value; set it to some reasonable default
private static final int DEFAULT_CHILD_ANIMATION_DURATION = 250;

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    final Fragment parent = getParentFragment();

    // Apply the workaround only if this is a child fragment, and the parent
    // is being removed.
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

private static long getNextAnimationDuration(Fragment fragment, long defValue) {
    try {
        // Attempt to get the resource ID of the next animation that
        // will be applied to the given fragment.
        Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");
        nextAnimField.setAccessible(true);
        int nextAnimResource = nextAnimField.getInt(fragment);
        Animation nextAnim = AnimationUtils.loadAnimation(fragment.getActivity(), nextAnimResource);

        // ...and if it can be loaded, return that animation's duration
        return (nextAnim == null) ? defValue : nextAnim.getDuration();
    } catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {
        Log.w(TAG, "Unable to load next animation from parent.", ex);
        return defValue;
    }
}

Thật không may, nó đòi hỏi sự phản ánh; tuy nhiên, vì cách giải quyết này dành cho thư viện hỗ trợ, bạn không gặp rủi ro khi triển khai cơ bản thay đổi trừ khi bạn cập nhật thư viện hỗ trợ của mình. Nếu bạn đang xây dựng thư viện hỗ trợ từ nguồn, bạn có thể thêm công cụ truy cập cho ID tài nguyên hoạt ảnh tiếp theo Fragment.javavà loại bỏ nhu cầu phản chiếu.

Giải pháp này loại bỏ nhu cầu "đoán" thời lượng hoạt ảnh của cha mẹ (để hoạt ảnh "không làm gì" sẽ có cùng thời lượng với hoạt ảnh thoát của cha) và cho phép bạn vẫn thực hiện hoạt ảnh tùy chỉnh trên các phân đoạn con (ví dụ: nếu bạn ' lại hoán đổi các đoạn con xung quanh bằng các hoạt ảnh khác nhau).


5
Đây là giải pháp yêu thích của tôi trong chủ đề. Không yêu cầu bitmap, không yêu cầu bất kỳ mã bổ sung nào trong phân đoạn con và không thực sự rò rỉ thông tin từ phân đoạn mẹ sang phân đoạn con.
jacobhyphenated

1
@EugenPechanec Bạn cần có nextAnim từ phân đoạn mẹ - không phải từ phân đoạn con. Đó là toàn bộ vấn đề.
Kevin Coppock

1
Thật không may này tiếp cận nguyên nhân rò rỉ bộ nhớ cho các mảnh vỡ con và mặc nhiên cho đoạn mẹ cũng trên các phiên bản Android dưới đây Lollipop :(
Cosmin

8
Cảm ơn bạn vì giải pháp rất hữu ích này, tuy nhiên nó yêu cầu một chút cập nhật để hoạt động với thư viện hỗ trợ hiện tại (27.0.2, không biết phiên bản nào đã phá vỡ mã này). mNextAnimbây giờ là bên trong một mAnimationInfođối tượng. Bạn có thể truy cập nó như thế này:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo"); animInfoField.setAccessible(true); Object animationInfo = animInfoField.get(fragment); Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
David Lericolais

5
@DavidLericolais, muốn thêm một dòng mã khác sau dòng mã của bạn. val nextAnimResource = nextAnimField.getInt(animationInfo);để thay thế dòngint nextAnimResource = nextAnimField.getInt(fragment);
tingyik90

32

Tôi đã có thể đưa ra một giải pháp khá sạch sẽ. IMO là giải pháp ít hack nhất, và trong khi về mặt kỹ thuật, đây là giải pháp "vẽ một bitmap", ít nhất nó cũng được trừu tượng hóa bởi mảnh lib.

Đảm bảo rằng con bạn đã ghi đè lên một lớp cha với điều này:

private static final Animation dummyAnimation = new AlphaAnimation(1,1);
static{
    dummyAnimation.setDuration(500);
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter && getParentFragment() != null){
        return dummyAnimation;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

Nếu chúng ta có hoạt ảnh thoát trên các khung hình trẻ em, chúng sẽ hoạt ảnh thay vì chớp mắt. Chúng ta có thể khai thác điều này bằng cách có một hoạt ảnh chỉ đơn giản là vẽ các đoạn con ở mức alpha đầy đủ trong một khoảng thời gian. Bằng cách này, chúng sẽ vẫn hiển thị trong phân đoạn mẹ khi nó hoạt hình, đưa ra hành vi mong muốn.

Vấn đề duy nhất tôi có thể nghĩ đến là theo dõi khoảng thời gian đó. Tôi có thể đặt nó thành một số lớn nhưng tôi sợ rằng điều đó có thể có vấn đề về hiệu suất nếu nó vẫn vẽ hoạt ảnh đó ở đâu đó.


Nó hữu ích, cảm ơn. Giá trị của thời lượng không quan trọng
iscariot

giải pháp sạch cho đến nay
Liran Cohen

16

Tôi đăng giải pháp của tôi cho rõ ràng. Giải pháp khá đơn giản. Nếu bạn đang cố gắng bắt chước hoạt ảnh giao dịch phân đoạn của cha mẹ, chỉ cần thêm hoạt ảnh tùy chỉnh vào giao dịch phân đoạn con với cùng thời lượng. Ồ và hãy chắc chắn rằng bạn đặt hoạt ảnh tùy chỉnh trước khi thêm ().

getChildFragmentManager().beginTransaction()
        .setCustomAnimations(R.anim.none, R.anim.none, R.anim.none, R.anim.none)
        .add(R.id.container, nestedFragment)
        .commit();

Xml cho R.anim.none (Thời gian hoạt ảnh vào / ra của bố mẹ tôi là 250ms)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="0" android:duration="250" />
</set>

Tôi đã thực hiện một cái gì đó rất tương tự nhưng sử dụng "hiển thị" thay vì "thêm" khi cập nhật đứa trẻ. Tôi cũng đã thêm "getChildFragmentManager (). ExecutePendingTransactions ()", mặc dù tôi không chắc điều đó có thực sự cần thiết hay không. Tuy nhiên, giải pháp này hoạt động tốt và không yêu cầu "cung cấp hình ảnh" của Fragment như một số đề xuất.
Brian Yencho

Điều này thật tuyệt. Tuy nhiên, tôi đã nhận được sự chậm trễ khi chuyển đổi các phân đoạn của con tôi. để tránh điều này, chỉ cần đặt tham số thứ 2 mà không có hoạt ảnh:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
ono

7

Tôi hiểu điều này có thể không thể giải quyết hoàn toàn vấn đề của bạn, nhưng có thể nó sẽ phù hợp với nhu cầu của người khác, bạn có thể thêm enter/ exitpopEnter/ popExithoạt ảnh cho con của bạn Fragmentmà không thực sự di chuyển / hoạt ảnh Fragment. Miễn là các hoạt ảnh có cùng thời lượng / độ lệch với Fragmenthoạt ảnh gốc của chúng , chúng sẽ có vẻ di chuyển / hoạt ảnh cùng với hoạt ảnh gốc.


1
Tôi đã trao tiền thưởng cho Luksprog vì giải pháp của anh ấy hoạt động trên toàn cầu. Tôi đã thử thủ thuật hình ảnh động tĩnh (thời lượng thực sự không quan trọng - khi phụ huynh biến mất, các lượt xem rõ ràng sẽ biến mất) nhưng chúng không hoạt động trong tất cả các trường hợp có thể xảy ra (xem nhận xét của tôi dưới câu hỏi). Ngoài ra, cách tiếp cận này làm rò rỉ tính trừu tượng, vì cha mẹ của một đoạn có trẻ em cần biết về thực tế đó và thực hiện thêm các bước để thiết lập hoạt ảnh của trẻ em. Trong mọi trường hợp, cảm ơn bạn rất nhiều vì thời gian của bạn!
Delyan

Đồng ý, đây là một giải pháp thay vì một giải pháp kín nước và có thể được coi là hơi mong manh. Nhưng nó sẽ hoạt động đối với những trường hợp đơn giản.
Steven Byle

4

bạn có thể làm điều này trong phân đoạn con.

@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    if (true) {//condition
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(getView(), "alpha", 1, 1);
        objectAnimator.setDuration(333);//time same with parent fragment's animation
        return objectAnimator;
    }
    return super.onCreateAnimator(transit, enter, nextAnim);
}

Cảm ơn bạn! Có thể không phải là tốt nhất nhưng có thể là giải pháp đơn giản nhất.
bug56

2

@@@@@@@@@@@@@@@@@@@@@@@@@@@@

CHỈNH SỬA: Tôi đã kết thúc giải pháp này vì có những vấn đề khác mà điều này có. Square gần đây đã ra mắt với 2 thư viện thay thế các mảnh. Tôi muốn nói rằng đây thực sự có thể là một giải pháp thay thế tốt hơn việc cố gắng hack các mảnh để làm điều gì đó mà google không muốn họ làm.

http://corner.squareup.com/2014/01/mortar-and-flow.html

@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Tôi nghĩ rằng tôi sẽ đưa ra giải pháp này để giúp những người gặp vấn đề này trong tương lai. Nếu bạn theo dõi cuộc trò chuyện trên áp phích gốc với những người khác và nhìn vào đoạn mã anh ấy đã đăng, bạn sẽ thấy người đăng gốc cuối cùng đi đến kết luận sử dụng hoạt ảnh không chọn trên các phân đoạn con trong khi tạo hoạt ảnh cho phân đoạn chính. Giải pháp này không lý tưởng vì nó buộc bạn phải theo dõi tất cả các đoạn con, điều này có thể phức tạp khi sử dụng ViewPager với FragmentPagerAdapter.

Vì tôi sử dụng Child Fragment khắp nơi nên tôi đã nghĩ ra giải pháp này hiệu quả và có tính mô-đun (vì vậy nó có thể dễ dàng loại bỏ) trong trường hợp họ sửa nó và hoạt ảnh no-op này không còn cần thiết nữa.

Có rất nhiều cách bạn có thể thực hiện điều này. Tôi đã chọn sử dụng một singleton và tôi gọi nó là ChildFragmentAnimationManager. Về cơ bản, nó sẽ theo dõi một đoạn con cho tôi dựa trên đoạn cha mẹ của nó và sẽ áp dụng hoạt ảnh no-op cho đứa trẻ khi được hỏi.

public class ChildFragmentAnimationManager {

private static ChildFragmentAnimationManager instance = null;

private Map<Fragment, List<Fragment>> fragmentMap;

private ChildFragmentAnimationManager() {
    fragmentMap = new HashMap<Fragment, List<Fragment>>();
}

public static ChildFragmentAnimationManager instance() {
    if (instance == null) {
        instance = new ChildFragmentAnimationManager();
    }
    return instance;
}

public FragmentTransaction animate(FragmentTransaction ft, Fragment parent) {
    List<Fragment> children = getChildren(parent);

    ft.setCustomAnimations(R.anim.no_anim, R.anim.no_anim, R.anim.no_anim, R.anim.no_anim);
    for (Fragment child : children) {
        ft.remove(child);
    }

    return ft;
}

public void putChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.add(child);
}

public void removeChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.remove(child);
}

private List<Fragment> getChildren(Fragment parent) {
    List<Fragment> children;

    if ( fragmentMap.containsKey(parent) ) {
        children = fragmentMap.get(parent);
    } else {
        children = new ArrayList<Fragment>(3);
        fragmentMap.put(parent, children);
    }

    return children;
}

}

Tiếp theo, bạn cần có một lớp mở rộng Fragment mà tất cả các Fragment của bạn đều mở rộng (ít nhất là các Fragment con của bạn). Tôi đã có lớp này và tôi gọi nó là BaseFragment. Khi chế độ xem phân mảnh được tạo, chúng tôi thêm nó vào ChildFragmentAnimationManager và xóa nó khi nó bị phá hủy. Bạn có thể làm điều này onAttach / Detach, hoặc các phương pháp so khớp khác trong trình tự. Logic của tôi khi chọn Create / Destroy View là vì nếu Fragment không có View, tôi không quan tâm đến việc tạo hoạt ảnh để tiếp tục được nhìn thấy. Cách tiếp cận này cũng sẽ hoạt động tốt hơn với các ViewPager sử dụng Fragment vì bạn sẽ không theo dõi từng Fragment đơn lẻ mà FragmentPagerAdapter đang nắm giữ mà chỉ có 3.

public abstract class BaseFragment extends Fragment {

@Override
public  View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().putChild(parent, this);
    }

    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onDestroyView() {
    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().removeChild(parent, this);
    }

    super.onDestroyView();
}

}

Giờ đây, tất cả các Phân đoạn của bạn được lưu trữ trong bộ nhớ bởi phân đoạn mẹ, bạn có thể gọi hoạt ảnh trên chúng như thế này và các phân đoạn con của bạn sẽ không biến mất.

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ChildFragmentAnimationManager.instance().animate(ft, ReaderFragment.this)
                    .setCustomAnimations(R.anim.up_in, R.anim.up_out, R.anim.down_in, R.anim.down_out)
                    .replace(R.id.container, f)
                    .addToBackStack(null)
                    .commit();

Ngoài ra, bạn có nó, đây là tệp no_anim.xml nằm trong thư mục res / anim của bạn:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator">
    <translate android:fromXDelta="0" android:toXDelta="0"
        android:duration="1000" />
</set>

Một lần nữa, tôi không nghĩ giải pháp này là hoàn hảo, nhưng nó tốt hơn nhiều so với mọi trường hợp bạn có Phân đoạn con, triển khai mã tùy chỉnh trong phân đoạn mẹ để theo dõi từng phân đoạn con. Tôi đã ở đó, và không có gì vui.


1

Tôi nghĩ rằng tôi đã tìm thấy một giải pháp tốt hơn cho vấn đề này hơn là chụp nhanh đoạn hiện tại thành một bitmap như Luksprog đã đề xuất.

Bí quyết là ẩn phân đoạn đang bị xóa hoặc tách ra và chỉ sau khi các hoạt ảnh đã hoàn thành, phân đoạn mới được xóa hoặc tách ra trong giao dịch phân đoạn của chính nó.

Hãy tưởng tượng chúng ta có FragmentAFragmentBcả hai đều có các phân đoạn phụ. Bây giờ khi bạn thường làm:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .remove(fragmentA)    <-------------------------------------------
  .addToBackStack(null)
  .commit()

Thay vào đó bạn làm

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .hide(fragmentA)    <---------------------------------------------
  .addToBackStack(null)
  .commit()

fragmentA.removeMe = true;

Bây giờ để triển khai Fragment:

public class BaseFragment extends Fragment {

    protected Boolean detachMe = false;
    protected Boolean removeMe = false;

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        if (nextAnim == 0) {
            if (!enter) {
                onExit();
            }

            return null;
        }

        Animation animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);
        assert animation != null;

        if (!enter) {
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    onExit();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }
            });
        }

        return animation;
    }

    private void onExit() {
        if (!detachMe && !removeMe) {
            return;
        }

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        if (detachMe) {
            fragmentTransaction.detach(this);
            detachMe = false;
        } else if (removeMe) {
            fragmentTransaction.remove(this);
            removeMe = false;
        }
        fragmentTransaction.commit();
    }
}

Không phải popBackStack sẽ gây ra Lỗi vì nó đang cố hiển thị một Fragment đã được tách ra?
Alexandre

1

Tôi đã gặp vấn đề tương tự với mảnh bản đồ. Nó tiếp tục biến mất trong khi hoạt ảnh thoát ra khỏi đoạn chứa của nó. Cách giải quyết là thêm hoạt ảnh cho phân đoạn bản đồ con mà sẽ giữ cho nó hiển thị trong quá trình hoạt ảnh thoát ra của phân đoạn mẹ. Hoạt ảnh của phân đoạn con đang giữ alpha ở 100% trong khoảng thời gian của nó.

Hoạt ảnh: res / animator / keep_child_fragment.xml

<?xml version="1.0" encoding="utf-8"?>    
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="1.0"
        android:duration="@integer/keep_child_fragment_animation_duration" />
</set>

Hoạt ảnh sau đó được áp dụng khi phân đoạn bản đồ được thêm vào phân đoạn mẹ.

Phân đoạn chính

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.map_parent_fragment, container, false);

    MapFragment mapFragment =  MapFragment.newInstance();

    getChildFragmentManager().beginTransaction()
            .setCustomAnimations(R.animator.keep_child_fragment, 0, 0, 0)
            .add(R.id.map, mapFragment)
            .commit();

    return view;
}

Cuối cùng, thời lượng của hoạt ảnh phân đoạn con được đặt trong tệp tài nguyên.

giá trị / số nguyên.xml

<resources>
  <integer name="keep_child_fragment_animation_duration">500</integer>
</resources>

0

Để tạo hoạt ảnh cho sự biến mất của các phân mảnh cũ, chúng ta có thể buộc ngăn xếp bật lại trên ChildFragmentManager. Điều này sẽ kích hoạt hoạt ảnh chuyển tiếp. Để làm điều này, chúng ta cần bắt kịp sự kiện OnBackButtonPressed hoặc lắng nghe các thay đổi của backstack.

Đây là ví dụ với mã.

View.OnClickListener() {//this is from custom button but you can listen for back button pressed
            @Override
            public void onClick(View v) {
                getChildFragmentManager().popBackStack();
                //and here we can manage other fragment operations 
            }
        });

  Fragment fr = MyNeastedFragment.newInstance(product);

  getChildFragmentManager()
          .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
                .replace(R.neasted_fragment_container, fr)
                .addToBackStack("Neasted Fragment")
                .commit();

0

Gần đây tôi đã gặp sự cố này trong câu hỏi của mình: Các đoạn lồng nhau chuyển đổi không chính xác

Tôi có một giải pháp giải quyết vấn đề này mà không cần lưu bitmap, cũng như không sử dụng phản xạ hoặc bất kỳ phương pháp nào khác không thỏa mãn.

Bạn có thể xem một dự án mẫu tại đây: https://github.com/zafrani/NestedFragmentTransitions

Bạn có thể xem ảnh GIF của hiệu ứng tại đây: https://imgur.com/94AvrW4

Trong ví dụ của tôi, có 6 đoạn con, được phân chia giữa hai đoạn cha. Tôi có thể đạt được các chuyển đổi để nhập, thoát, bật và đẩy mà không gặp bất kỳ sự cố nào. Việc thay đổi cấu hình và bấm ngược cũng được xử lý thành công.

Phần lớn giải pháp nằm trong hàm onCreateAnimator của BaseFragment của tôi (đoạn được mở rộng bởi các đoạn con và đoạn cha) của tôi) hàm onCreateAnimator trông giống như sau:

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

Hoạt động và phân đoạn cha chịu trách nhiệm thiết lập trạng thái của các boolean này. Nó dễ dàng hơn để xem như thế nào và ở đâu từ dự án mẫu của tôi.

Tôi không sử dụng các phân đoạn hỗ trợ trong ví dụ của mình, nhưng cùng một logic có thể được sử dụng với chúng và hàm onCreateAnimation của chúng


0

Một cách đơn giản để khắc phục sự cố này là sử dụng Fragmentlớp từ thư viện này thay vì lớp phân mảnh thư viện tiêu chuẩn:

https://github.com/marksalpeter/contract-fragment

Lưu ý thêm, gói này cũng chứa một mẫu đại biểu hữu ích được gọi là ContractFragmentmà bạn có thể thấy hữu ích để xây dựng ứng dụng của mình tận dụng mối quan hệ phân đoạn cha-con.


0

Từ câu trả lời trên của @kcoppock,

nếu bạn có Hoạt động-> Phân mảnh-> Phân mảnh (nhiều lần xếp chồng, cách sau sẽ hữu ích), một chỉnh sửa nhỏ cho câu trả lời tốt nhất IMHO.

public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {

    final Fragment parent = getParentFragment();

    Fragment parentOfParent = null;

    if( parent!=null ) {
        parentOfParent = parent.getParentFragment();
    }

    if( !enter && parent != null && parentOfParent!=null && parentOfParent.isRemoving()){
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

0

Sự cố của tôi là về loại bỏ phân đoạn chính (ft.remove (phân mảnh)), hoạt ảnh con không xảy ra.

Vấn đề cơ bản là các phân đoạn con ngay lập tức bị HỦY DIỆT TRƯỚC để phân mảnh cha mẹ thoát ra khỏi hoạt ảnh.

Hoạt ảnh tùy chỉnh phân đoạn con không được thực thi khi xóa Phân đoạn chính

Như những người khác đã trốn tránh, che giấu PHỤ HUYNH (chứ không phải đứa trẻ) trước khi loại bỏ PHỤ HUYNH là cách nên làm.

            val ft = fragmentManager?.beginTransaction()
            ft?.setCustomAnimations(R.anim.enter_from_right,
                    R.anim.exit_to_right)
            if (parentFragment.isHidden()) {
                ft?.show(vehicleModule)
            } else {
                ft?.hide(vehicleModule)
            }
            ft?.commit()

Nếu bạn thực sự muốn xóa phần chính, bạn có thể nên thiết lập một bộ lắng nghe trên hoạt ảnh tùy chỉnh của bạn để biết khi nào hoạt ảnh kết thúc, vì vậy bạn có thể thực hiện một số hoàn thiện một cách an toàn trên Phân đoạn chính (loại bỏ). Nếu bạn không làm điều này, một cách kịp thời, bạn có thể giết chết hoạt ảnh. Hoạt ảnh NB được thực hiện trên hàng đợi không đồng bộ của riêng nó.

BTW bạn không cần hoạt ảnh tùy chỉnh trên phân đoạn con, vì chúng sẽ kế thừa các hoạt ảnh gốc.



0

Chủ đề cũ, nhưng trong trường hợp ai đó tình cờ vào đây:

Tất cả các cách tiếp cận ở trên cảm thấy không hấp dẫn đối với tôi, giải pháp bitmap rất bẩn và không hiệu quả; những cái khác yêu cầu các phân đoạn con biết về khoảng thời gian của quá trình chuyển đổi được sử dụng trong giao dịch được sử dụng để tạo phân đoạn con được đề cập. Một giải pháp tốt hơn trong mắt tôi tôi giống như sau:

val currentFragment = supportFragmentManager.findFragmentByTag(TAG)
val transaction = supportFragmentManager
    .beginTransaction()
    .setCustomAnimations(anim1, anim2, anim1, anim2)
    .add(R.id.fragmentHolder, FragmentB(), TAG)
if (currentFragment != null) {
    transaction.hide(currentFragment).commit()
    Handler().postDelayed({
        supportFragmentManager.beginTransaction().remove(currentFragment).commit()
    }, DURATION_OF_ANIM)
} else {
    transaction.commit()
}

Chúng ta chỉ ẩn đoạn hiện tại và thêm đoạn mới, khi hoạt ảnh kết thúc, chúng ta xóa đoạn cũ. Bằng cách này, nó được xử lý ở một nơi và không có bitmap nào được tạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.