UFO ET IT

팝 애니메이션을 재생하지 않고 조각 백 스택 팝

ufoet 2021. 1. 7. 08:21
반응형

팝 애니메이션을 재생하지 않고 조각 백 스택 팝


다음 코드를 사용하여 조각 스택에 조각을 푸시합니다.

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right,
     R.anim.slide_in_left, R.anim.slide_out_left);
fragmentTransaction.replace(getId(), newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

이렇게하면 예를 들어 뒤로 버튼을 눌러 조각 스택이 팝될 때 조각 팝 애니메이션이 재생됩니다. 그러나이 애니메이션을 표시하지 않고 프래그먼트 백 스택을 팝하고 싶은 상황이 있습니다. 예를 들어 다른 활동에서 방금 돌아 왔고 애니메이션없이 이전 프래그먼트를 한 번에 표시하고 싶기 때문입니다.

예제 탐색은 다음과 같습니다.

  • 사용자는 루트 조각이있는 시작 화면에 있습니다.
  • 그는 루트 조각에서 항목을 선택한 다음 해당 항목의 세부 정보를 표시하는 새 조각을 표시합니다. 푸시 및 팝 케이스 모두에 대해 애니메이션을 설정하는 조각 트랜잭션을 사용하여 수행합니다 (따라서 사용자가 뒤로 버튼을 누르면 전환이 애니메이션됩니다).
  • 이 조각에서 그는 (어떤 이유로 든) 방금 표시된 항목을 삭제하는 활동을 시작합니다.
  • 이 활동이 끝나면 "세부 조각"의 "팝 애니메이션"을 표시하지 않고 루트 조각으로 돌아가고 싶습니다.

지정된 팝 애니메이션을 재생하지 않고 조각 백 스택을 팝하는 방법이 있습니까?


그래서 Warpzit은 올바른 길을 가고 있었으며 그는 당신의 특정 문제를 너무 잘 다루지 않았습니다. 나는 똑같은 문제를 만났고 여기에 내가 어떻게 해결했는지가 있습니다.

먼저 정적 부울 변수를 만들었습니다 (간단하게하기 위해 FragmentUtils 클래스에 넣습니다) ...

public class FragmentUtils {
    public static boolean sDisableFragmentAnimations = false;
}

그런 다음 가지고있는 모든 조각에서 onCreateAnimation 메서드를 재정의해야합니다.

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if (FragmentUtils.sDisableFragmentAnimations) {
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

그런 다음 활동에서 백 스택을 제거해야 할 때 다음을 수행하십시오.

public void clearBackStack() {
    FragmentUtils.sDisableFragmentAnimations = true;
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentUtils.sDisableFragmentAnimations = false;
}

그리고 짜잔, clearBackStack ()을 호출하면 전환 애니메이션없이 루트 조각으로 돌아갑니다.

바라건대 큰 G가 앞으로이 일을하는 덜 어리석은 방법을 추가 할 것입니다.


사용자는 루트 조각이있는 시작 화면에 있습니다.

루트 조각이 활동 A에 포함되어 있다고 가정 해 보겠습니다.

그는 루트 조각에서 항목을 선택한 다음 해당 항목의 세부 정보를 표시하는 새 조각을 표시합니다. 푸시 및 팝 케이스 모두에 대해 애니메이션을 설정하는 조각 트랜잭션을 사용하여 수행합니다 (따라서 사용자가 뒤로 버튼을 누르면 전환이 애니메이션됩니다).

트랜잭션이 백 스택에 추가됩니다. 즉, 세부 조각에서 뒤로 버튼을 누르면 팝핑 프로세스가 애니메이션으로 표시됩니다.

이 조각에서 그는 방금 표시된 항목을 (어떤 이유로 든) 삭제하는 활동을 시작합니다.

활동 B라고 가정 해 보겠습니다.

이 활동이 끝나면 "세부 조각"의 "팝 애니메이션"을 표시하지 않고 루트 조각으로 돌아가고 싶습니다.

이 동작을 얻는 한 가지 방법은 활동 B에서 수행하는 것입니다.

Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();

그러면 문서 에 따라 활동 A가 루트 상태로 재설정되어 시작 됩니다 ( "이 시작 모드는 FLAG_ACTIVITY_NEW_TASK : ......"와 함께 좋은 효과를 낼 수도있는 섹션의 마지막 단락을 확인하십시오. "). )

이 구성을 사용하면 애니메이션이 기본 케이스에 표시되고 특별한 경우 다음을 사용하여 애니메이션을 제어 할 수 있습니다.

intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

애니메이션없이 새로운 활동을 시작합니다. 애니메이션이 필요하면 overridePendingTransition방법을 사용하여 할 수 있습니다 .


따라서 지원 라이브러리의 경우 다음 작업이 수행됩니다.

커스텀 팝 애니메이션이 있어야하는 프래그먼트에서 onCreateAnimation을 커스텀 애니메이션으로 재정의합니다. 당신은 그것을 얻고 원하는 것에 따라 어떤 종류의 매개 변수를 설정할 수 있습니다. 일반 조각으로 작동하려면 추가 작업을 수행해야 할 수 있습니다.

다음은이를 재정의하고 설정된 기간을 변경하는 예입니다.

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim);
    if(!enter) {
        if(anim != null) {
            anim.setDuration(0); // This doesn't seem to be called.
            return anim;
        } else {
            Animation test = new TestAnimation();
            test.setDuration(0);
            return test;
        }
    }
    return anim;
}

private class TestAnimation extends Animation {

}

그래서 @Geoff의 대답에 약간의 변경을 제안하고 싶습니다.

전역 정적 부울을 사용하는 대신 로컬 비 정적 부울을 사용하는 것이 좋습니다. 이것이 제가 생각 해낸 것입니다.

인터페이스 만들기

public interface TransitionAnimator {
    void disableTransitionAnimation();
    void enableTransitionAnimation();
}

조각이 해당 인터페이스를 구현하도록합니다.

public class MyFragment extends Fragment implements TransitionAnimator {

    private boolean mTransitionAnimation;

    @Override
    public void disableTransitionAnimation() {
        mTransitionAnimation = false;
    }

    @Override
    public void enableTransitionAnimation() {
        mTransitionAnimation = true;
    }

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        Animation result;
        if (!mTransitionAnimation) {
            Animation dummyAnimation = new Animation() {
            };
            dummyAnimation.setDuration(0);
            result = dummyAnimation;
        } else {
            result = super.onCreateAnimation(transit, enter, nextAnim);
        }
        return result;
    }

그런 다음 조각에 대한 전환 애니메이션을 비활성화하려면

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).disableTransitionAnimation();
}

