Summary of android2.x using ActionBar

Source: Internet
Author: User

Summary of android2.x using ActionBar

I haven't written a blog on csdn for a long time. Recently I had nothing to worry about. I decided to do something like the interface. I thought it was quite simple, but it took a long time to get the first ActionBar done, the process is very rough and can be recorded to share with you.

First, let's introduce my mobile phone. My mobile phone is android2.3.4. There are two options to use ActionBar. One is to use the well-known open-source component: ActionBarSherlock; one is to use google's own android-support-v7 package; ActionBarSherlock since google launched the android-support-v7 package, basically to exit the historical stage, so I decided to use a android-support-v7 to try.

First download the android-support-v7-appcompat (including jar packages and resource projects), create the weixin Project (note the minimum sdk version requirements), introduce the android-support-v7-appcompat.jar (put the jar package under the lib directory, refresh the project ), and the android-support-v7-appcompat resource project as a class library into the project, the project results are as follows:

Create a MainActivity and inherit the android. support. v7.app. ActionBarActivity class. The Code is as follows:

 

package com.example.weixin;import android.os.Bundle;import android.support.v7.app.ActionBarActivity;import android.view.Menu;public class MainActivity extends ActionBarActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}}
Modify resaluesstyles. xml to use the Theme. AppCompat. Light. DarkActionBar style of the androi-support-v7, as shown below

 

     
  
      
  
  
 
Modify resmenumain. xml as follows:

 

              

 

To resolve this issue, because there were no showAsAction, actionProviderClass, actionViewClass, and other attributes before android3.0, we need to introduce the namespace: xmlns: xxx = Hangzhou. 

 

 

After this setting, we should be able to run it in the android2.3 environment.

Android2.3 running effect:

The ActionBar does not have the overflow button. Click the physical menu key and the context menu appears below. This is not what we want. See android doc, the display status of the overflow button is related to the hardware status of the mobile phone. If the mobile phone does not have the physical Menu key, the overflow button can be displayed. If there is a physical Menu key, the overflow button is not displayed. To change the default behavior, there is a static variable named sHasPermanentMenuKey in the ViewConfiguration class, the system determines whether the mobile phone has a physical Menu key based on the value of this variable. Of course, this is an internal variable. We cannot directly access it, but we can modify its value through reflection so that it can always be set to false. Add a method to MainActivity

 

private void setOverflowShowingAlway(){try{ViewConfiguration config = ViewConfiguration.get(this);Field menuKeyField = ViewConfiguration.class.getDeclaredField(sHasPermanentMenuKey);menuKeyField.setAccessible(true);menuKeyField.setBoolean(config, false);}catch(Exception e){e.printStackTrace();}}
And call it in the onCreate (Bundle savedInstanceState) method.

 

 

 

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);setOverflowShowingAlway();}

Run the task again. In the android4.0 environment, the overflow button is displayed, and pressing the physical menu key is displayed normally. However, only text is displayed in the displayed menu, but no icon is displayed, this is the official default effect. Google believes that Action buttons hidden in overflow should only display text. In fact, Action Buttons in overflow should not display icons, it is determined by the setOptionalIconsVisible method of the MenuBuilder class. If we pass true to this method when overflow is expanded, the icon corresponding to each Action button will be displayed, and the method will be added to MainActivity.

 

 

  private void setOptionalIconsVisible(Menu menu){if(menu == null) return;if(!menu.getClass().getSimpleName().equals(MenuBuilder)) return;try {              Method m = menu.getClass().getDeclaredMethod(setOptionalIconsVisible, Boolean.TYPE);              m.setAccessible(true);              m.invoke(menu, true);          } catch (Exception e) {          } }@Override  public boolean onMenuOpened(int featureId, Menu menu) { if(featureId == Window.FEATURE_ACTION_BAR){setOptionalIconsVisible(menu);}    return super.onMenuOpened(featureId, menu); }

Run again and find that the android4.0 environment is running normally. The running effect is as follows:

When I was happy, I started running in the android2.3 environment and found that the android2.3 environment had not changed, the overflow button did not come out, and the context menu appeared below after pressing the physical menu key, the following error occurs in logcat:

 

