Fragment - Wait until transaction animation is complete - android

I am using a simple slide left, slide right custom animation when I perform FragmentTransaction. My problem is, the fragment I'm loading with the transaction animation contains code in the onResume() to make a HTTP call which causes my "Please Wait" progress dialog to show. It works, but I would like the move my code from onResume to another method in the Fragment that would not fire until the fragment is completely loaded. In other words, when the animation is complete and it is completely slid into place.
I use this to start the new fragment
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.setCustomAnimation(R.anim.slide_in_right, R.anim.slide_out_left);
ft.replace(R.id.realtabcontent, new MyFragment());
ft.commit();
and I want the method in MyNewFragment() to initialize after the animation is 100% complete
Is that possible?

There are two approaches possible here,
Implement an AnimationListener on the slide-in animation.
animation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
In onAnimationEnd() of the listener, call a method in the fragment to make the HTTP call.
The above approach wastes time just for the sake of the animation. So, instead of having a progress dialog, implement a circular progress bar within your fragment. That way your animation and http call can happen simultaneously. Once you get the response, remove the progress spinner and show your actual fragment views.

Related

Async Task running from Fragment

i saw a lot of posts saying that you should put your async task inside a Fragment(i dont remember other ways), so you can rotate screen and keep it...
but its not working for me
Activity shows the Fragment;
MyGetJsonFragment frag= ....
frag.setRetainInstance(true);
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(R.id.fragment_main, frag);
ft.commit();
than, inside this Fragment i have a RecyclerView, and when the user click in any item on list, it call the Adapter and inside the adapter onBindViewHolder i implemented the onClick to call the AsyncTask
holder.button_get.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new AsyncGet(activity, AdapterList.this).execute();
}
});
if i use the configChanges ( android:configChanges="orientation|keyboardHidden|screenSize)
at manifest it works normal, but i heard and read that this is not a good workaround!
after removed the configChanges, if i rotate the screen, the progress dialog dismiss and than on the post execute i get the error at progressdialog.dissmiss or anyother thing that dependes the activity
i understood the activity life cycle that when i rotate the original that started the async is destroyed and new one is created...
but how can i handle it and still show the progressdialog?
i need the Context/Activity at the async to start the progress and i have some db methods inside the postExecute that use Activity/Context
and why setRetainInstance(true) is not holding the Instance?

Changes fragments with sequential animation

I can't found how make animation between two fragments sequential. Is it possible?
For example. I have fragmentA and fragmentB. I can make animation
getSupportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_fade_in, R.anim.fragment_fade_out)
.replace(fragmentB)
.commit();
But animations shows together. I need scenario:
remove fragmentA with animation
waiting some ms
add fragmentB with animation
I found solution, but I think it's not good.
Handler mSequentiallyFrgAnimHandler = new Handler(Looper.getMainLooper());
//first remove preview fragment
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(0, R.anim.fragment_fade_out)
.remove(fragmentA)
.commit();
//we need animations sequentially, wait 400ms between animations
mSequentiallyFrgAnimHandler.postDelayed(new Runnable() {
#Override
public void run() {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fragment_fade_in, 0)
.add(fragmentB)
.commit();
}
}, 400);
Can someone give an example? How does it make correctly?

Wait for Fragment transaction to happen before taking an action

I have used a fragment as a slider menu and have list view in it.
The fragment has a slide in and out animation.
OnClick of the item in listview I open an activity. Here the problem I face is when I start the activity . The slider in the fragment is freezed half way sliding back and on finish of the activity that half slider completes its popupstack action and closes .Thus giving a glitch effect.
I want that the slider slides in on click of the item in the list view and then the activity starts such that when the activity closes the slider would have already completed its transaction of popupstack.
Is there any way where I can know when the transaction is completed and then I start my new activity.
starting of Slider fragment from my main fragment.
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.enter_from_left, 0, 0,
R.anim.exit_to_left)
.add(R.id.parent, new SliderMenuFragment())
.addToBackStack("animation").commit();
Slider fragment onclick event of Listview item
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
if (((TextView) view).getText().equals("Home")) {
getFragmentManager().popBackStackImmediate(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
Intent homePage = new Intent(getActivity(),
MyActivity.class);
getActivity().startActivity(homePage);
}
MyActivity
finish() method is called.
I guess it can be done by overriding onCreateAnimator and adding animation listener so that you can start new activity from onAnimationEnd
public class MyFragment extends Fragment
{
#Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim)
{
final int animatorId = ... // get proper animation id
final Animator anim = AnimatorInflater.loadAnimator(getActivity(), animatorId);
anim.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationStart(Animator animation)
{
...
}
#Override
public void onAnimationEnd(Animator animation)
{
...
}
});
return anim;
}

