1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.view.menu; 18 19 import android.content.Context; 20 import android.view.LayoutInflater; 21 import android.view.View; 22 import android.view.ViewGroup; 23 24 import java.util.ArrayList; 25 26 /** 27 * Base class for MenuPresenters that have a consistent container view and item 28 * views. Behaves similarly to an AdapterView in that existing item views will 29 * be reused if possible when items change. 30 */ 31 public abstract class BaseMenuPresenter implements MenuPresenter { 32 protected Context mSystemContext; 33 protected Context mContext; 34 protected MenuBuilder mMenu; 35 protected LayoutInflater mSystemInflater; 36 protected LayoutInflater mInflater; 37 private Callback mCallback; 38 39 private int mMenuLayoutRes; 40 private int mItemLayoutRes; 41 42 protected MenuView mMenuView; 43 44 private int mId; 45 46 /** 47 * Construct a new BaseMenuPresenter. 48 * 49 * @param context Context for generating system-supplied views 50 * @param menuLayoutRes Layout resource ID for the menu container view 51 * @param itemLayoutRes Layout resource ID for a single item view 52 */ BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes)53 public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) { 54 mSystemContext = context; 55 mSystemInflater = LayoutInflater.from(context); 56 mMenuLayoutRes = menuLayoutRes; 57 mItemLayoutRes = itemLayoutRes; 58 } 59 60 @Override initForMenu(Context context, MenuBuilder menu)61 public void initForMenu(Context context, MenuBuilder menu) { 62 mContext = context; 63 mInflater = LayoutInflater.from(mContext); 64 mMenu = menu; 65 } 66 67 @Override getMenuView(ViewGroup root)68 public MenuView getMenuView(ViewGroup root) { 69 if (mMenuView == null) { 70 mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false); 71 mMenuView.initialize(mMenu); 72 updateMenuView(true); 73 } 74 75 return mMenuView; 76 } 77 78 /** 79 * Reuses item views when it can 80 */ updateMenuView(boolean cleared)81 public void updateMenuView(boolean cleared) { 82 final ViewGroup parent = (ViewGroup) mMenuView; 83 if (parent == null) return; 84 85 int childIndex = 0; 86 if (mMenu != null) { 87 mMenu.flagActionItems(); 88 ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems(); 89 final int itemCount = visibleItems.size(); 90 for (int i = 0; i < itemCount; i++) { 91 MenuItemImpl item = visibleItems.get(i); 92 if (shouldIncludeItem(childIndex, item)) { 93 final View convertView = parent.getChildAt(childIndex); 94 final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ? 95 ((MenuView.ItemView) convertView).getItemData() : null; 96 final View itemView = getItemView(item, convertView, parent); 97 if (item != oldItem) { 98 // Don't let old states linger with new data. 99 itemView.setPressed(false); 100 itemView.jumpDrawablesToCurrentState(); 101 } 102 if (itemView != convertView) { 103 addItemView(itemView, childIndex); 104 } 105 childIndex++; 106 } 107 } 108 } 109 110 // Remove leftover views. 111 while (childIndex < parent.getChildCount()) { 112 if (!filterLeftoverView(parent, childIndex)) { 113 childIndex++; 114 } 115 } 116 } 117 118 /** 119 * Add an item view at the given index. 120 * 121 * @param itemView View to add 122 * @param childIndex Index within the parent to insert at 123 */ addItemView(View itemView, int childIndex)124 protected void addItemView(View itemView, int childIndex) { 125 final ViewGroup currentParent = (ViewGroup) itemView.getParent(); 126 if (currentParent != null) { 127 currentParent.removeView(itemView); 128 } 129 ((ViewGroup) mMenuView).addView(itemView, childIndex); 130 } 131 132 /** 133 * Filter the child view at index and remove it if appropriate. 134 * @param parent Parent to filter from 135 * @param childIndex Index to filter 136 * @return true if the child view at index was removed 137 */ filterLeftoverView(ViewGroup parent, int childIndex)138 protected boolean filterLeftoverView(ViewGroup parent, int childIndex) { 139 parent.removeViewAt(childIndex); 140 return true; 141 } 142 setCallback(Callback cb)143 public void setCallback(Callback cb) { 144 mCallback = cb; 145 } 146 147 /** 148 * Create a new item view that can be re-bound to other item data later. 149 * 150 * @return The new item view 151 */ createItemView(ViewGroup parent)152 public MenuView.ItemView createItemView(ViewGroup parent) { 153 return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false); 154 } 155 156 /** 157 * Prepare an item view for use. See AdapterView for the basic idea at work here. 158 * This may require creating a new item view, but well-behaved implementations will 159 * re-use the view passed as convertView if present. The returned view will be populated 160 * with data from the item parameter. 161 * 162 * @param item Item to present 163 * @param convertView Existing view to reuse 164 * @param parent Intended parent view - use for inflation. 165 * @return View that presents the requested menu item 166 */ getItemView(MenuItemImpl item, View convertView, ViewGroup parent)167 public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { 168 MenuView.ItemView itemView; 169 if (convertView instanceof MenuView.ItemView) { 170 itemView = (MenuView.ItemView) convertView; 171 } else { 172 itemView = createItemView(parent); 173 } 174 bindItemView(item, itemView); 175 return (View) itemView; 176 } 177 178 /** 179 * Bind item data to an existing item view. 180 * 181 * @param item Item to bind 182 * @param itemView View to populate with item data 183 */ bindItemView(MenuItemImpl item, MenuView.ItemView itemView)184 public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView); 185 186 /** 187 * Filter item by child index and item data. 188 * 189 * @param childIndex Indended presentation index of this item 190 * @param item Item to present 191 * @return true if this item should be included in this menu presentation; false otherwise 192 */ shouldIncludeItem(int childIndex, MenuItemImpl item)193 public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { 194 return true; 195 } 196 onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing)197 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 198 if (mCallback != null) { 199 mCallback.onCloseMenu(menu, allMenusAreClosing); 200 } 201 } 202 onSubMenuSelected(SubMenuBuilder menu)203 public boolean onSubMenuSelected(SubMenuBuilder menu) { 204 if (mCallback != null) { 205 return mCallback.onOpenSubMenu(menu); 206 } 207 return false; 208 } 209 flagActionItems()210 public boolean flagActionItems() { 211 return false; 212 } 213 expandItemActionView(MenuBuilder menu, MenuItemImpl item)214 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { 215 return false; 216 } 217 collapseItemActionView(MenuBuilder menu, MenuItemImpl item)218 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { 219 return false; 220 } 221 getId()222 public int getId() { 223 return mId; 224 } 225 setId(int id)226 public void setId(int id) { 227 mId = id; 228 } 229 } 230