BaseMenuPresenter.java revision 49c78900da0d43140fb602431fb93212bd7f6c70
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    @Override
71    public MenuView getMenuView(ViewGroup root) {
72        if (mMenuView == null) {
73            mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
74            mMenuView.initialize(mMenu);
75            updateMenuView(true);
76        }
77
78        return mMenuView;
79    }
80
81    /**
82     * Reuses item views when it can
83     */
84    public void updateMenuView(boolean cleared) {
85        final ViewGroup parent = (ViewGroup) mMenuView;
86        if (parent == null) return;
87
88        int childIndex = 0;
89        if (mMenu != null) {
90            mMenu.flagActionItems();
91            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
92            final int itemCount = visibleItems.size();
93            for (int i = 0; i < itemCount; i++) {
94                MenuItemImpl item = visibleItems.get(i);
95                if (shouldIncludeItem(childIndex, item)) {
96                    final View convertView = parent.getChildAt(childIndex);
97                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
98                            ((MenuView.ItemView) convertView).getItemData() : null;
99                    final View itemView = getItemView(item, convertView, parent);
100                    if (item != oldItem) {
101                        // Don't let old states linger with new data.
102                        itemView.setPressed(false);
103                        // itemView.jumpDrawablesToCurrentState();
104                        // Animation API: Not available on API < 11
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