Can I disable ActionBar's navigation Spinner?

2019-04-09 21:01发布

What I want to achieve is disabling all items on ActionBar except one. I have a custom ActionBar with Menu,several TextViews, one Button and a Spinner from ListNavigation. Spinner is created because of bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); like this :

    SpinnerAdapter spinnerAdapter = new ArrayAdapter<String>(this.getActivity(), R.layout.action_bar_spinner, names);
    // "listener" for spinner
    bar.setListNavigationCallbacks(spinnerAdapter, new OnNavigationListener() {
        @Override
        public boolean onNavigationItemSelected(int position, long itemId) {
            // do some stuff

            return true;
        }
    });

I want to disable the Spinner, so that it is inactive, but still visible with last item selected before disabling it.In short I need something like bar.getNavigationSpinner.setEnabled(false) if it existed. Question is: Is there some kind of workaround ? If not, is there a way to disable whole ActionBar,but keep it visible ?

Note: I want to achieve it in a Fragment.

3条回答
你好瞎i
2楼-- · 2019-04-09 21:33

This can be simplified by noting that the ActionBar (and Spinner) is between the root view and the content (with id == android.R.id.content). So you can implement a breadth-first search and return the first Spinner you find, no longer adding to the queue once you hit the view with id == android.R.id.content.

Or do the depth-first search, but stop recursing when you hit the view with id == android.R.id.content as the Spinner isn't contained in there.

So there's no need to maintain any list, just return the first Spinner found.

查看更多
beautiful°
3楼-- · 2019-04-09 21:39

The above code does not work with Action Bar compat (v7 support library). I needed to enable/disable the action bar spinner of my app (API 2.2) using this support library but I have noticed that the name of the class is SpinnerICS instead Spinner.

The full package name is

android.support.v7.internal.widget.SpinnerICS

Because of this class is not visible and can not be imported in my code, I can not use:

if (child instanceof SpinnerICS) ...

So finally I solved my issue using getClass().getName. Therefore the code for enabling/disabling the spinner using Action Bar compat support library (v7) will be:

private List<View> traverseViewChildren(ViewGroup parent) {
    List<View> spinners = new ArrayList<View>();
    for (int i = 0; i < parent.getChildCount(); i++) {
        View child = parent.getChildAt(i);

        String className = child.getClass().getName();
        if (child instanceof Spinner || className.equals("android.support.v7.internal.widget.SpinnerICS")) {
            spinners.add( child );
        } else if (child instanceof ViewGroup) {
            spinners.addAll( traverseViewChildren( (ViewGroup) child ) );
        }
    }
    return spinners;
}

Also, I have modified my findListNavigationSpinner method to check if the spinners list has elements to prevent ArrayIndexOutOfBoundsException. Because I only have one spinner in my app (in the Action Bar compat), so my code is this (you should adapt it if you would have more than one spinner):

private View findListNavigationSpinner(List<View> spinners) {
    // We only have one spinner in the app
    View result = null;
    if (spinners != null && spinners.size() > 0) {
         result = spinners.get(0);
    }
    return result;
}
查看更多
淡お忘
4楼-- · 2019-04-09 21:54

Yes, it is possible to disable the Spinner used in list navigation in ActionBar. But is't not a straightforward solution, rather a hack. ActionBar doesn't provide a direct access to the Spinner view. Unfortunately the Spinner is created in a private code without any id.

So how to access the Spinner instance? One solution could be to access it via Java reflection API, but I wouldn't recommend that.

A better solution is to get the root view for the current Activity, traverse it's child views (action bar and all it's views are present in the view hierarchy) and find the proper Spinner. Since the Spinner in action bar is presumably the only one you haven't created yourself, you should be able to distinguish it from the others.

Getting the root View is described in this SO question.

The traversal is rather simple, just bear in mind that if you are using ActionBarSherlock, you have to look for IcsSpinner instead of Spinner (IcsSpinner does not extend from Spinner).

private View findActionBarSpinner() {
    View rootView = findViewById(android.R.id.content).getRootView();
    List<View> spinners = traverseViewChildren( (ViewGroup) rootView );
    return findListNavigationSpinner(spinners); // implement according to your layouts
}

private List<View> traverseViewChildren(ViewGroup parent) {
    List<View> spinners = new ArrayList<View>();
    for (int i = 0; i < parent.getChildCount(); i++) {
        View child = parent.getChildAt(i);
        if (child instanceof Spinner) {
            spinners.add( child );
        } else if (child instanceof IcsSpinner) { // add this if you are using ActionBarSherlock
            spinners.add( child );
        } else if (child instanceof ViewGroup) {
            spinners.addAll( traverseViewChildren( (ViewGroup) child ) );
        }
    }
    return spinners;
}

The function findListNavigationSpinner should be implemented in a way that you are able to distinguish the other spinners. If you are not using any Spinner (or any view derived from it), the returned list should contain just one item.

The code above describes how to get the Spinner in an Activity. Naturally, it is not a problem to disable the Spinner from within a Fragment. The fragment has a reference to it's activity, so the activity can expose the code to the fragment via some interface.

查看更多
登录 后发表回答