編輯:關於Android編程
好久沒有在csdn上寫博客了,最近閒來無事,決定仿微信界面做幾個東西,原本以為挺簡單的事情,結果折騰了好久才把第一步的ActionBar搞定,其中過程可謂坎坷之極,記錄下來,以便給各位分享。
首先介紹一下我的手機,我的手機是android2.3.4的系統,要使用ActionBar,有兩種選擇,一個是使用大名鼎鼎的開源組件:ActionBarSherlock;一個是使用google自己出的android-support-v7包;ActionBarSherlock自從google推出android-support-v7包以後,基本上要退出歷史舞台了,因此我決定使用android-support-v7一試。
首先下載android-support-v7-appcompat(包括jar包和資源項目),新建項目weixin(注意最低sdk版本要求),引入android-support-v7-appcompat.jar(把該jar包放到lib目錄下,刷新工程),並把android-support-v7-appcompat資源項目作為類庫引入到項目中,項目結果如下

新建MainActivity,繼承類android.support.v7.app.ActionBarActivity,代碼如下:
package com.example.weixin;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
public class MainActivity extends ActionBarActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
修改resaluesstyles.xml,使用androi-support-v7的Theme.AppCompat.Light.DarkActionBar風格,如下
修改resmenumain.xml,如下:
解析一下,由於android3.0之前,並沒有showAsAction、actionProviderClass和actionViewClass等屬性,因此需要引入命名空間:xmlns:xxx=http://schemas.android.com/apk/res-auto,使用android-support-v7-appcompat包的屬性,因此後面的menuitem的統一使用xxx:showAsAction、xxx:actionProviderClass和xxx:actionViewClass而不是android:showAsAction、android:actionProviderClass和android:actionViewClass;另外搜索按鈕的actionViewClass,在android3.0以後可以使用android.widget.SearchView,但在android3.0之前該類並不存在,需要使用android.support.v7.widget.SearchView
經過這樣設置以後,按理來說就應該可以了,我們在android2.3環境下運行試試
android2.3運行效果:

發現ActionBar沒有overflow按鈕,點擊物理menu鍵在下面出現了上下文菜單,這並不是我們想要的效果,查看android doc可知,overflow按鈕的顯示情況和手機的硬件情況是有關系的,如果手機沒有物理Menu鍵的話,overflow按鈕就可以顯示, 如果有物理Menu鍵的話,overflow按鈕就不會顯示出來,要改變這個默認行為,在ViewConfiguration這個類中, 有一個叫做sHasPermanentMenuKey的靜態變量,系統就是根據這個變量的值來判斷手機有沒有物理Menu鍵的。當然這是一個內部變量,我們無法直接訪問它,但是可以通過反射的方式修改它的值,讓它永遠為false就可以了。在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();
}
}
並在onCreate(Bundle savedInstanceState)方法中調用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setOverflowShowingAlway();
}
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);
}
重新運行,發現android4.0環境下已經運行正常了,運行效果如下:

