setSupportActionBar添加Toolbar后系统之后如何创建Menu

在正常的开发中我们会使用 setSupportActionBar(Toolbar toolbar)方法设置Toolbar,通过Toolbar我们可以添加Menu等菜单操作。那么setSupportActionBar方法到底是如何设置了ToolBar并且关联到Menu的?

首先从setSupportActionBar(mToolbar)看起。首先这个方法调用到了this.getDelegate().setSupportActionBar(toolbar);这里通过代理的方式调用了的AppCompatDelegateImpl的setSupportActionBar(Toolbar toolbar)。

public void setSupportActionBar(Toolbar toolbar) {
        if (this.mOriginalWindowCallback instanceof Activity) {
            ActionBar ab = this.getSupportActionBar();
            if (ab instanceof WindowDecorActionBar) {
                throw new IllegalStateException("This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.");
            } else {
                this.mMenuInflater = null;
                if (ab != null) {
                    ab.onDestroy();
                }

                if (toolbar != null) {
                    ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity)this.mOriginalWindowCallback).getTitle(), this.mAppCompatWindowCallback);
                    this.mActionBar = tbab;
                    this.mWindow.setCallback(tbab.getWrappedWindowCallback());
                } else {
                    this.mActionBar = null;
                    this.mWindow.setCallback(this.mAppCompatWindowCallback);
                }

                this.invalidateOptionsMenu();
            }
        }
    }

这里对Toolbar进行了一系列处理。

主要是创建了ToolbarActionBar 并且赋值给mActionBar。

最后调用了invalidateOptionsMenu()

public void invalidateOptionsMenu() {
        ActionBar ab = this.getSupportActionBar();
        if (ab == null || !ab.invalidateOptionsMenu()) {
            this.invalidatePanelMenu(0);
        }
    }

查看getSupportActionBar();最后返回了我们前面创建的ToolBarActionBar

public ActionBar getSupportActionBar() {
        this.initWindowDecorActionBar();
        return this.mActionBar;
    }

所以又执行到了ToolbarActionBar的invalidateOptionsMenu()

 public boolean invalidateOptionsMenu() {
        this.mDecorToolbar.getViewGroup().removeCallbacks(this.mMenuInvalidator);
        ViewCompat.postOnAnimation(this.mDecorToolbar.getViewGroup(), this.mMenuInvalidator);
        return true;
    }
this.mDecorToolbar.getViewGroup().removeCallbacks(this.mMenuInvalidator);执行了一个removeCallbacks()方法。这里传入了一个mMenuInvalidator一个Runable对象。
private final Runnable mMenuInvalidator = new Runnable() {
        public void run() {
            ToolbarActionBar.this.populateOptionsMenu();
        }
    };
void populateOptionsMenu() {
        Menu menu = this.getMenu();//创建获取了一个Menu
        MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder)menu : null;
        if (mb != null) {
            mb.stopDispatchingItemsChanged();
        }

        try {
            menu.clear();
            if (!this.mWindowCallback.onCreatePanelMenu(0, menu) || !this.mWindowCallback.onPreparePanel(0, (View)null, menu)) {//执行这里
                menu.clear();
            }
        } finally {
            if (mb != null) {
                mb.startDispatchingItemsChanged();
            }

        }

    }

我们大致能看出,最后执行了

this.mWindowCallback.onCreatePanelMenu(0, menu) 

this.mWindowCallback.onPreparePanel(0, (View)null, menu))

我们仔细追踪一下就会找到这个mWindowCallback在AppCompatDelegateImpl的构造方法中

this.mOriginalWindowCallback = this.mWindow.getCallback();

被赋值。然后再创建ToolbarActionBar的时候赋值给ToolbarActionBar的mWindowCallback。

在ToolbarActionBar的populateOptionsMenu()中执行的也就是对应的mWindow.getCallback的onCreatePanelMenu()方法和onPreparePanel()方法,这里了解源码也就知道了,这个mWindow就对应了我们操作的一个Activity的Window,所以就执行到了Activity中。(Activity实现了Window.Callback接口,这个接口包含了我们看到的如下两个方法

 @Override
    public boolean onCreatePanelMenu(int featureId, Menu menu) {
        return super.onCreatePanelMenu(featureId, menu);
    }
    

    @Override
    public boolean onPreparePanel(int featureId, View view, Menu menu) {
        return super.onPreparePanel(featureId, view, menu);
    }

查看Activity的源码找到这个方法,我们找到我们经常重写的方法onCreateOptionsMenu(menu);//重写的方法。

 public boolean onCreatePanelMenu(int featureId, Menu menu) {
        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
            boolean show = onCreateOptionsMenu(menu);//重写的方法
            show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
            return show;
        }
        return false;
    }

这两个方法中传递了Menu参数,这个参数在前面的代码中已经写过了。populateOptionsMenu()方法中有一句

Menu menu = this.getMenu();//创建获取了一个Menu

我们看下如果得到这个Menu对象。

 private Menu getMenu() {
        if (!this.mMenuCallbackSet) {
            this.mDecorToolbar.setMenuCallbacks(new ToolbarActionBar.ActionMenuPresenterCallback(), new ToolbarActionBar.MenuBuilderCallback());
            this.mMenuCallbackSet = true;
        }

        return this.mDecorToolbar.getMenu();
    }

