Home | History | Annotate | Download | only in menu
      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.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.content.Context;
     22 import android.view.LayoutInflater;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 
     26 import java.util.ArrayList;
     27 
     28 /**
     29  * Base class for MenuPresenters that have a consistent container view and item
     30  * views. Behaves similarly to an AdapterView in that existing item views will
     31  * be reused if possible when items change.
     32  */
     33 public abstract class BaseMenuPresenter implements MenuPresenter {
     34     protected Context mSystemContext;
     35     protected Context mContext;
     36     protected MenuBuilder mMenu;
     37     protected LayoutInflater mSystemInflater;
     38     protected LayoutInflater mInflater;
     39     private Callback mCallback;
     40 
     41     private int mMenuLayoutRes;
     42     private int mItemLayoutRes;
     43 
     44     protected MenuView mMenuView;
     45 
     46     private int mId;
     47 
     48     /**
     49      * Construct a new BaseMenuPresenter.
     50      *
     51      * @param context Context for generating system-supplied views
     52      * @param menuLayoutRes Layout resource ID for the menu container view
     53      * @param itemLayoutRes Layout resource ID for a single item view
     54      */
     55     public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
     56         mSystemContext = context;
     57         mSystemInflater = LayoutInflater.from(context);
     58         mMenuLayoutRes = menuLayoutRes;
     59         mItemLayoutRes = itemLayoutRes;
     60     }
     61 
     62     @Override
     63     public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
     64         mContext = context;
     65         mInflater = LayoutInflater.from(mContext);
     66         mMenu = menu;
     67     }
     68 
     69     @Override
     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) return;
     86 
     87         int childIndex = 0;
     88         if (mMenu != null) {
     89             mMenu.flagActionItems();
     90             ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
     91             final int itemCount = visibleItems.size();
     92             for (int i = 0; i < itemCount; i++) {
     93                 MenuItemImpl item = visibleItems.get(i);
     94                 if (shouldIncludeItem(childIndex, item)) {
     95                     final View convertView = parent.getChildAt(childIndex);
     96                     final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
     97                             ((MenuView.ItemView) convertView).getItemData() : null;
     98                     final View itemView = getItemView(item, convertView, parent);
     99                     if (item != oldItem) {
    100                         // Don't let old states linger with new data.
    101                         itemView.setPressed(false);
    102                         itemView.jumpDrawablesToCurrentState();
    103                     }
    104                     if (itemView != convertView) {
    105                         addItemView(itemView, childIndex);
    106                     }
    107                     childIndex++;
    108                 }
    109             }
    110         }
    111 
    112         // Remove leftover views.
    113         while (childIndex < parent.getChildCount()) {
    114             if (!filterLeftoverView(parent, childIndex)) {
    115                 childIndex++;
    116             }
    117         }
    118     }
    119 
    120     /**
    121      * Add an item view at the given index.
    122      *
    123      * @param itemView View to add
    124      * @param childIndex Index within the parent to insert at
    125      */
    126     protected void addItemView(View itemView, int childIndex) {
    127         final ViewGroup currentParent = (ViewGroup) itemView.getParent();
    128         if (currentParent != null) {
    129             currentParent.removeView(itemView);
    130         }
    131         ((ViewGroup) mMenuView).addView(itemView, childIndex);
    132     }
    133 
    134     /**
    135      * Filter the child view at index and remove it if appropriate.
    136      * @param parent Parent to filter from
    137      * @param childIndex Index to filter
    138      * @return true if the child view at index was removed
    139      */
    140     protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
    141         parent.removeViewAt(childIndex);
    142         return true;
    143     }
    144 
    145     public void setCallback(Callback cb) {
    146         mCallback = cb;
    147     }
    148 
    149     public Callback getCallback() {
    150         return mCallback;
    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.
    164      * This may require creating a new item view, but well-behaved implementations will
    165      * re-use the view passed as convertView if present. The returned view will be populated
    166      * with data from the item 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