正在高興之余,打開android2.3環境下運行,發現android2.3環境並沒有任何變化,overflow按鈕沒有出來,且按下物理menu鍵以後再下面出現了上下文菜單,在logcat中出現以下錯誤:
03-06 13:57:51.587: W/System.err(328): java.lang.NoSuchFieldException: sHasPermanentMenuKey 03-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)
首先查找android4.0版本中看看在哪使用了ViewConfiguration.hasPermanentMenuKey()方法,經過分析是在類com.android.internal.view.ActionBarPolicy中的showsOverflowMenuButton()使用了,代碼如下:
public boolean showsOverflowMenuButton() {
return !ViewConfiguration.get(mContext).hasPermanentMenuKey();
}
@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) { // 創建overflow按鈕
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;
}
原來在ActionBarMenu初始化時,是根據showsOverflowMenuButton()的返回結果來決定是否創建overflow按鈕的,而在android-support-v7-appcompat中也有類ActionBarPolicy和ActionMenuPresenter,其中類ActionMenuPresenter的initForMenu方法與android4.0版本代碼差不多,但是類ActionBarPolicy的showsOverflowMenuButton()代碼卻如下:
public boolean showsOverflowMenuButton()
{
return Build.VERSION.SDK_INT >= 11;
}
判斷sdk的版本大於11(版本大於android3.0)即返回true,否則返回false。看來通過反射方法修改類ActionBarPolicy的showsOverflowMenuButton()返回值是不太可能了,繼續往下分析,查看一下類ActionMenuPresenter的initForMenu方法在哪被調用了,結果分析,原來是在類android.support.v7.internal.widget.ActionBarView的configPresenters(MenuBuilder builder)方法中調用的,而configPresenters方法又是在setMenu(SupportMenu menu, MenuPresenter.Callback cb)中調用的,源代碼如下:
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);
}
}
看紅字加粗部分,這是實例化ActionMenuPresenter方法的過程,原來如此!我們只要把ActionBarView中修改mActionMenuPresenter的值即可,廢話少說,修改MainActivity的setOverflowShowingAlway()方法如下:
private void setOverflowShowingAlway(){
try{
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ // android3.0以後的版本才存在sHasPermanentMenuKey屬性
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class
.getDeclaredField(sHasPermanentMenuKey);
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
} else {
// 獲取actionBarView
final ActionBarView actionBarView = (ActionBarView) findViewById(android.support.v7.appcompat.R.id.action_bar);
MenuPresenter.Callback menuCallback = new MenuPresenter.Callback() {
@Override
public 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;
}
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
closeOptionsMenu();
}
};
Field menuPresenterField = actionBarView.getClass().getSuperclass().getDeclaredField(mActionMenuPresenter);
menuPresenterField.setAccessible(true);
final ActionMenuPresenter menuPresenter = new ActionMenuPresenter(this);
menuPresenter.setReserveOverflow(true); // 這句是關鍵,設置overflow可見
menuPresenter.setCallback(menuCallback);
menuPresenter.setId(android.support.v7.appcompat.R.id.action_menu_presenter);
// 修改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);
// 修改actionBarView.mExpandedMenuPresenter=expandedMenuPresenter;
expandedMenuPresenterField.set(actionBarView, expandedMenuPresenter);
}
} catch(Exception e){
e.printStackTrace();
}
}
以上代碼首先判斷sdk版本是否是android3.0之後的版本,如果是則直接反射修改ViewConfiguration的sHasPermanentMenuKey的值;否則查找出ActionBarView,替換該對象的mActionMenuPresenter及mExpandedMenuPresenter的值,其中mActionMenuPresenter需要設置setReserveOverflow(true),也就是ActionMenuPresenter的mReserveOverflow=true,mReserveOverflowSet=true,這樣就能保證程序在運行到ActionMenuPresenter的initForMenu方法時,不z再獲取ActionBarPolicy的showsOverflowMenuButton()的返回值作為判斷是否增加overflow按鈕的依據。
重新運行,呵呵,overflow按鈕可以出來了,點擊overflow按鈕也出現了下來菜單,但是菜單只有文字沒有顯示圖標,並且按物理menu鍵菜單還是顯示在下邊。經過分析,原來是android3.0之前的類com.android.internal.policy.impl.PhoneWindow並沒有任何ActionBar的代碼,而在android3.0以後的版本中增加了不少ActionBar代碼,修改MainActivity的onMenuOpened(int featureId, Menu menu)方法如下:
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ // android3.0以後的版本,參數menu不為空,直接通過反射方法修改setOptionalIconsVisible的值
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);
// 通過點擊overflowButton時,傳入的參數featureId為FEATURE_ACTION_BAR,而通過點擊menu按鍵,傳入的參數featureId為FEATURE_OPTIONS_PANEL
if(featureId == Window.FEATURE_ACTION_BAR){
// android3.0之前的版本,由於phonewindow的機制,導致傳進的menu是空的,需要把menu賦值為ActionMenuPresenter.mMenu
if(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(); // 把下拉菜單顯示到ActionBar上
return false;
}
}
}
以上代碼首先判斷首先判斷sdk版本是否是android3.0之後的版本,如果是則直接反射修改menu的setOptionalIconsVisible;否則根據傳入的參數featureId是否等於Window.FEATURE_ACTION_BAR來判斷用戶是通過點擊overflow按鈕還是點擊物理menu鍵觸發的,如果是通過overflow按鈕觸發的,則其featureId為Window.FEATURE_ACTION_BAR,但是menu=null,後面根據發射方法對menu進行賦值,並修改menu的setOptionalIconsVisible=true;如果是通過點擊物理menu鍵觸發的,則其featureId為Window.FEATURE_OPTIONS_PANEL,則調用ActionBarView的showOverflowMenu()方法,把菜單掛接到overflow下。
至此所有修改已完成,我們看一下在android2.3環境下運行效果:

Android 使用開源庫加載網絡圖片
Android 使用開源庫加載網絡圖片,使用開源庫加載圖片。單擊listview彈出popupwindow彈出框詳情查看:Android 單擊listview彈出popu
安卓環形菜單(處理了菜單鍵和返回鍵+加入了動畫)
廢話不多說,先看效果圖: package com.example.circlemenuofbottom.anim;import android.v
Android上使用ZXing識別條形碼與二維碼的方法
目前有越來越多的手機具備自動對焦的拍攝功能,這也意味著這些手機可以具備條碼掃描的功能。手機具備條碼掃描的功能,可以優化購物流程,快速存儲電子名片(二維碼)等。本文所述實例
Android BLE開發之Android手機搜索iBeacon基站
本文來自http://blog.csdn.net/hellogv/ ,引用必須注明出處! 上次講了Android手機與BLE終端之間的通信,而最常見的BLE終端應該是蘋果