| /* |
| * 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 androidx.appcompat.view.menu; |
| |
| import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; |
| |
| import android.content.Context; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import androidx.annotation.RestrictTo; |
| |
| 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. |
| * |
| * @hide |
| */ |
| @RestrictTo(LIBRARY_GROUP) |
| 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(Context context, 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 |
| */ |
| @Override |
| 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); |
| 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; |
| } |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { |
| if (mCallback != null) { |
| mCallback.onCloseMenu(menu, allMenusAreClosing); |
| } |
| } |
| |
| @Override |
| public boolean onSubMenuSelected(SubMenuBuilder menu) { |
| if (mCallback != null) { |
| return mCallback.onOpenSubMenu(menu); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean flagActionItems() { |
| return false; |
| } |
| |
| @Override |
| public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { |
| return false; |
| } |
| |
| @Override |
| public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { |
| return false; |
| } |
| |
| @Override |
| public int getId() { |
| return mId; |
| } |
| |
| public void setId(int id) { |
| mId = id; |
| } |
| } |