Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Action bar icon size in Android 4.2

Has the action bar icon size changed in Android 4.2 ? I've had a 120x48px HDPI icon that was rendered perfectly in Android 4.1 and below. It still is.

However, on any 4.2 device, it is squelched to fit as 48x48px from what I can see. Or something like that; it's definitely a square.

Any ideas ? Thanks !

like image 801
Bogdan Zurac Avatar asked Nov 22 '12 20:11

Bogdan Zurac


2 Answers

This is not ideal, but it appears you can get around this limitation by using an custom action view.

Something like this:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@+id/log_in"
    android:title="Login"
    android:showAsAction="always"
    android:actionLayout="@layout/log_in_button"/>
</menu>

Where @layout/log_in_button points to a layout file containing an ImageButton with your icon set as the src.

You will have to bind the click listener in the OnCreateOptionsMenu method. There is a halfway good example here: http://developer.android.com/guide/topics/ui/actionbar.html#ActionView.

I only just learned to use this method, so I don't know yet if there are any major downfalls besides the increased complexity.

like image 79
craigrs84 Avatar answered Nov 16 '22 02:11

craigrs84


So, I found an answer, it's kinda hacky but works (TM):

The general idea is to listen for the layout changes and apply new bounds to the drawables. This could look like this:

public static void updateActionBar(final Activity activity) {
        if (Build.VERSION.SDK_INT >= 17) {
            try {
                final View content = activity.findViewById(android.R.id.content);
                if (content instanceof FrameLayout) {
                    final FrameLayout contentFrameLayout = (FrameLayout) content;
                    final ViewParent parent = contentFrameLayout.getParent();
                    if (parent instanceof LinearLayout) {
                        final LinearLayout parentLinearLayout = (LinearLayout) parent;
                        final Class<?> actionBarContainerClass = Class.forName("com.android.internal.widget.ActionBarContainer");
                        final Class<?> actionBarViewClass = Class.forName("com.android.internal.widget.ActionBarView");
                        final Class<?> actionMenuViewClass = Class.forName("com.android.internal.view.menu.ActionMenuView");
                        final Class<?> actionMenuItemViewClass = Class.forName("com.android.internal.view.menu.ActionMenuItemView");

                        for (int i = 0, childCount = parentLinearLayout.getChildCount(); i < childCount; i++) {
                            final View parentLinearLayoutChild = parentLinearLayout.getChildAt(i);
                            handleParentLinearLayoutChild(actionBarContainerClass, actionBarViewClass, actionMenuViewClass, actionMenuItemViewClass, parentLinearLayoutChild);
                        }
                    }
                }
            } catch (Exception e) {
                // Handle or ignore
            }
        }
    }

    private static void handleParentLinearLayoutChild(final Class<?> actionBarContainerClass, final Class<?> actionBarViewClass, final Class<?> actionMenuViewClass, final Class<?> actionMenuItemViewClass, final View parentLinearLayoutChild) {
        if (parentLinearLayoutChild instanceof FrameLayout && parentLinearLayoutChild.getClass().equals(actionBarContainerClass)) {
            final FrameLayout actionBarContainer = (FrameLayout) parentLinearLayoutChild;
            for (int i = 0, actionBarContainerChildCount = actionBarContainer.getChildCount(); i < actionBarContainerChildCount; i++) {
                final View actionBarContainerChild = actionBarContainer.getChildAt(i);
                handleActionBarContainerChild(actionBarViewClass, actionMenuViewClass, actionMenuItemViewClass, actionBarContainerChild);
            }
        }
    }

    private static void handleActionBarContainerChild(final Class<?> actionBarViewClass, final Class<?> actionMenuViewClass, final Class<?> actionMenuItemViewClass, final View actionBarContainerChild) {
        if (actionBarContainerChild instanceof ViewGroup && actionBarContainerChild.getClass().equals(actionBarViewClass)) {
            final ViewGroup actionBarView = (ViewGroup) actionBarContainerChild;
            actionBarView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
                @Override
                public void onChildViewAdded(final View parent, final View child) {
                    handleActionBarViewChild(child, actionMenuViewClass, actionMenuItemViewClass);
                }

                @Override
                public void onChildViewRemoved(final View parent, final View child) {
                }
            });
            for (int i = 0, actionBarViewCount = actionBarView.getChildCount(); i < actionBarViewCount; i++) {
                handleActionBarViewChild(actionBarView.getChildAt(i3), actionMenuViewClass, actionMenuItemViewClass);
            }
        }
    }

    private static void handleActionBarViewChild(final View child, final Class<?> actionMenuViewClass, final Class<?> actionMenuItemViewClass) {
        try {
            if (child instanceof LinearLayout && child.getClass().equals(actionMenuViewClass)) {
                final LinearLayout actionMenuView = (LinearLayout) child;
                actionMenuView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
                    @Override
                    public void onChildViewAdded(final View parent, final View child) {
                        handleActionMenuViewChild(child, actionMenuItemViewClass);
                    }

                    @Override
                    public void onChildViewRemoved(final View parent, final View child) {
                    }
                });
                for (int i = 0, actionMenuViewCount = actionMenuView.getChildCount(); i < actionMenuViewCount; i++) {
                    handleActionMenuViewChild(actionMenuView.getChildAt(i), actionMenuItemViewClass);
                }
            }
        } catch (Exception e) {
            // Handle or ignore
        }
    }

    private static void handleActionMenuViewChild(final View child, final Class<?> actionMenuItemViewClass) {
        try {
            if (child instanceof TextView && child.getClass().equals(actionMenuItemViewClass)) {
                final TextView menuViewChild = (TextView) child;
                final Drawable[] compoundDrawables = menuViewChild.getCompoundDrawables();
                final Drawable leftDrawable = compoundDrawables[0];
                final int intrinsicWidth = leftDrawable.getIntrinsicWidth();
                final int intrinsicHeight = leftDrawable.getIntrinsicHeight();
                leftDrawable.setBounds(0, 0, intrinsicWidth , intrinsicHeight );
                menuViewChild.setCompoundDrawables(leftDrawable, null, null, null);
                menuViewChild.setPadding(menuViewChild.getPaddingLeft(), 0, menuViewChild.getPaddingRight(), 0);
                menuViewChild.invalidate();
                menuViewChild.requestLayout();
            }
        } catch (Exception e) {
            // Handle or ignore
        }
    }

You then have to call updateActionBar in every Activity (I suggest making an abstract base activity from which you extend) in the following callbacks: onCreate onMenuOpened (I found that it would improve performance and reduce flickering (size changes of the drawables) if you call this delayed (e.g. 200ms)) onPrepareOptionsMenu (I found that it would improve performance and reduce flickering (size changes of the drawables) if you call this delayed (e.g. 200ms))

This works for me on Nexus 7 and Nexus 10 with Android 4.2. You can expect it to fail with future updates but at least for now it seems to work.

like image 23
Hameno Avatar answered Nov 16 '22 02:11

Hameno