Java – Invalidates the context action bar when switching tabs

Invalidates the context action bar when switching tabs… here is a solution to the problem.

Invalidates the context action bar when switching tabs

I’m looking for a way so that when the user switches tabs, the contextual action bar I have becomes ineffective.

I’ve set it up to link tabs to fragments, and each tab has a different contextual action bar. I guess there might be a way to do this in the onTabUnselected() method, but I’m not sure how.

Any help would be appreciated.

package com.androidDev.taskmanager;

import java.util.ArrayList;
import java.util.Locale;

import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.ListFragment;
import android.support.v4.view.ViewPager;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

public class Home extends FragmentActivity implements ActionBar.TabListener {
    private SectionsPagerAdapter mSectionsPagerAdapter;
    private ViewPager mViewPager;
    public static Menu mainMenu;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

 Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

 Create the adapter that will return a fragment for each of the three
         primary sections of the app.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

 Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);

 When swiping between different sections, select the corresponding
         tab. We can also use ActionBar.Tab#select() to do this if we have
         a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                    @Override
                    public void onPageSelected(int position) {
                        actionBar.setSelectedNavigationItem(position);
                    }
                });

 For each of the sections in the app, add a tab to the action bar.
        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
             Create a tab with text corresponding to the page title defined by
             the adapter. Also specify this Activity object, which implements
             the TabListener interface, as the callback (listener) for when
             this tab is selected.
            actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
        }
    }

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
         Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.home, menu);
        mainMenu = menu;
        return true;
    }

@Override
    public void onTabSelected(ActionBar.Tab tab,
            FragmentTransaction fragmentTransaction) {
         When the given tab is selected, switch to the corresponding page in
         the ViewPager.
        mViewPager.setCurrentItem(tab.getPosition());
    }

@Override
    public void onTabUnselected(ActionBar.Tab tab,FragmentTransaction fragmentTransaction) {
    }

@Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

public class SectionsPagerAdapter extends FragmentPagerAdapter {

public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

@Override
        public Fragment getItem(int position) {
             getItem is called to instantiate the fragment for the given page.
             Return a DummySectionFragment (defined as a static inner class
             below) with the page number as its lone argument.
            if(position==0){
                Fragment fragment = new ViewTaskFragment();
                return fragment;
            }
            else {
                Fragment fragment = new ViewReminderFragment();
                return fragment;
            }
        }

@Override
        public int getCount() {
             Show 2 total pages.
            return 2;
        }

@Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();
            switch (position) {
            case 0:
                return getString(R.string.title_tasks).toUpperCase(l);
            case 1:
                return getString(R.string.title_reminders).toUpperCase(l);
            }
            return null;
        }
    }

public static class ViewTaskFragment extends ListFragment {
        private final boolean POST_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES. HONEYCOMB;
        private SimpleCursorAdapter sca;
        private ListView lv;
        private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
             Called when the action mode is created; startActionMode() was called
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                 Inflate a menu resource providing context menu items
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.task_context_menu, menu);
                return true;
            }

 Called each time the action mode is shown. Always called after onCreateActionMode, but
             may be called multiple times if the mode is invalidated.
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;  Return false if nothing is done
            }

 Called when the user selects a contextual menu item
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.menu_delete:
                        MyDB db = new MyDB(getActivity());
                        SparseBooleanArray checked = lv.getCheckedItemPositions();
                        ArrayList<String> valueBuilder = new ArrayList<String>();
                        String[] values;
                        for (int i = 0; i < checked.size(); i++) {
                            if(checked.valueAt(i) == true) {
                                valueBuilder.add(String.valueOf(lv.getItemIdAtPosition(i)));
                            }
                        }
                        values = valueBuilder.toArray(new String[valueBuilder.size()]);
                        if(values!=null)
                            db.deleteTasks(values);
                        refreshListView();
                        mode.finish();  Action picked, so close the CAB
                        return true;
                    default:
                        return false;
                }
            }
             Called when the user exits the action mode
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                lv.clearChoices();
                for (int i = 0; i < lv.getChildCount(); i++)
                    lv.setItemChecked(i, false);
                mode = null;
            }
        };
        public ViewTaskFragment() {}
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstaceState) {
            final View rootView = inflater.inflate(R.layout.view_task, container, false);
            Context context = getActivity();
            hasOptionsMenu();
            String[] from = {"_id", "task_name", "completion_date"};
            int[] to={R.id.rpt_task_id, R.id.rpt_task_name, R.id.rpt_completion_date};
            MyDB db = new MyDB(context);
            sca = new SimpleCursorAdapter(context, R.layout.task_item, db.selectTasks(), from, to, 0);
            lv = (ListView) rootView.findViewById(android. R.id.list);
            lv.setAdapter(sca);
            lv.setLongClickable(true);
            lv.setOnItemLongClickListener(new OnItemLongClickListener() {
                    public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
                        Toast.makeText(getActivity(), "Running onItemLongClick Orig Code", Toast.LENGTH_SHORT).show();
                        if(POST_HONEYCOMB) {
                             getActivity(). startActionMode(mActionModeCallback);
                        }
                        else {
                            Needs at least honeycomb
                        }
                        return true;
                    }
                });
            return rootView;
        }