android lollipop - setentertransition() for fragment not working properly on exiting

While replacing fragment, I am using Slide animation available for android lollipop version. It works as expected for this particular replacement, but on pressing back button it first pops back current fragment & then reverse of enter animation (slide out) is executed.
private void replaceContentFrameByFragment(Fragment replaceBy, String replaceByFragmentTag) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Slide slide = new Slide(Gravity.BOTTOM);
slide.setDuration(1000);
replaceBy.setEnterTransition(slide);
}
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, replaceBy, replaceByFragmentTag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
}
So, how to force fragment to pop back only after reverse animation of slide is finished? I noticed that activity has method finishAfterTransition() provided. Is there something similar for frgament?
I had the same issue, onReturn from Fragment B -> A, Fragment B seem to be doing an extra slide transition. To get around it, I put in a fade transition for setReturnTransition() of a shorter time than the slide which looks quite good (as it dissolves into previous fragment). The code for this is:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Slide slideRight = new Slide(Gravity.RIGHT);
slideRight.setDuration(200);
fragment.setEnterTransition(slideRight);
Fade fade = new Fade();
fade.setDuration(100);
fragment.setReturnTransition(fade);
}
I was having the same issue with roughly the same pattern and was able to get around it by posting my fragments transition:
new Handler().post(new Runnable() {
#Override
public void run() {
page.setEnterTransition(new Slide());
getFragmentManager()
.beginTransaction()
.replace(R.id.root_layout, page, "current")
.commit();
}
});
Not sure why this resolves the issue (or if this is the absolute correct solution), but it worked for me.

How to animate fragment removal

