Neil
Neil

Reputation: 1861

Android - problems animating ActionBar icon from Fragments

I'm having some problems consistently animating a 'refresh' icon in the ActionBar of my app.

I have a container FragmentActivity which swaps fragments in and out as the user navigates through the app (either from within the fragment itself or from a SlidingMenu option). So when the app first loads, my FragmentContainer adds FragA. From FragA the user can navigate to FragB which is then swapped in.

In the action bar I display a static 'refresh' icon. As each Fragment loads, I replace this with an animated 'spinner' icon. When the load completes, I revert to the original refresh icon.

Problem is, this animation only works for the original fragment (FragA, in this case). When the user navigates to FragB and selects the refresh icon, the refresh is triggered, but the animation never happens. Similarly, if the back button is pressed to return to FragA, this now follows the same pattern i.e. the refresh button does not animate when pressed.

Things to note...

  1. I'm using ActionBarSherlock and the SlidingMenu implementation at https://github.com/jfeinstein10/SlidingMenu. So the above activity is actually a SlidingFragmentActivity.
  2. Both Fragments call setHasOptionsMenu(true) - I've debugged through this and onCreateOptionsMenu is being correctly called for each.
  3. The icons are being correctly displayed for both Fragments - the animation is just not happening when I navigate off the 'default' fragment.
  4. I see the same behaviour when using the SlidingMenu to navigate - FragA loads, animation works -> SlidingMenu is used to navigate to a different fragment... animation doesn't work -> Back button to FragA... animation doesn't work here either.
  5. I'm using FragmentTransaction.remove() and add() rather than replace() as I have previously had back-button issues with replace() - I am using the compatibility lib and I read on here that the replace implementation is a bit buggy - and not using it certainly fixed the issues I was seeing.

Code snippets below:

My code to load the original fragment is....

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.content_frame, new FragA());
ft.addToBackStack(null);
ft.commit();

To 'swap' FragB for FragA....

public void switchContent(PysoBaseFragment fragment) {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.remove(existingFragment);
    ft.add(R.id.content_frame, fragment);
    ft.addToBackStack(null);
    ft.commit();
}

This method is declared in the container activity and is called from FragA i.e....

getFragmentContainer().switchContent(new FragB());

The code to spin the icon is called from the new Fragment as it starts to load. Its something like...

    ImageView spinnerActionView = (ImageView) inflater.inflate(R.layout.refresh_action_view, null);
    Animation rotation = AnimationUtils.loadAnimation(this, R.anim.rotate_animation);
    rotation.setRepeatCount(Animation.INFINITE);
    spinnerActionView.startAnimation(rotation);
    menuItemRefresh = menu.findItem(R.id.menu_refresh);
    menuItemRefresh.setActionView(spinnerActionView);

Where menu is assigned to an instance variable of the container when onCreateOptionsMenu is called.

Update:

I've noticed another weird bug in this area (I'm happy to add this as a separate question, but I'm updating it here in the hope that it will shed some light on my original problem - I believe both are caused by how I have configured my action-bar from my Fragments).

When I first load a fragment I have 1 static refresh icon displayed. If I rotate the screen... another refresh icon appears... when I rotate the screen back, a 3rd refresh icon appears!

Stranger still, clicking the back-button removes each additional icon in turn, before finally (on the 4th click) returning to the previous screen.

Upvotes: 11

Views: 3079

Answers (6)

Omar A. Khaled
Omar A. Khaled

Reputation: 21

I know it is too late but i've faced this issue now and after 2 days of researching i've concluded that if you are using the ActionBarDrawerToggle you need to syncState() each time you switch fragments in my case i've put

toggle.syncState()

in the MainActivity class in a onOptionsItemSelected() method which is called everytime the fragments get changed and it worked

Upvotes: 0

Handroid
Handroid

Reputation: 399

Only call onCreateOptionsMenu() in the main activity (I mean Sliding fragment activity).No need of calling onCreateOptionsMenu() from every fragment.I am giving a sample snippet below.

