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