I want to animate the removal of fragment.
I tried:
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.push_down_in, R.anim.push_up_out)
.remove(myFragment)
.commit();
but the fragment just disappears.
I noticed the out animation only plays with 'replace', so I tried to replace the fragment with an empty Fragment like this:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.push_down_in, R.anim.push_up_out)
.replace(viewId, new Fragment())
.commit();
But it still just disappears disappears.
So, how can I animate the removal of fragment?
I saw this when I was having similar problems and just thought Id drop a quick note.
Rather than creating a dummy fragment in order to replace the existing one I think you should animate the current fragments view. When the animation finishes you can simply remove the fragment.
This is how i did it:
final FragmentActivity a = getSherlockActivity();
if (a != null) {
//Your animation
Animation animation = AnimationUtils.loadAnimation(a, R.anim.bottom_out);
animation.setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime));
//You can use AnimationListener, MagicAnimationListener is simply a class extending it.
animation.setAnimationListener(new MagicAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
//This is the key, when the animation is finished, remove the fragment.
try {
FragmentTransaction ft = a.getSupportFragmentManager().beginTransaction();
ft.remove(RestTimerFragment.this);
ft.commitAllowingStateLoss();
} catch (Exception e) {
e.printStackTrace();
}
}
});
//Start the animation.
getView().startAnimation(animation);
}
I figured it out.
The exiting view is animated on the canvas of the entering view so if there is no entering canvas there is no canvas for the animation.
To show the animation I had to always use replace and use entering fragments of the same size to those exiting. After the animation is finished I set the view of the new fragments to gone.
You could animate the removal by setting this custom animation to the fragmentTransaction
fragmentTransaction.setCustomAnimations(R.anim.right_in, R.anim.defff,R.anim.defff,R.anim.right_out);
The third and fourth params are for removing the fragment
I got inspiration from Zoltish answer , this is my implementation:
1.add this method inside the fragment , it will animate the fragment out of the screen to the left:
public void animateOut()
{
TranslateAnimation trans=new TranslateAnimation(0,-300*Utils.getDensity(getActivity()), 0,0);
trans.setDuration(150);
trans.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
((BetsActivty)getActivity()).removeFrontFragmentAndSetControllToBetting();
}
});
getView().startAnimation(trans);
}
The method that inside onAnimationEnd() removes the fragment like this:
getSupportFragmentManager().beginTransaction().
remove(getSupportFragmentManager().findFragmentById(R.id.fragment_container)).commit();
2.call the animateOut of the fragment from onBack() of the activity.
Cheers
by the way my getDensity() is:
public static int getDensity(Context context)
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int)metrics.density;
}
with it i calculate the DP value for the current running Device.
Replacing with an empty fragment, before the point of insertion of the next fragment and also delaying the insertion of the next fragment (by 200ms) so that the exit animation of the blank fragment can play, solved my problem.
This the code to insert an empty fragment with exit animation.
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.exit, R.anim.pop_exit)
.replace(R.id.fragmentLayout, new Fragment())
.commit();
Exit.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="-100%"
android:interpolator="#android:anim/accelerate_interpolator"
android:duration="200"/>
</set>
pop_exit.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="100%"
android:interpolator="#android:anim/accelerate_interpolator"
android:duration="200"/>
</set>
I agreed with hugoc and here some code
to solve it
public class MyActivity extends Activity {
//Some thing
public void Fragment2BackToFragment1(Fragment fragment1, Fragment fragment2) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
animateExit(fragment2);
ft.replace(R.id.main_content, fragment1, "fragment1");
ft.commit();
}
private void animateExit(Fragment exitFragment) {
if (exitFragment != null) {
final View view = exitFragment.getView();
if (view != null) {
int anim = R.anim.fade_out;
Animation animation =
AnimationUtils.loadAnimation(getActivity(), anim);
animation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
view.postDelayed(new Runnable() {
#Override
public void run() {
view.setVisibility(View.GONE);
}
}, 300);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(animation);
}
}
}
}
An easy fix below:
1-Call animate on fragment.getView().
2-Remove fragment inside onAnimationEnd().
final Fragment frag= getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
frag.getView().animate().alpha(0f).scaleX(0f).scaleY(0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
getSupportFragmentManager()
.beginTransaction()
.remove(frag)
.commit();
}
}).start();
reason as #hugoc said
The exiting view is animated on the canvas of the entering view so if there is no entering canvas there is no canvas for the animation.
To show the animation I had to always use replace and use entering fragments of the same size to those exiting. After the animation is finished I set the view of the new fragments to gone.
below is actual code:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_bottom, R.anim.slide_out_top);
transaction.hide(removeFragment).add(R.id.fragment_container,addFragment).commit();
transaction = manager.beginTransaction();
transaction.remove(removeFragment).commit();
What enter:
it is new fragment which must be show
What exit:
it is current fragment which must be hide
What popEnter:
it is previous fragment which must be show
What popExit:
it is current fragment which must be hide
For use these animations, you should target them on show or hide transaction commands. Exit animations doesn't work on remove/replace procedures.
setCustomAnimations(enter, exit, popEnter, popExit) support enter and exit animation, So set four animations and must keep it before transaction.replace()
Kotlin:
val manager = supportFragmentManager
val transaction = manager.beginTransaction()
transaction.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right ,
android.R.anim.slide_in_left, android.R.anim.slide_out_right)
transaction.commit()
So the easy way:
When you open a fragment (called from parent Activity):
FragmentA fragment = new FragmentA();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_up, R.anim.slide_down);
transaction.add(android.R.id.content, fragment);
transaction.addToBackStack(null);
transaction.commit();
Specify enter and exit transactions
transaction.setCustomAnimations(R.anim.slide_up, R.anim.slide_down);
When closing a fragment (called from inside the fragment)
getActivity().getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.slide_up, R.anim.slide_down).remove(this).commit();
Specify enter and exit transactions
setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
See different variants of animations in https://developer.android.com/training/basics/fragments/animate.
On any event (button click, time out, etc.) you can write inside a fragment:
parentFragmentManager.beginTransaction()
.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right)
.remove(this)
.commitAllowingStateLoss()
// parentFragmentManager.popBackStack() - might be needed if the fragment keeps visible after removing.
Just because I spent time on it and I like my extension function: (Kotlin)
fun FragmentManager.removeByTagAnimated(tag: String, animation: Int){
findFragmentByTag(tag)?.let { fragToRemove ->
val anim = AnimationUtils.loadAnimation(fragToRemove.context, animation).apply {
setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {}
override fun onAnimationRepeat(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) {
commit {
remove(fragToRemove)
}
}
})
}
fragToRemove.view?.startAnimation(anim)
}
}
Animation is an R.anim resource like in this page
Just use supportFragmentManager.removeByTagAnimated(MY_FRAGMENT_TAG, R.anim.my_exit_animation) in your Activity.

Categories

Resources