활성화하려면

if (fragment instanceof TransitionAnimator) {
    ((TransitionAnimator) fragment).enableTransitionAnimation();
}

조각 관리자의 모든 조각에 대해 동일한 작업을 수행하려면

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
    if (fragment instanceof TransitionAnimator) {
        // disable animations
        ((TransitionAnimator) fragment).disableTransitionAnimation();
    }
}

매우 유사하지만 정적 필드가 없습니다.


R.anim.slide_out을setCustomAnimation() 설정하지 않는 또 다른 오버로드 방법을 사용 하면 문제가 해결됩니다.

건배 :)


질문에 답하기 전에 직접 질문해야합니다.

두 번째 활동의 onBackPressed () 메서드에서 첫 번째 활동의 백 스택에 액세스 할 수 있습니까?

그렇다면 popBackStackImmediate (String trnaisiotnName, int inclusive)를 호출하면 백 스택에서 조각 전환이 제거되므로 애니메이션에 대해 걱정할 필요가 없습니다.

이전 활동의 백 스택에 액세스 할 수 있다고 가정합니다. 그렇지 않으면 작동하지 않습니다.


이것은 애니메이션없이 overridePendingTransition(int enterAnim, int exitAnim)두 가지 모두 0통해 쉽게 달성 할 수 있습니다.

FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    fm.popBackStack();
    overridePendingTransition(0, 0);
}

이것은 @Geoff의 훌륭한 답변에 대한 후속 조치이지만 더 역동적이고 실제적인 시나리오에 적합합니다.

나는 이것이 멋진 작은 게시물이라고 상상했지만 이제는 그것이 약간의 손에서 벗어났다는 것을 깨달았습니다. 그러나 코드는 모두 거기에 있으며 전환 애니메이션을 비활성화하는 방법보다 훨씬 많은 것을 다루지 만 정말 유용하다고 생각합니다.

일반적으로 Fragments로 작업 할 때 BaseActivityCallback에 연결되는 BaseFragment를 갖고 싶습니다. 이 BaseActivityCallback은 내 Fragments에서 자체 위에 새 Fragment를 추가하거나 그 아래에 Fragment를 팝하는 데 사용할 수 있으므로 팝 애니메이션을 비활성화하거나 조용히 팝하려는 욕구가 있습니다 .

interface BaseActivityCallback
{
    void addFragment ( BaseFragment f, int containerResId );
    void popFragment ( boolean silently );
}