03-06 13:57:51.587: W/System.err(328): java.lang.NoSuchFieldException: sHasPermanentMenuKey03-06 13:57:51.606: W/System.err(328): at java.lang.ClassCache.findFieldByName(ClassCache.java:446)03-06 13:57:51.616: W/System.err(328): at java.lang.Class.getDeclaredField(Class.java:666)03-06 13:57:51.626: W/System.err(328): at com.example.weixin.MainActivity.setOverflowShowingAlway(MainActivity.java:32)03-06 13:57:51.636: W/System.err(328): at com.example.weixin.MainActivity.onCreate(MainActivity.java:19)03-06 13:57:51.636: W/System.err(328): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)03-06 13:57:51.647: W/System.err(328): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1586)03-06 13:57:51.656: W/System.err(328): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)03-06 13:57:51.656: W/System.err(328): at android.app.ActivityThread.access$1500(ActivityThread.java:117)03-06 13:57:51.667: W/System.err(328): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)03-06 13:57:51.676: W/System.err(328): at android.os.Handler.dispatchMessage(Handler.java:99)03-06 13:57:51.696: W/System.err(328): at android.os.Looper.loop(Looper.java:123)03-06 13:57:51.707: W/System.err(328): at android.app.ActivityThread.main(ActivityThread.java:3647)03-06 13:57:51.717: W/System.err(328): at java.lang.reflect.Method.invokeNative(Native Method)03-06 13:57:51.736: W/System.err(328): at java.lang.reflect.Method.invoke(Method.java:507)03-06 13:57:51.746: W/System.err(328): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)03-06 13:57:51.756: W/System.err(328): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)03-06 13:57:51.756: W/System.err(328): at dalvik.system.NativeStart.main(Native Method)

No sHasPermanentMenuKey class member found in ViewConfiguration! Before Android, ViewConfiguration did not have members of the sHasPermanentMenuKey class. What should I do? No matter du Niang or google, the solution to this error cannot be found and can only be tracked from the sdk source code.

 

 

First, find out which ViewConfiguration is used in android4.0. hasPermanentMenuKey () method, analyzed in the class com. android. internal. view. showsOverflowMenuButton () in ActionBarPolicy is used. The Code is as follows:

 

    public boolean showsOverflowMenuButton() {        return !ViewConfiguration.get(mContext).hasPermanentMenuKey();    }

Where is the showsOverflowMenuButton () method of the ActionBarPolicy class used? It was originally used in the initForMenu (Context context, MenuBuilder menu) method of com. android. internal. view. menu. ActionMenuPresenter class.

 

