web-dev-qa-db-ja.com

Androidアクションバーのメニュー項目を左に揃える

アプリケーションに、res/menu/activity_main.xmlで定義されたメニュー項目を表示するアクションバーがあります。

メニュー項目はアクションバーの右側に配置されています。左揃えにしてほしい。

この使用されたカスタムアクションバーに対して私が見つけた解決策のみ: HoneycombのActionBarの左側にメニュー項目を配置する

ただし、メニューのカスタムレイアウトを作成したくありませんres/menu/activity_main.xmlから生成されたデフォルトのメニュー項目を使用したい。

これは可能ですか?

14
hendrix

さて、私はこれに興味があったので、SDKのソースの奥深くを掘り下げました。 XMLファイルの3つのメニュー項目でAppCompatActivityを使用し、デフォルトのonCreateOptionMenuメソッドを使用しました。これは次のとおりです。

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

デバッガーでinflateメソッドから移動した後、次のスタックを通過しました。

updateMenuView():96, BaseMenuPresenter (Android.support.v7.internal.view.menu)
updateMenuView():231, ActionMenuPresenter (Android.support.v7.widget)
dispatchPresenterUpdate():284, MenuBuilder (Android.support.v7.internal.view.menu)
onItemsChanged():1030, MenuBuilder (Android.support.v7.internal.view.menu)
startDispatchingItemsChanged():1053, MenuBuilder (Android.support.v7.internal.view.menu)
preparePanel():1303, AppCompatDelegateImplV7 (Android.support.v7.app)
doInvalidatePanelMenu():1541, AppCompatDelegateImplV7 (Android.support.v7.app)
access$100():92, AppCompatDelegateImplV7 (Android.support.v7.app)
run():130, AppCompatDelegateImplV7$1 (Android.support.v7.app)
handleCallback():739, Handler (Android.os)
dispatchMessage():95, Handler (Android.os)
loop():148, Looper (Android.os)
main():5417, ActivityThread (Android.app)
invoke():-1, Method (Java.lang.reflect)
run():726, ZygoteInit$MethodAndArgsCaller (com.Android.internal.os)
main():616, ZygoteInit (com.Android.internal.os)

それはBaseMenuPresenterupdateMenuViewメソッドで終わりました。ここで、関連する作業が行われます。

メソッドのコード:

   public void updateMenuView(boolean cleared) {
        final ViewGroup parent = (ViewGroup) mMenuView;
        if (parent == null) return;

        int childIndex = 0;
        if (mMenu != null) {
            mMenu.flagActionItems();
            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
            final int itemCount = visibleItems.size();
            for (int i = 0; i < itemCount; i++) {
                MenuItemImpl item = visibleItems.get(i);
                if (shouldIncludeItem(childIndex, item)) {
                    final View convertView = parent.getChildAt(childIndex);
                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
                            ((MenuView.ItemView) convertView).getItemData() : null;
                    final View itemView = getItemView(item, convertView, parent);
                    if (item != oldItem) {
                        // Don't let old states linger with new data.
                        itemView.setPressed(false);
                        ViewCompat.jumpDrawablesToCurrentState(itemView);
                    }
                    if (itemView != convertView) {
                        addItemView(itemView, childIndex);
                    }
                    childIndex++;
                }
            }
        }

        // Remove leftover views.
        while (childIndex < parent.getChildCount()) {
            if (!filterLeftoverView(parent, childIndex)) {
                childIndex++;
            }
        }
    }

ここで、getItemViewメソッドとaddItemViewメソッドは、その名前が示すとおりに機能します。 1つ目は新しいビューを膨らませ、2つ目はそれを親に追加します。さらに重要なのは、デバッガーの下で親オブジェクトをチェックできることです。これは ActionMenuView であり、LinearLayoutと拡張形式 abc_action_menu_layout.xml を継承します。

つまり、このビューを取得できれば、やりたいことができるということです。理論的には、多くの反省でそれを行うことができると思いますが、それは苦痛です。その代わりに、コードで再現できます。実装は見つけることができます ここ

上記のことによると、あなたの質問に対する答えは「はい」です。それは可能ですが、注意が必要です。