Call from fragment (whenerver loading starts and end)

    yourSlidingFragmnetActivity.refresh = true/false; (u can have this logic of your own)
    getActivity().supportInvalidateOptionsMenu();


    @Override
        public boolean onCreateOptionsMenu(Menu menu) {


            menu.add(BaseListActivity.DEFAULT_GROUP_ID, BaseListActivity.MENU_ITEM_RELOAD_LIST, BaseListActivity.DEFAULT_ORDER,
                    "Refresh").setIcon(R.drawable.ic_menu_refresh_new)
                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
            return true;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            if (item.getItemId() == BaseListActivity.MENU_ITEM_RELOAD_LIST) {
                item.setActionView(R.layout.progressbar_layout);
                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                return true;
            }
            return true;
        }

        public static boolean refresh = false;

        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
            MenuItem item = menu.getItem(0);
            if (refresh) {
                item.setActionView(R.layout.progressbar_layout);
            } else {
                item.setIcon(R.drawable.ic_menu_refresh_new);
            }
            return true;
        }

        R.layout.progressbar_layout:
        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"
                      android:addStatesFromChildren="true"
                      android:focusable="true"
                      android:paddingLeft="4dp"
                      android:paddingRight="4dp"
                      android:gravity="center"
                      style="?attr/actionButtonStyle">
        <ProgressBar
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:focusable="true"
                    style="@android:style/Widget.ProgressBar.Small"/>
        </LinearLayout>

I am also using jfeinstein10 slidingmenu lib , I am not adding or removing fragments , I am always replace them and in oncreate menthod iam using like this

private Fragment mContent;
if(mContent == null){
mContent = new SampleFragment();
}
// set the Above View
    setContentView(R.layout.content_frame);
                getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.content_frame,mContent)
                .commit();      

Upvotes: 1

frozenkoi
frozenkoi

Reputation: 3248

You are adding an action view to add the animation. If you try to do something else, like changing the menu item's icon (even if it's not animated, just to see if it changes) instead does that work?

Upvotes: 1

Viswanath Lekshmanan
Viswanath Lekshmanan

Reputation: 10083

    "I've noticed another weird bug in this area (I'm happy to add this as a separate question, but I'm updating it here in the hope that it will shed some light on my original problem - I believe both are caused by how I have configured my action-bar from my Fragments).   
    When I first load a fragment I have 1 static refresh icon displayed. If I rotate the screen... another refresh icon appears... when I rotate the screen back, a 3rd refresh icon appears! 
    Stranger still, clicking the back-button removes each additional icon in turn, before finally (on the 4th click) returning to the previous screen."

I can give Explanation to this ,When ever you change orientation (Potrait<->landscape).

An activity is restarted when the orientation changes. I'm guessing your code isn't saving needed information before this restart occurs. You can stop this default behavior by handling specific configuration changes yourself (ie: orientation change). A good tutorial on doing this is located here: Handling Runtime Changes

Upvotes: 1

Artem Zelinskiy
Artem Zelinskiy

Reputation: 2210

Never chang menu items somewhere except

onPrepareOtionsMenu(){
}

you should something like this.

in your activity:

boolean mIsRefreshing =false;

public boolean onPrepareOptionsMenu(Menu menu) {
if(mIsRefreshing){
        final MenuItem menuItemRefresh = menu.findItem(R.id.menu_refresh);
    menuItemRefresh.setActionView(spinnerActionView);
}
        return true;
    }

public void setRefreshing(boolean refreshing){
mIsRefreshing = refreshing;
invalidateOptionsMenu();  //supportInvelidateOptionsMenu()
}

So now you can call from your frament

((YourActivity)getActivity()).setRefreshing(true);
((YourActivity)getActivity()).setRefreshing(false);

Upvotes: 5

megabits
megabits

Reputation: 145

If you are using the same Animation object each time, you may need to reset your animation before attempting to run it again.

Try adding rotation.reset() above your call to startAnimation().

Upvotes: 1

Related Questions