@Override
        public void onResume() {
            super.onResume();
            refreshListView();
        }
        public void refreshListView() {
            MyDB db = new MyDB(getActivity());
            sca.changeCursor(db.selectTasks());
        }

}
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
         Handle item selection
        Intent intent = null;
        switch (item.getItemId()) {
        case R.id.mnu_action_settings:
            intent = new Intent(this, Settings.class);
            startActivity(intent);
            return true;
        case R.id.mnu_add_reminder:
            intent = new Intent(this, AddReminder.class);
            startActivity(intent);  
            return true;
        case R.id.mnu_add_task:
            intent = new Intent(this, AddTask.class);
            startActivity(intent);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }
    /** New Fragment Section**/
    public static class ViewReminderFragment extends Fragment {
        private final boolean POST_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES. HONEYCOMB;
        private SimpleCursorAdapter sca;
        private ListView lv;
        private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
             Called when the action mode is created; startActionMode() was called
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                 Inflate a menu resource providing context menu items
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.task_context_menu, menu);
                return true;
            }

 Called each time the action mode is shown. Always called after onCreateActionMode, but
             may be called multiple times if the mode is invalidated.
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;  Return false if nothing is done
            }

 Called when the user selects a contextual menu item
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.menu_delete:
                        refreshListView();
                        mode.finish();  Action picked, so close the CAB
                        return true;
                    default:
                        return false;
                }
            }
             Called when the user exits the action mode
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                lv.clearChoices();
                for (int i = 0; i < lv.getChildCount(); i++)
                    lv.setItemChecked(i, false);
                mode = null;
            }
        };

public ViewReminderFragment() {}
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstaceState) {
            final View rootView = inflater.inflate(R.layout.view_reminder, container, false);
            Context context = getActivity();
            hasOptionsMenu();
            String[] from = {"_id", "task_name", "reminder_date"};
            int[] to={R.id.rpt_task_id, R.id.rpt_task_name, R.id.rpt_reminder_date};
            MyDB db = new MyDB(context);
            sca = new SimpleCursorAdapter(context, R.layout.reminder_item, db.selectReminder(), from, to, 0);
            lv = (ListView) rootView.findViewById(R.id.reminderList);
            lv.setAdapter(sca);
            lv.setLongClickable(true);
            lv.setOnItemLongClickListener(new OnItemLongClickListener() {
                    public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
                        if(POST_HONEYCOMB) {
                             getActivity(). startActionMode(mActionModeCallback);
                        }
                        else {
                            Needs at least honeycomb
                        }
                        return true;
                    }
                });
            return rootView;
        }

@Override
        public void onResume() {
            super.onResume();
            refreshListView();
        }
        public void refreshListView() {
            MyDB db = new MyDB(getActivity());
            sca.changeCursor(db.selectReminder());
        }
    }

Solution

I

think I came up with an interesting way not to “cheat”.

First, I created a FragmentTabListener interface so that activities let tab fragments know when they are selected or unchecked.

public interface FragmentTabListener {
    public void onTabSelected();

public void onTabUnselected();
}

Then, on the ActionBar.TabListener onTabUnselected() method, I can let each tab know when it is selected or unchecked.

ActionBar.TabListener tabListener = new ActionBar.TabListener() {

@Override
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        ((FragmentTabListener) MyFragmentForTab).onTabUnselected();
    }

I

created a different listener because I really don’t want to expose anything else to the tab, but only want to show the fact that it’s checked or not. In addition, the above is a slight pseudocode that hides irrelevant information.

Next, on the fragment used for a specific tab, I keep the actionMode when creating (and delete it on destroy).

@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
     Inflate the menu for the CAB
    ...
    MyFragmentForTab.this.mActionMode = mode;
    return true;
}

@Override
public void onDestroyActionMode(ActionMode mode) {
    MyFragmentForTab.this.mActionMode = null;
}

Finally, I only call when no tab is selected

if (this.mActionMode != null)
    this.mActionMode.finish();

Related Problems and Solutions