调用了mDeorToolbar.getMenu()方法。追踪mDeorToolbar找到ToolbarWidgetWrapper这个类。调用了getMenu().

public Menu getMenu() {
        return this.mToolbar.getMenu();
    }

最后又回到了Toolbar中。

在ToolBar中查看

public Menu getMenu() {
        this.ensureMenu();
        return this.mMenuView.getMenu();
    }

创建了mMenuView。

 private void ensureMenuView() {
        if (this.mMenuView == null) {
            this.mMenuView = new ActionMenuView(this.getContext());
            this.mMenuView.setPopupTheme(this.mPopupTheme);
            this.mMenuView.setOnMenuItemClickListener(this.mMenuViewItemClickListener);
            this.mMenuView.setMenuCallbacks(this.mActionMenuPresenterCallback, this.mMenuBuilderCallback);
            Toolbar.LayoutParams lp = this.generateDefaultLayoutParams();
            lp.gravity = 8388613 | this.mButtonGravity & 112;
            this.mMenuView.setLayoutParams(lp);
            this.addSystemView(this.mMenuView, false);
        }

    }

调用mMenuView.getMeun(),从而创建Menu

  public Menu getMenu() {
        if (this.mMenu == null) {
            Context context = this.getContext();
            this.mMenu = new MenuBuilder(context);
            this.mMenu.setCallback(new ActionMenuView.MenuBuilderCallback());
            this.mPresenter = new ActionMenuPresenter(context);
            this.mPresenter.setReserveOverflow(true);
            this.mPresenter.setCallback((Callback)(this.mActionMenuPresenterCallback != null ? this.mActionMenuPresenterCallback : new ActionMenuView.ActionMenuPresenterCallback()));
            this.mMenu.addMenuPresenter(this.mPresenter, this.mPopupContext);
            this.mPresenter.setMenuView(this);
        }

        return this.mMenu;
    }

我们在Activity中一般会重写这个方法

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (mConversationType != null && mConversationType == Conversation.ConversationType.GROUP) {
            MenuInflater inflater = getMenuInflater();
            //增加统一的Menu
            inflater.inflate(R.menu.menu_im_group, menu);//解析Menu布局
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

解析Menu布局,获取布局的View。

查看inflate()方法可以知道。inflate()中调用了parseMenu(parser, attrs, menu);

继续跟踪发现通过Menu对象创建了MenuState menuState = new MenuState(menu);

循环解析xml文件获取item,然后调用meunStatu.addItem();

menuState.readItem(attrs);
 public MenuItem addItem() {
            itemAdded = true;
            MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle);
            setItem(item);
            return item;
        }

最后把解析的item添加到menu中。

回到创建Menu的MenuBuilder中

 public MenuItem add(CharSequence title) {
        return this.addInternal(0, 0, 0, title);
    }
 protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
        int ordering = getOrdering(categoryOrder);
        MenuItemImpl item = this.createNewMenuItem(group, id, categoryOrder, ordering, title, this.mDefaultShowAsAction);
        if (this.mCurrentMenuInfo != null) {
            item.setMenuInfo(this.mCurrentMenuInfo);
        }

        this.mItems.add(findInsertIndex(this.mItems, ordering), item);//加入集合
        this.onItemsChanged(true);
        return item;
    }
ArrayList<MenuItemImpl> mItems;
this.mItems.add(findInsertIndex(this.mItems, ordering), item);//加入集合
最后将创建的item添加到ArrayList集合中,完成整个Menu的创建。

 

要实现点击Android ToolBar中的SearchView跳转到一个新的Activity,你可以按照以下步骤进行: 1. 在你的Menu文件中定义一个SearchView,例如:search_menu.xml ```xml <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_search" android:title="Search" android:icon="@drawable/ic_search" android:orderInCategory="100" app:showAsAction="ifRoom" app:actionViewClass="android.widget.SearchView" /> </menu> ``` 2. 在你的Activity中设置ToolBar,并在onCreateOptionsMenu方法中inflate你的menu文件,例如: ```java public class MainActivity extends AppCompatActivity { private Toolbar mToolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mToolbar = findViewById(R.id.toolbar); setSupportActionBar(mToolbar); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.search_menu, menu); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); searchView.setQueryHint("Search..."); return true; } // ... } ``` 3. 在你的Activity中为SearchView设置OnQueryTextListener,并在onQueryTextSubmit方法中处理搜索逻辑,例如: ```java public class MainActivity extends AppCompatActivity { // ... @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.search_menu, menu); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); searchView.setQueryHint("Search..."); // Set OnQueryTextListener searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { Intent intent = new Intent(MainActivity.this, SearchResultActivity.class); intent.putExtra("search_query", query); startActivity(intent); return true; } @Override public boolean onQueryTextChange(String newText) { // Do nothing return false; } }); return true; } // ... } ``` 4. 创建一个新的Activity来显示搜索结果,例如: ```java public class SearchResultActivity extends AppCompatActivity { private TextView mResultTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_result); mResultTextView = findViewById(R.id.result_text_view); String searchQuery = getIntent().getStringExtra("search_query"); mResultTextView.setText("Search result for: " + searchQuery); } } ``` 这样,当用户在SearchView中输入搜索关键字并点击搜索按钮时,就会跳转到SearchResultActivity,并将搜索关键字传递给该Activity进行搜索。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值