2012年3月20日火曜日

Android ActionBar の withText で文字がでる条件はどこで決まる?

ActionBar に表示する Action Item はこれまでのメニュー項目と同じように XML で定義できます。
  1. <menu xmlns:android="http://schemas.android.com/apk/res/android" >  
  2.   
  3.     <item  
  4.         android:id="@+id/menu_edit"  
  5.         android:icon="@android:drawable/ic_menu_edit"  
  6.         android:showAsAction="ifRoom|withText"  
  7.         android:title="Edit"/>  
  8.     <item  
  9.         android:id="@+id/menu_save"  
  10.         android:showAsAction="ifRoom"  
  11.         android:title="Save"/>  
  12.     <item  
  13.         android:id="@+id/menu_delete"  
  14.         android:icon="@android:drawable/ic_menu_delete"  
  15.         android:showAsAction="ifRoom"  
  16.         android:title="Delete"/>  
  17.   
  18. </menu>  

android:showAsAction に ifRoom を指定すると領域があるときは Action Item として表示されます。

android:icon が指定されていないときは android:title に指定されている文字列が表示されますが、 android:icon が指定されていたらデフォルトでは android:title の文字列は表示されません。

android:showAsAction に withText を指定すると、

 ハンドセット縦画面 : アイコンだけ
 ハンドセット横画面 : アイコン + 文字列
 タブレット縦画面 : アイコン + 文字列
 タブレット横画面 : アイコン + 文字列

のような表示になります。

ハンドセット縦画面


ハンドセット横画面


この条件がどこで決まっているのか調べました。

---

まず、Menu オブジェクトの View を作っているベースクラスが BaseMenuPresentar です。このクラスは MenuPresenter インタフェースを実装しています。 このクラスのコンストラクタで指定されたリソースID (mMenuLayoutRes) が
  1. 53     public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {  
  2. 54         mSystemContext = context;  
  3. 55         mSystemInflater = LayoutInflater.from(context);  
  4. 56         mMenuLayoutRes = menuLayoutRes;  
  5. 57         mItemLayoutRes = itemLayoutRes;  
  6. 58     }  

getMenuView でインフレートされて Menu の View として返されます。
  1. 67     @Override  
  2. 68     public MenuView getMenuView(ViewGroup root) {  
  3. 69         if (mMenuView == null) {  
  4. 70             mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);  
  5. 71             mMenuView.initialize(mMenu);  
  6. 72             updateMenuView(true);  
  7. 73         }  
  8. 74   
  9. 75         return mMenuView;  
  10. 76     }  

この BaseMenuPresenter のインスタンスを生成しているのが ActionMenuPresenter です。
  1. 71     public ActionMenuPresenter(Context context) {  
  2. 72         super(context, com.android.internal.R.layout.action_menu_layout,  
  3. 73                 com.android.internal.R.layout.action_menu_item_layout);  
  4. 74     }  

  1. 145     @Override  
  2. 146     public MenuView getMenuView(ViewGroup root) {  
  3. 147         MenuView result = super.getMenuView(root);  
  4. 148         ((ActionMenuView) result).setPresenter(this);  
  5. 149         return result;  
  6. 150     }  

つまり、com.android.internal.R.layout.action_menu_item_layout がメニューのレイアウトになります。

このレイアウトのルートは com.android.internal.view.menu.ActionItemMenuView になっています。

この ActionItemMenuView の updateTextButtonVisibility() メソッドで文字列の表示/非表示をセットしています。
  1. 132     private void updateTextButtonVisibility() {  
  2. 133         boolean visible = !TextUtils.isEmpty(mTextButton.getText());  
  3. 134         visible &= mImageButton.getDrawable() == null ||  
  4. 135                 (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));  
  5. 136   
  6. 137         mTextButton.setVisibility(visible ? VISIBLE : GONE);  
  7. 138     }  

ここを見ると、文字列が表示される条件は

 ・文字列が空ではない
    +
 ・アイコンが指定されていない

もしくは、

 ・文字列が空ではない
    +
 ・android:showAsAction に withText がセットされている
    +
 ・mAllowTextWithIcon もしくは mExpandedFormat が true

であることがわかります。


ハンドセットの縦/横で文字列が出ない/出るの違いを決めているのは

 ・mAllowTextWithIcon もしくは mExpandedFormat が true

の部分です。
  1. 37 public class ActionMenuItemView extends LinearLayout  
  2. 38         implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,  
  3. 39         ActionMenuView.ActionMenuChildView {  
  4.   
  5. 48     private boolean mAllowTextWithIcon;  
  6.   
  7. 50     private boolean mExpandedFormat;  

まず、mExpandedFormat についてみると setExpandFormat() メソッドで外部から設定するしかありません。
  1. 123     public void setExpandedFormat(boolean expandedFormat) {  
  2. 124         if (mExpandedFormat != expandedFormat) {  
  3. 125             mExpandedFormat = expandedFormat;  
  4. 126             if (mItemData != null) {  
  5. 127                 mItemData.actionFormatChanged();  
  6. 128             }  
  7. 129         }  
  8. 130     }  

しかし、このメソッドを呼んでいるところがなかったので、現状では常に boolean の初期値の false になってるようです。

次に、mAllowTextWithIcon です。

この変数には、 ActionMenuItemView のコンストラクタで、com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon の値がセットされています。

  1. 60     public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {  
  2. 61         super(context, attrs, defStyle);  
  3. 62         final Resources res = context.getResources();  
  4. 63         mAllowTextWithIcon = res.getBoolean(  
  5. 64                 com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon);  
  6. 65         mShowTextAllCaps = res.getBoolean(com.android.internal.R.bool.config_actionMenuItemAllCaps);  
  7. 66     }  


com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon の値がどうなっているかをみると、

res/values/config.xml
  1. 708     <!-- Whether action menu items should obey the "withText" showAsAction  
  2. 709          flag. This may be set to false for situations where space is  
  3. 710          extremely limited. -->  
  4. 711     <bool name="config_allowActionMenuItemTextWithIcon">false</bool>  

res/values-w480dp/config.xml
  1. 20     <bool name="config_allowActionMenuItemTextWithIcon">true</bool>  

つまり、横幅が 480dp より大きい画面の向きでは mAllowTextWithIcon が true になり、Action Item にアイコン + 文字列が表示されるということでしたー。



0 件のコメント:

コメントを投稿