class BaseActivity extends android.support.v4.app.FragmentActivity implements BaseActivityCallback
{
    public void addFragment ( BaseFragment f, int containerResId )
    {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://stackoverflow.com/a/17488542/2412477
        ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME);
        ft.replace(containerResId, fragment);
        ft.commitAllowingStateLoss();
    }        

    public void popFragment ( boolean silently )
    {
        FragmentManager fm = getSupportFragmentManager();                
        if ( silently ) {
            int count = fm.getFragments().size();
            BaseFragment f = (BaseFragment)fm.getFragments().get(count-1);
            f.setDisableTransitionAnimations(true);
        }
        fm.popBackStackImmediate();
    }
}

public abstract class BaseFragment extends android.support.v4.app.Fragment
{
    private static final String TAG = "BaseFragment";
    private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations";

    protected BaseActivityCallback baseActivityCallback;
    private boolean disableTransitionAnimations;    

    @Override
    public void onCreate ( @Nullable Bundle savedInstanceState )
    {
        super.onCreate(savedInstanceState);
        disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false));
    }

    @Override
    public void onAttach ( Context context )
    {
        super.onAttach(context);
        baseActivityCallback = (BaseActivityCallback)context;
    }

    @Override
    public void onSaveInstanceState ( Bundle outState )
    {
        super.onSaveInstanceState(outState);
        outState.putBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, disableTransitionAnimations);
    }

    @Override
    public Animation onCreateAnimation ( int transit, boolean enter, int nextAnim )
    {
        if ( disableTransitionAnimations ) {
            Animation nop = new Animation(){};
            nop.setDuration(0);
            return nop;
        }
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    public void setDisableTransitionAnimations ( boolean disableTransitionAnimations )
    {
        this.disableTransitionAnimations = disableTransitionAnimations; // http://stackoverflow.com/a/11253987/2412477
    }
}

지금 당신은 당신을 만들 수 있습니다 MainActivity쇼가와가 Fragment1또 다른 추가 할 수있는 Fragment2다시 팝업 수있는 Fragment1 자동을 :

public class MainActivity extends BaseActivity
{
    protected void onCreate ( Bundle savedInstanceState )
    {
        setContentView(R.layout.main_activity);
        ...
        if ( getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty() ) {

            addFragment( FragmentA.newInstance(), R.id.main_activity_fragment_container );
        }
    }
    ...
}

public class FragmentA extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false);
        ...
        root.findViewById(R.id.fragment_a_next_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.addFragment( FragmentB.newInstance(), R.id.main_activity_fragment_container );
                 }
             });
    }
}

public class FragmentB extends BaseFragment
{
    public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false);
        ...
        root.findViewById(R.id.fragment_b_pop_silently_button)
            .setOnClickListener( new View.OnClickListener() {
                 public void onClick ( View v ) {
                      baseActivityCallback.popFragment( true );
                 }
             });
    }
}

애니메이션없이 팝하려는 조각에서이를 재정의하고 진입 할 때 애니메이션을 유지합니다.

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter){
        Animation a = new Animation() {};
        a.setDuration(0);
        return a;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

Easier solution:

for (fragment in supportFragmentManager.fragments) {
    removeFragment(fragment)
}
if (supportFragmentManager.backStackEntryCount > 0) {
    supportFragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

Android actually now has a way to do this without the work around @Geoff answered.

To avoid the animation to run on popBackStack(), when inflating your fragments add .setReorderingAllowed(true) to your fragmentTransaction.

So for example:

supportFragmentTransaction.beginTransaction()
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .setCustomAnimations(
        android.R.anim.fade_in,
        android.R.anim.fade_out,
        android.R.anim.fade_in,
        android.R.anim.fade_out
    )
    .replace(yourContainer.id, yourFragment)
    .commit()

You'll notice that if you set setReorderingAllowed(true), the pop animation would no longer play. The results are actually similar to the result of @Geoff's answer.


Reply to Geoff and plackemacher comment.

You can try to remove all views from this Fragment. Then fragment will show but it should be transparent.

Remove all-1 (I use navigate drawer so drawer fragment should stay) fragment:

    int size = fragmentsList.size ()-1;

    FragmentTransaction transaction = fragmentManager.beginTransaction ();
    transaction.setTransition (FragmentTransaction.TRANSIT_NONE);

    Fragment fragment;

    for (int i = size ; i > 0 ; i--)
    {
        fragment = fragmentsList.get (i);
        if(fragment != null)
        {
            View viewContainer = fragment.getView ();
            if (viewContainer != null)
            {
                ((ViewGroup) viewContainer).removeAllViews ();
            }
            transaction.remove (fragment);
        }
    }

    size = fragmentManager.getBackStackEntryCount ();

    for (int i = 0; i < size  ; i++)
    {
        fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

Sorry for my English

ReferenceURL : https://stackoverflow.com/questions/9194311/pop-the-fragment-backstack-without-playing-the-pop-animation

반응형