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