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