/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.view.menu; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; /** * Base class for MenuPresenters that have a consistent container view and item * views. Behaves similarly to an AdapterView in that existing item views will * be reused if possible when items change. */ public abstract class BaseMenuPresenter implements MenuPresenter { protected Context mSystemContext; protected Context mContext; protected MenuBuilder mMenu; protected LayoutInflater mSystemInflater; protected LayoutInflater mInflater; private Callback mCallback; private int mMenuLayoutRes; private int mItemLayoutRes; protected MenuView mMenuView; private int mId; /** * Construct a new BaseMenuPresenter. * * @param context Context for generating system-supplied views * @param menuLayoutRes Layout resource ID for the menu container view * @param itemLayoutRes Layout resource ID for a single item view */ public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) { mSystemContext = context; mSystemInflater = LayoutInflater.from(context); mMenuLayoutRes = menuLayoutRes; mItemLayoutRes = itemLayoutRes; } @Override public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) { mContext = context; mInflater = LayoutInflater.from(mContext); mMenu = menu; } @Override public MenuView getMenuView(ViewGroup root) { if (mMenuView == null) { mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false); mMenuView.initialize(mMenu); updateMenuView(true); } return mMenuView; } /** * Reuses item views when it can */ public void updateMenuView(boolean cleared) { final ViewGroup parent = (ViewGroup) mMenuView; if (parent == null) return; int childIndex = 0; if (mMenu != null) { mMenu.flagActionItems(); ArrayList 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); itemView.jumpDrawablesToCurrentState(); } if (itemView != convertView) { addItemView(itemView, childIndex); } childIndex++; } } } // Remove leftover views. while (childIndex < parent.getChildCount()) { if (!filterLeftoverView(parent, childIndex)) { childIndex++; } } } /** * Add an item view at the given index. * * @param itemView View to add * @param childIndex Index within the parent to insert at */ protected void addItemView(View itemView, int childIndex) { final ViewGroup currentParent = (ViewGroup) itemView.getParent(); if (currentParent != null) { currentParent.removeView(itemView); } ((ViewGroup) mMenuView).addView(itemView, childIndex); } /** * Filter the child view at index and remove it if appropriate. * @param parent Parent to filter from * @param childIndex Index to filter * @return true if the child view at index was removed */ protected boolean filterLeftoverView(ViewGroup parent, int childIndex) { parent.removeViewAt(childIndex); return true; } public void setCallback(Callback cb) { mCallback = cb; } public Callback getCallback() { return mCallback; } /** * Create a new item view that can be re-bound to other item data later. * * @return The new item view */ public MenuView.ItemView createItemView(ViewGroup parent) { return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false); } /** * Prepare an item view for use. See AdapterView for the basic idea at work here. * This may require creating a new item view, but well-behaved implementations will * re-use the view passed as convertView if present. The returned view will be populated * with data from the item parameter. * * @param item Item to present * @param convertView Existing view to reuse * @param parent Intended parent view - use for inflation. * @return View that presents the requested menu item */ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { MenuView.ItemView itemView; if (convertView instanceof MenuView.ItemView) { itemView = (MenuView.ItemView) convertView; } else { itemView = createItemView(parent); } bindItemView(item, itemView); return (View) itemView; } /** * Bind item data to an existing item view. * * @param item Item to bind * @param itemView View to populate with item data */ public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView); /** * Filter item by child index and item data. * * @param childIndex Indended presentation index of this item * @param item Item to present * @return true if this item should be included in this menu presentation; false otherwise */ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { return true; } public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { if (mCallback != null) { mCallback.onCloseMenu(menu, allMenusAreClosing); } } public boolean onSubMenuSelected(SubMenuBuilder menu) { if (mCallback != null) { return mCallback.onOpenSubMenu(menu); } return false; } public boolean flagActionItems() { return false; } public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { return false; } public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { return false; } public int getId() { return mId; } public void setId(int id) { mId = id; } }