編集:

これをリフレクションで行うための概念実証を作成しました。 com.Android.support:appcompat-v7:23.1.0を使用しました。

エミュレーター(Android 6.0)とZuk Z1(CM Android 5.1.1)でこれを試しましたが、どちらでも問題なく動作します。

メニューXML:

<menu xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto">

    <item Android:id="@+id/action_settings" Android:title="@string/action_settings"
        Android:orderInCategory="100" app:showAsAction="always" />
    <item Android:id="@+id/action_settings2" Android:title="TEST1"
        Android:orderInCategory="100" app:showAsAction="always" />
    <item Android:id="@+id/action_settings3" Android:title="TEST2"
        Android:orderInCategory="100" app:showAsAction="always" />
</menu>

アクティビティXML:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools" Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Button
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="New Button"
        Android:id="@+id/button"
        Android:layout_gravity="center_vertical" />
</LinearLayout>

アクティビティ:

public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //only a linear layout with one button
        setContentView(R.layout.activity_main2);

        Button b = (Button) findViewById(R.id.button);

        // do the whole process for a click, everything is inited so we dont run into NPE
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                AppCompatDelegate delegate = getDelegate();

                Class delegateImpClass = null;
                Field menu = null;
                Method[] methods = null;

                try {

                    //get objects based on the stack trace
                    delegateImpClass = Class.forName("Android.support.v7.app.AppCompatDelegateImplV7");

                    //get delegate->mPreparedPanel
                    Field mPreparedPanelField = delegateImpClass.getDeclaredField("mPreparedPanel");
                    mPreparedPanelField.setAccessible(true);
                    Object mPreparedPanelObject = mPreparedPanelField.get(delegate);

                    //get delegate->mPreparedPanel->menu
                    Class PanelFeatureStateClass = Class.forName("Android.support.v7.app.AppCompatDelegateImplV7$PanelFeatureState");
                    Field menuField = PanelFeatureStateClass.getDeclaredField("menu");
                    menuField.setAccessible(true);
                    Object menuObjectRaw = menuField.get(mPreparedPanelObject);
                    MenuBuilder menuObject = (MenuBuilder) menuObjectRaw;

                    //get delegate->mPreparedPanel->menu->mPresenter(0)
                    Field mPresentersField = menuObject.getClass().getDeclaredField("mPresenters");
                    mPresentersField.setAccessible(true);
                    CopyOnWriteArrayList<WeakReference<MenuPresenter>> mPresenters = (CopyOnWriteArrayList<WeakReference<MenuPresenter>>) mPresentersField.get(menuObject);
                    ActionMenuPresenter presenter0 = (ActionMenuPresenter) mPresenters.get(0).get();

                    //get the view from the presenter
                    Field mMenuViewField = presenter0.getClass().getSuperclass().getDeclaredField("mMenuView");
                    mMenuViewField.setAccessible(true);
                    MenuView menuView = (MenuView) mMenuViewField.get(presenter0);
                    ViewGroup menuViewParentObject = (ViewGroup) ((View) menuView);

                    //check the menu items count
                    int a = menuViewParentObject.getChildCount();
                    Log.i("ChildNum", a + "");




                    //set params as you want
                    Toolbar.LayoutParams params = (Toolbar.LayoutParams) menuViewParentObject.getLayoutParams();

                    params.gravity = Gravity.LEFT;

                    menuViewParentObject.setLayoutParams(params);




                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
}

ここでは重力が変更されていますが、画面上ではこれによって大きな違いはありません。実際に目に見える変化を得るには、他のレイアウトパラメータ(幅など)を調整する必要があります。

全体として、カスタムレイアウトははるかに使いやすいです。

4
csenga

あなたはこのようにそれを行うことができます:

activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
            ActionBar.DISPLAY_SHOW_CUSTOM);
activity.getActionBar().setCustomView(mAutoSyncSwitch, new ActionBar.LayoutParams(
            ActionBar.LayoutParams.WRAP_CONTENT,
            ActionBar.LayoutParams.WRAP_CONTENT,
            Gravity.CENTER_VERTICAL | Gravity.LEFT));
0
user2004181