I'm trying to use code from This Link creating multiple fragment wizard page. On the first fragment there is EditText and Next Button. On clicking Next Button, I'm disabling these controls and then navigating to 2nd fragment(page).
Now when I press back button using the below code
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
Fragment get changed back to first activity. But the EditText and Next Button are still disable.
I want to enable these button on back button clicking. But not getting any call in the overridden onResume(), onStart(), onViewStateRestored() function of FirstFragment class.
mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (position == 0) { // first fragment
// enable the views
}
if (position == 1) { // Second fragment
// disable the views
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Use the onPageSelected to disable and enable the buttons depending on the position you are at
Fragment is not actually paused or disabled when you go thru pager (depends on your nr. of active pages setting), it is still there. You should send event from your Adapter to Fragment when changing pages.
Background
FragmentList contains a view pager with an underlying FragmentStatePagerAdapter to show pages of FragmentDetail
FragmentDetail contains a scroll view
What i want to do
As you swipe, i want the previous FragmentDetail scroll position reset to the top. At the moment, when you swipe back to it, the scroll position goes back to where you left off.
E.g. I am on page 1 of view pager, i scroll to bottom of the current detail fragment. I then go to next page. Finally i go back to the first page, i want the scroll to be at the top and not where i left it
I tried the following
in OnPause of fragment detail, i tried the following code
#Override
public void onPause() {
super.onPause();
mScrollView.scrollTo(0,0);
}
I also tried the following
#Override
public void onPause() {
super.onPause();
mScrollView.post(new Runnable() {
#Override
public void run() {
mScrollView.fullScroll(View.FOCUS_UP);
}
});
}
Also put the scrollTo code in a runnable is well.
Does not scroll to the top
Forget about onPause(); you will drive yourself insane.
The best way I have found to do this sort of thing is override setPrimaryItem() in the FragmentPagerAdapter subclass. setPrimaryItem() is called whenever a fragment is displayed by the ViewPager.
Let's say you have an interface that looks like this:
public interface ResettableFragment {
public void reset();
}
and so your fragment implementation is:
#Override
public void reset() {
mScrollView.scrollTo(0,0);
}
In your FragmentPagerAdapter subclass, create a member
private ResettableFragment mLast;
and override setPrimaryItem() like this:
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (mLast != null) {
mLast.reset();
mLast = null;
}
if (object instanceof ResettableFragment) {
mLast = (ResettableFragment) object;
}
}
Some people prefer to register a ViewPager.OnPageChangListener which overrides onPageSelected() to do the reset logic instead of overriding setPrimaryItem() in the adapter.
That might look something like this:
viewPager.addOnPageChangeListener(new SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == POSITION_OF_PAGE_TO_RESET + 1 ||
position == POSITION_OF_PAGE_TO_RESET - 1) {
FragmentPagerAdapter adapter = (FragmentPagerAdapter) viewPager.getAdapter();
ResettableFragment fragment = (ResettableFragment) adapter.getItem(position);
fragment.reset();
}
}
});
I am using the sample code from the developer.android.com for navigation drawer. I am unable to know what exactly needs to be changed to start activities instead of images which now appear. So what parts do I need to delete so that I can make listviewitemclick to open activities?
I am working here
private void selectItem(int position) {
switch(position){
case 0:
Intent a = new Intent(MainActivity.this, sampleopen.class);
startActivity(a);
}
}
The problem is that it opens the second activity first then when we press back it goes to first activity and there the drawer is implemented
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
Here you define a listener - you need to provide your own instead of new DrawerItemClickListener() and there you'll be able to launch activities according to position received.
// set the on item click listener for the listview object
mNavigationListView.setOnItemClickListener(mOnNavigationItemClickListener);
// handle clicks here
private AdapterView.OnItemClickListener mOnNavigationItemClickListener = new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0) then launch ACtivity #1
//....
}
};
I'm developing an application which uses the navigation drawer pattern (With DrawerLayout).
Each click on a drawer's item, replaces the fragment in the main container.
However, I'm not sure when is the right time to do the fragment transaction?
When the drawer starts closing? Or after it is closed?
In google's documentaion example, you can see that they are doing the transaction
right after the item click, and then close the drawer.
As a result, the drawer seems laggy and not smooth, and it looks very bad (It happens in my application too).
In Gmail and Google Drive applications, on the other way, It seems like they are doing the transaction after the drawer closed (Am I Right?).
As a result, the drawer is not laggy and very smooth, BUT it takes about 1 second (the time it takes to the drawer get closed) at least, to see the next fragment.
It seems like there is no way the drawer will be smooth when immediately doing fragment transaction.
What do you think about that?
Thanks in advance!
Yup, couldn't agree more, performing a fragment (with view) transaction results in a layout pass which causes janky animations on views being animated, citing DrawerLayout docs:
DrawerLayout.DrawerListener can be used to monitor the state and motion of drawer views. Avoid performing expensive operations such as layout during animation as it can cause stuttering; try to perform expensive operations during the STATE_IDLE state.
So please perform your fragment transactions after the drawer is closed or somebody patches the support library to somehow fix that :)
Another solution is to create a Handler and post a delayed Runnable after you close the drawer, as shown here: https://stackoverflow.com/a/18483633/769501. The benefit with this approach is that your fragments will be replaced much sooner than they would be if you waited for DrawerListener#onDrawerClosed(), but of course the arbitrary delay doesn't 100% guarantee the drawer animation will be finished in time.
That said, I use a 200ms delay and it works wonderfully.
private class DrawerItemClickListener implements OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
drawerLayout.closeDrawer(drawerList);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
switchFragments(position); // your fragment transactions go here
}
}, 200);
}
}
This is what I do to achieve an smooth transaction animation similar to Gmail app:
activity_drawer.xml
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer -->
<ListView
android:id="#+id/left_drawer"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:choiceMode="singleChoice" />
</android.support.v4.widget.DrawerLayout>
DrawerActivity.java
private Fragment mContentFragment;
private Fragment mNextContentFragment;
private boolean mChangeContentFragment = false;
private Handler mHandler = new Handler();
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
mDrawerLayout.setDrawerListener(new DrawerListener());
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
....
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
getSupportFragmentManager().beginTransaction().remove(mContentFragment).commit();
switch (position) {
case 0:
mNextContentFragment = new Fragment1();
break;
case 1:
mNextContentFragment = new Fragment2();
break;
case 2:
mNextContentFragment = new Fragment3();
break;
}
mChangeContentFragment = true;
mDrawerList.setItemChecked(position, true);
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mDrawerLayout.closeDrawer(mDrawerList);
}
}, 150);
}
}
private class DrawerListener implements android.support.v4.widget.DrawerLayout.DrawerListener {
#Override
public void onDrawerClosed(View view) {
if (mChangeContentFragment) {
getSupportFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).replace(R.id.content_frame, mNextContentFragment).commit();
mContentFragment = mNextContentFragment;
mNextContentFragment = null;
mChangeContentFragment = false;
}
}
}
Hope that helps you! :-)
I know this question is old but I ran into the same problem and figured I would post my solution as I think it is a better implementation than adding a hardcoded delay time. What I did was use the onDrawerClosed function to verify that the drawer IS closed before doing my task.
//on button click...
private void displayView(int position) {
switch (position) {
//if item 1 is selected, update a global variable `"int itemPosition"` to be 1
case 1:
itemPosition = 1;
//();
break;
default:
break;
}
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
mDrawerLayout.closeDrawer(mDrawerList); //close drawer
}
and then in onDrawerClosed, open the corresponding activity.
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
// calling onPrepareOptionsMenu() to show action bar icons
supportInvalidateOptionsMenu();
if (itemPosition == 1) {
Intent intent = new Intent(BaseActivity.this, SecondActivity.class);
startActivity(intent);
}
}
Just write your code in a handler and put 200 ms delay.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
openSelectionDrawerItem(position);
}
}, 200);
Instead of delaying your item clicks which may make your app feel slow. I would just delay the closing of the mDrawerLayout. I would not use the DrawerLayout.OnDrawerListener onClose(...) either because those callbacks are so slow to be called.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mDrawerLayout.closeDrawer(GravityCompat.START);
}
}, 200);
If you want it smooth and without any delay, leave the drawer open and close it afterwards when returning (in the onRestart() method).
#Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
mDrawerLayout.closeDrawer(mDrawerList);
}
The side effect is an (speedy) animation when returning, but this might be acceptable.
I push a fragment on the fragment stack using the following code:
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();
This way, when the fragment stack is popped, e.g. by pressing the back button, a fragment pop animation is played. However, there are situations in which i would like to pop the fragment backstack without showing this animation, e.g. because I just returned from another activity and want to display the previous fragment at once, without animation.
An example navigation could look like this:
The user is on the start screen with the root fragment
He selects an item on the root fragment which then displays a new fragment to show details of that item. It does so using a fragment transaction that sets animations both for the push and the pop case (so when the user presses the back button, the transition is animated)
From this fragment he starts an activity which (for whatever reason) deletes the item that was just shown
When this activity finishes, I would like to return to the root fragment without showing the "pop animation" of the "detail fragment"
Is there a way to pop the fragment backstack without playing the specified pop animation?
So Warpzit was on the right track, he just didn't address your specific issue too well. I came across the exact same issue and here is how I solved it.
First I created a static boolean variable (for simplicity's sake, lets put it in the FragmentUtils class)...
public class FragmentUtils {
public static boolean sDisableFragmentAnimations = false;
}
Then, in EVERY fragment you have, you need to override the onCreateAnimation method...
#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);
}
Then, when you need to clear the backstack from your activity simply do the following...
public void clearBackStack() {
FragmentUtils.sDisableFragmentAnimations = true;
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentUtils.sDisableFragmentAnimations = false;
}
And voila, a call to clearBackStack() will drop you back into the root fragment without any transition animations.
Hopefully the big G will add a less stupid way of doing this in the future.
So for the support library following works:
In the fragment which should have a custom pop animation you override the onCreateAnimation with your own custom one. You could get it and set some kind of parameter depending on what you want. There might need to be done some extra work to make it work with regular fragments.
Here is the example where I'm overriding it and changing the set duration:
#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 {
}
The user is on the start screen with the root fragment
Lets say the root fragment is contained in Activity A.
He selects an item on the root fragment which then displays a new fragment to show details of that item. It does so using a fragment transaction that sets animations both for the push and the pop case (so when the user presses the back button, the transition is animated)
The transaction is added to the back stack. Which means that when the back button is pressed from detail fragment, the popping process is animated.
From this fragment he starts an activity which (for whatever reason) deletes the item that was just shown.
Lets say it is Activity B
When this activity finishes, I would like to return to the root fragment without showing the "pop animation" of the "detail fragment"
One way of getting this behavior is by doing this in Activity 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();
This will start the Activity A resetting it to its root state according to the documentation.(check the last paragraph in the section which says "This launch mode can also be used to good effect in conjunction with FLAG_ACTIVITY_NEW_TASK:......")
With this configuration, the animation will be present in the default case while in the special case you can control the animation using :
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Which starts new activity without any animations. If you do want any animation, you can do it using the overridePendingTransition method.
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.
So, I'd like to suggest a small change to #Geoff's answer.
Instead of having a global static boolean, I'd rather have a local non-static one. This is what I came up with.
Create an interface
public interface TransitionAnimator {
void disableTransitionAnimation();
void enableTransitionAnimation();
}
Make the fragment implement that interface.
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;
}
And then, when you want to disable the transition animations for a fragment, just do
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).disableTransitionAnimation();
}
to enable them, just do
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).enableTransitionAnimation();
}
If you want to do the same for all the fragments in the fragment manager, just do
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof TransitionAnimator) {
// disable animations
((TransitionAnimator) fragment).disableTransitionAnimation();
}
}
Very similar, but without static fields.
Just use another overloaded method of setCustomAnimation() and in which do not set the R.anim.slide_out
and that will solve your problem
Cheers :)
Before answering your question, I need to ask a question myself.
In the onBackPressed() method of the second activity, can you access the backstack of the first activity?
If yes, then you can call popBackStackImmediate(String trnaisiotnName, int inclusive) and it will remove the fragment transition from the backstack, and you dont need to worry about animations.
I am assuming you can access backstack of the previous activity, otherwise this wont work
This is fairly easy to achieve through overridePendingTransition(int enterAnim, int exitAnim) with both 0 for no animation.
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
overridePendingTransition(0, 0);
}
This is a follow-up to #Geoff's excellent answer, but fitted for a more dynamic and real-live scenario.
I imagined this being a nice little post, but I realize now that it got a little out of hand. However, the code is all there and I find it really useful, though it covers a lot more than just how to disable transition animations.
Usually, when I work with Fragments I like to have a BaseFragment that attaches to a BaseActivityCallback. This BaseActivityCallback can be used by the my Fragments to add a new Fragment on top of itself, or even to pop Fragments beneath it, hence the desire to disable pop animations -- or pop silently:
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
}
}
Now you can create your MainActivity and have that show a Fragment1 which can add another Fragment2 which may in turn pop Fragment1 silently:
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 this in the fragment that you want to pop without animation and still keep the animation when you enter
#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)
}
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