@ Override public void initForMenu (Context context, MenuBuilder menu) {super. initForMenu (context, menu); final Resources res = context. getResources (); final ActionBarPolicy abp = ActionBarPolicy. get (context); if (! MReserveOverflowSet) {mReserveOverflow = abp. showsOverflowMenuButton ();} if (! MWidthLimitSet) {mWidthLimit = abp. getEmbeddedMenuWidthLimit ();} // Measure for initial configuration if (! MMaxItemsSet) {mMaxItems = abp. getMaxActionButtons ();} int width = mWidthLimit; if (mReserveOverflow) {if (mOverflowButton = null) {// create overflow button mOverflowButton = new OverflowMenuButton (mSystemContext ); final int spec = MeasureSpec. makeMeasureSpec (0, MeasureSpec. UNSPECIFIED); mOverflowButton. measure (spec, spec);} width-= mOverflowButton. getMeasuredWidth ();} else {mOverflowButton = null;} mActionItemWidthLimit = width; mMinCellSize = (int) (ActionMenuView. MIN_CELL_SIZE * res. getDisplayMetrics (). density); // Drop a scrap view as it may no longer reflect the proper context/config. mScrapActionButtonView = null ;}
In the original ActionBarMenu initialization, it is based on showsOverflowMenuButton () returned results to determine whether to create the overflow button, and in the android-support-v7-appcompat there are class ActionBarPolicy and ActionMenuPresenter, the initForMenu method of ActionMenuPresenter is similar to the android4.0 code, but the showsOverflowMenuButton () Code of ActionBarPolicy is as follows:

 

 

 

  public boolean showsOverflowMenuButton()  {    return Build.VERSION.SDK_INT >= 11;  }
If the sdk version is later than 11 (the version is later than android3.0), true is returned. Otherwise, false is returned. It seems that it is impossible to modify the showsOverflowMenuButton () Return Value of the ActionBarPolicy class through the reflection method. Continue to the analysis and check where the initForMenu method of the ActionMenuPresenter class is called. The result is analyzed, it was originally in the android class. support. v7.internal. widget. the configPresenters (MenuBuilder builder) method of ActionBarView is called, while the configPresenters method is in setMenu (SupportMenu menu, MenuPresenter. callback cb), the source code is as follows:
    public void setMenu(Menu menu, MenuPresenter.Callback cb) {        if (menu == mOptionsMenu) return;        if (mOptionsMenu != null) {            mOptionsMenu.removeMenuPresenter(mActionMenuPresenter);            mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter);        }        MenuBuilder builder = (MenuBuilder) menu;        mOptionsMenu = builder;        if (mMenuView != null) {            final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();            if (oldParent != null) {                oldParent.removeView(mMenuView);            }        }        if (mActionMenuPresenter == null) {            mActionMenuPresenter = new ActionMenuPresenter(mContext);            mActionMenuPresenter.setCallback(cb);            mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();        }        ActionMenuView menuView;        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,                LayoutParams.MATCH_PARENT);        if (!mSplitActionBar) {            mActionMenuPresenter.setExpandedActionViewsExclusive(                    getResources().getBoolean(                    com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));            configPresenters(builder);            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);            final ViewGroup oldParent = (ViewGroup) menuView.getParent();            if (oldParent != null && oldParent != this) {                oldParent.removeView(menuView);            }            addView(menuView, layoutParams);        } else {            mActionMenuPresenter.setExpandedActionViewsExclusive(false);            // Allow full screen width in split mode.            mActionMenuPresenter.setWidthLimit(                    getContext().getResources().getDisplayMetrics().widthPixels, true);            // No limit to the item count; use whatever will fit.            mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);            // Span the whole width            layoutParams.width = LayoutParams.MATCH_PARENT;            configPresenters(builder);            menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);            if (mSplitView != null) {                final ViewGroup oldParent = (ViewGroup) menuView.getParent();                if (oldParent != null && oldParent != mSplitView) {                    oldParent.removeView(menuView);                }                menuView.setVisibility(getAnimatedVisibility());                mSplitView.addView(menuView, layoutParams);            } else {                // We'll add this later if we missed it this time.                menuView.setLayoutParams(layoutParams);            }        }        mMenuView = menuView;    }    private void configPresenters(MenuBuilder builder) {        if (builder != null) {            builder.addMenuPresenter(mActionMenuPresenter);            builder.addMenuPresenter(mExpandedMenuPresenter);        } else {            mActionMenuPresenter.initForMenu(mContext, null);            mExpandedMenuPresenter.initForMenu(mContext, null);            mActionMenuPresenter.updateMenuView(true);            mExpandedMenuPresenter.updateMenuView(true);        }    }

Look at the bold part of the red text. This is the process of instantiating the ActionMenuPresenter method! You only need to modify the value of mActionMenuPresenter in ActionBarView. The setOverflowShowingAlway () method for modifying MainActivity is as follows:

Private void setOverflowShowingAlway () {try {if (Build. VERSION. SDK_INT> = Build. VERSION_CODES.HONEYCOMB) {// ViewConfiguration config = ViewConfiguration in Versions later than android3.0. get (this); Field menuKeyField = ViewConfiguration. class. getDeclaredField (sHasPermanentMenuKey); menuKeyField. setAccessible (true); menuKeyField. setBoolean (config, false);} else {// get actionBarView final ActionBarView ActionBarView = (ActionBarView) findViewById (android. support. v7.appcompat. r. id. action_bar); MenuPresenter. callback menuCallback = new MenuPresenter. callback () {@ Overridepublic boolean onOpenSubMenu (MenuBuilder subMenu) {android. view. window. callback cb = getWindow (). getCallback (); if (cb! = Null) {cb. onMenuOpened (Window. FEATURE_ACTION_BAR, subMenu); return true;} return false;} @ Overridepublic void onCloseMenu (MenuBuilder menu, boolean allMenusAreClosing) {comment () ;}}; Field menuPresenterField = actionBarView. getClass (). getSuperclass (). getDeclaredField (mActionMenuPresenter); menuPresenterField. setAccessible (true); final ActionMenuPresenter menuPresenter = new ActionMenuPresenter (this); menuPresenter. setReserveOverflow (true); // This sentence is critical. Set overflow to see menuPresenter. setCallback (menuCallback); menuPresenter. setId (android. support. v7.appcompat. r. id. action_menu_presenter); // modify actionBarView. mActionMenuPresenter = menuPresenter; menuPresenterField. set (actionBarView, menuPresenter); Field expandedMenuPresenterField = actionBarView. getClass (). getDeclaredField (mExpandedMenuPresenter); expandedMenuPresenterField. setAccessible (true); Constructor contructor = Class. forName (android. support. v7.internal. widget. actionBarView $ ExpandedActionViewMenuPresenter ). getDeclaredConstructor (actionBarView. getClass (); contructor. setAccessible (true); Object expandedMenuPresenter = contructor. newInstance (actionBarView); // modify actionBarView. mExpandedMenuPresenter = expandedMenuPresenter; expandedMenuPresenterField. set (actionBarView, expandedMenuPresenter) ;}} catch (Exception e) {e. printStackTrace ();}}

The above code first checks whether the sdk version is a version later than android3.0. If so, the sHasPermanentMenuKey value of ViewConfiguration is directly reflected; otherwise, the ActionBarView is searched and the mActionMenuPresenter and mpandedmenupresenter values of the object are, among them, mActionMenuPresenter needs to set setReserveOverflow (true), that is, mReserveOverflow = true and mReserveOverflowSet = true of ActionMenuPresenter. This ensures that the program runs the initForMenu method of actionmenupresen, without z, the return value of showsOverflowMenuButton () of ActionBarPolicy is obtained as the basis for determining whether to add the overflow button.

 

 

Run the task again. You can click the overflow button and the overflow button appears. However, only the text icon is displayed, and the physical menu key is displayed below. After analysis, the original class com. android. internal. policy. impl. phoneWindow does not have any ActionBar code, but many ActionBar codes have been added in Versions later than android3.0. The onMenuOpened (int featureId, Menu menu) method of MainActivity is as follows:

@ Override public boolean onMenuOpened (int featureId, Menu menu) {if (Build. VERSION. SDK_INT> = Build. VERSION_CODES.HONEYCOMB) {// For Versions later than android3.0, the menu parameter is not empty. You can directly modify the value of setOptionalIconsVisible through the reflection method if (featureId = Window. FEATURE_ACTION_BAR) {setOptionalIconsVisible (menu);} return super. onMenuOpened (featureId, menu);} else {ActionBarView actionBarView = (ActionBarView) findViewById (android. support. v7.appcompat. r. id. action_bar); // when you click overflowButton, the passed featureId parameter is FEATURE_ACTION_BAR. by clicking the menu button, the passed featureId parameter is FEATURE_OPTIONS_PANELif (featureId = Window. FEATURE_ACTION_BAR) {// version earlier than android3.0. Due to the phonewindow mechanism, the imported menu is empty. You need to assign the menu value to ActionMenuPresenter. mMenuif (menu = null) {try {Field menuPresenterField = actionBarView. getClass (). getSuperclass (). getDeclaredField (mActionMenuPresenter); menuPresenterField. setAccessible (true); ActionMenuPresenter menuPresenter = (ActionMenuPresenter) menuPresenterField. get (actionBarView); Field menuField = BaseMenuPresenter. class. getDeclaredField (mMenu); menuField. setAccessible (true); menu = (Menu) menuField. get (menuPresenter);} catch (Exception e) {e. printStackTrace () ;}} setOptionalIconsVisible (menu); return super. onMenuOpened (featureId, menu);} else {actionBarView. showOverflowMenu (); // display the drop-down menu to the ActionBar and return false ;}}}

The above code first checks whether the sdk version is a version later than android3.0. If so, it directly reflects the setOptionalIconsVisible of the modified menu; otherwise, it determines whether the passed parameter featureId is equal to Window. FEATURE_ACTION_BAR is used to determine whether the user is triggered by clicking the overflow button or the physical menu key. If it is triggered by the overflow button, the featureId is Window. FEATURE_ACTION_BAR, but menu = null, then assign a value to the menu according to the emission method, and modify setOptionalIconsVisible = true of the menu. If it is triggered by clicking the physical menu key, its featureId is Window. FEATURE_OPTIONS_PANEL, call the showOverflowMenu () method of ActionBarView and link the menu to overflow.

 

 

Now all the modifications have been completed. Let's take a look at the Running Effect in the android2.3 environment:

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.