Home | History | Annotate | Download | only in menu
      1 /*
      2  * Copyright (C) 2006 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 com.android.internal.R;
     20 
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Rect;
     24 import android.graphics.drawable.Drawable;
     25 import android.util.AttributeSet;
     26 import android.view.LayoutInflater;
     27 import android.view.View;
     28 import android.view.ViewGroup;
     29 import android.view.accessibility.AccessibilityNodeInfo;
     30 import android.widget.AbsListView;
     31 import android.widget.CheckBox;
     32 import android.widget.CompoundButton;
     33 import android.widget.ImageView;
     34 import android.widget.LinearLayout;
     35 import android.widget.RadioButton;
     36 import android.widget.TextView;
     37 
     38 /**
     39  * The item view for each item in the ListView-based MenuViews.
     40  */
     41 public class ListMenuItemView extends LinearLayout
     42         implements MenuView.ItemView, AbsListView.SelectionBoundsAdjuster {
     43     private static final String TAG = "ListMenuItemView";
     44     private MenuItemImpl mItemData;
     45 
     46     private ImageView mIconView;
     47     private RadioButton mRadioButton;
     48     private TextView mTitleView;
     49     private CheckBox mCheckBox;
     50     private TextView mShortcutView;
     51     private ImageView mSubMenuArrowView;
     52     private ImageView mGroupDivider;
     53     private LinearLayout mContent;
     54 
     55     private Drawable mBackground;
     56     private int mTextAppearance;
     57     private Context mTextAppearanceContext;
     58     private boolean mPreserveIconSpacing;
     59     private Drawable mSubMenuArrow;
     60     private boolean mHasListDivider;
     61 
     62     private int mMenuType;
     63 
     64     private LayoutInflater mInflater;
     65 
     66     private boolean mForceShowIcon;
     67 
     68     public ListMenuItemView(
     69             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     70         super(context, attrs, defStyleAttr, defStyleRes);
     71 
     72         final TypedArray a = context.obtainStyledAttributes(
     73                 attrs, com.android.internal.R.styleable.MenuView, defStyleAttr, defStyleRes);
     74 
     75         mBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground);
     76         mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
     77                                           MenuView_itemTextAppearance, -1);
     78         mPreserveIconSpacing = a.getBoolean(
     79                 com.android.internal.R.styleable.MenuView_preserveIconSpacing, false);
     80         mTextAppearanceContext = context;
     81         mSubMenuArrow = a.getDrawable(com.android.internal.R.styleable.MenuView_subMenuArrow);
     82 
     83         final TypedArray b = context.getTheme()
     84                 .obtainStyledAttributes(null, new int[] { com.android.internal.R.attr.divider },
     85                         com.android.internal.R.attr.dropDownListViewStyle, 0);
     86         mHasListDivider = b.hasValue(0);
     87 
     88         a.recycle();
     89         b.recycle();
     90     }
     91 
     92     public ListMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
     93         this(context, attrs, defStyleAttr, 0);
     94     }
     95 
     96     public ListMenuItemView(Context context, AttributeSet attrs) {
     97         this(context, attrs, com.android.internal.R.attr.listMenuViewStyle);
     98     }
     99 
    100     @Override
    101     protected void onFinishInflate() {
    102         super.onFinishInflate();
    103 
    104         setBackgroundDrawable(mBackground);
    105 
    106         mTitleView = findViewById(com.android.internal.R.id.title);
    107         if (mTextAppearance != -1) {
    108             mTitleView.setTextAppearance(mTextAppearanceContext,
    109                                          mTextAppearance);
    110         }
    111 
    112         mShortcutView = findViewById(com.android.internal.R.id.shortcut);
    113         mSubMenuArrowView = findViewById(com.android.internal.R.id.submenuarrow);
    114         if (mSubMenuArrowView != null) {
    115             mSubMenuArrowView.setImageDrawable(mSubMenuArrow);
    116         }
    117         mGroupDivider = findViewById(com.android.internal.R.id.group_divider);
    118 
    119         mContent = findViewById(com.android.internal.R.id.content);
    120     }
    121 
    122     public void initialize(MenuItemImpl itemData, int menuType) {
    123         mItemData = itemData;
    124         mMenuType = menuType;
    125 
    126         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
    127 
    128         setTitle(itemData.getTitleForItemView(this));
    129         setCheckable(itemData.isCheckable());
    130         setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
    131         setIcon(itemData.getIcon());
    132         setEnabled(itemData.isEnabled());
    133         setSubMenuArrowVisible(itemData.hasSubMenu());
    134         setContentDescription(itemData.getContentDescription());
    135     }
    136 
    137     private void addContentView(View v) {
    138         addContentView(v, -1);
    139     }
    140 
    141     private void addContentView(View v, int index) {
    142         if (mContent != null) {
    143             mContent.addView(v, index);
    144         } else {
    145             addView(v, index);
    146         }
    147     }
    148 
    149     public void setForceShowIcon(boolean forceShow) {
    150         mPreserveIconSpacing = mForceShowIcon = forceShow;
    151     }
    152 
    153     public void setTitle(CharSequence title) {
    154         if (title != null) {
    155             mTitleView.setText(title);
    156 
    157             if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
    158         } else {
    159             if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
    160         }
    161     }
    162 
    163     public MenuItemImpl getItemData() {
    164         return mItemData;
    165     }
    166 
    167     public void setCheckable(boolean checkable) {
    168         if (!checkable && mRadioButton == null && mCheckBox == null) {
    169             return;
    170         }
    171 
    172         // Depending on whether its exclusive check or not, the checkbox or
    173         // radio button will be the one in use (and the other will be otherCompoundButton)
    174         final CompoundButton compoundButton;
    175         final CompoundButton otherCompoundButton;
    176 
    177         if (mItemData.isExclusiveCheckable()) {
    178             if (mRadioButton == null) {
    179                 insertRadioButton();
    180             }
    181             compoundButton = mRadioButton;
    182             otherCompoundButton = mCheckBox;
    183         } else {
    184             if (mCheckBox == null) {
    185                 insertCheckBox();
    186             }
    187             compoundButton = mCheckBox;
    188             otherCompoundButton = mRadioButton;
    189         }
    190 
    191         if (checkable) {
    192             compoundButton.setChecked(mItemData.isChecked());
    193 
    194             final int newVisibility = checkable ? VISIBLE : GONE;
    195             if (compoundButton.getVisibility() != newVisibility) {
    196                 compoundButton.setVisibility(newVisibility);
    197             }
    198 
    199             // Make sure the other compound button isn't visible
    200             if (otherCompoundButton != null && otherCompoundButton.getVisibility() != GONE) {
    201                 otherCompoundButton.setVisibility(GONE);
    202             }
    203         } else {
    204             if (mCheckBox != null) mCheckBox.setVisibility(GONE);
    205             if (mRadioButton != null) mRadioButton.setVisibility(GONE);
    206         }
    207     }
    208 
    209     public void setChecked(boolean checked) {
    210         CompoundButton compoundButton;
    211 
    212         if (mItemData.isExclusiveCheckable()) {
    213             if (mRadioButton == null) {
    214                 insertRadioButton();
    215             }
    216             compoundButton = mRadioButton;
    217         } else {
    218             if (mCheckBox == null) {
    219                 insertCheckBox();
    220             }
    221             compoundButton = mCheckBox;
    222         }
    223 
    224         compoundButton.setChecked(checked);
    225     }
    226 
    227     private void setSubMenuArrowVisible(boolean hasSubmenu) {
    228         if (mSubMenuArrowView != null) {
    229             mSubMenuArrowView.setVisibility(hasSubmenu ? View.VISIBLE : View.GONE);
    230         }
    231     }
    232 
    233     public void setShortcut(boolean showShortcut, char shortcutKey) {
    234         final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
    235                 ? VISIBLE : GONE;
    236 
    237         if (newVisibility == VISIBLE) {
    238             mShortcutView.setText(mItemData.getShortcutLabel());
    239         }
    240 
    241         if (mShortcutView.getVisibility() != newVisibility) {
    242             mShortcutView.setVisibility(newVisibility);
    243         }
    244     }
    245 
    246     public void setIcon(Drawable icon) {
    247         final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
    248         if (!showIcon && !mPreserveIconSpacing) {
    249             return;
    250         }
    251 
    252         if (mIconView == null && icon == null && !mPreserveIconSpacing) {
    253             return;
    254         }
    255 
    256         if (mIconView == null) {
    257             insertIconView();
    258         }
    259 
    260         if (icon != null || mPreserveIconSpacing) {
    261             mIconView.setImageDrawable(showIcon ? icon : null);
    262 
    263             if (mIconView.getVisibility() != VISIBLE) {
    264                 mIconView.setVisibility(VISIBLE);
    265             }
    266         } else {
    267             mIconView.setVisibility(GONE);
    268         }
    269     }
    270 
    271     @Override
    272     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    273         if (mIconView != null && mPreserveIconSpacing) {
    274             // Enforce minimum icon spacing
    275             ViewGroup.LayoutParams lp = getLayoutParams();
    276             LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
    277             if (lp.height > 0 && iconLp.width <= 0) {
    278                 iconLp.width = lp.height;
    279             }
    280         }
    281         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    282     }
    283 
    284     private void insertIconView() {
    285         LayoutInflater inflater = getInflater();
    286         mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
    287                 this, false);
    288         addContentView(mIconView, 0);
    289     }
    290 
    291     private void insertRadioButton() {
    292         LayoutInflater inflater = getInflater();
    293         mRadioButton =
    294                 (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio,
    295                 this, false);
    296         addContentView(mRadioButton);
    297     }
    298 
    299     private void insertCheckBox() {
    300         LayoutInflater inflater = getInflater();
    301         mCheckBox =
    302                 (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox,
    303                 this, false);
    304         addContentView(mCheckBox);
    305     }
    306 
    307     public boolean prefersCondensedTitle() {
    308         return false;
    309     }
    310 
    311     public boolean showsIcon() {
    312         return mForceShowIcon;
    313     }
    314 
    315     private LayoutInflater getInflater() {
    316         if (mInflater == null) {
    317             mInflater = LayoutInflater.from(mContext);
    318         }
    319         return mInflater;
    320     }
    321 
    322     @Override
    323     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
    324         super.onInitializeAccessibilityNodeInfoInternal(info);
    325 
    326         if (mItemData != null && mItemData.hasSubMenu()) {
    327             info.setCanOpenPopup(true);
    328         }
    329     }
    330 
    331     /**
    332      * Enable or disable group dividers for this view.
    333      */
    334     public void setGroupDividerEnabled(boolean groupDividerEnabled) {
    335         // If mHasListDivider is true, disabling the groupDivider.
    336         // Otherwise, checking enbling it according to groupDividerEnabled flag.
    337         if (mGroupDivider != null) {
    338             mGroupDivider.setVisibility(!mHasListDivider
    339                     && groupDividerEnabled ? View.VISIBLE : View.GONE);
    340         }
    341     }
    342 
    343     @Override
    344     public void adjustListItemSelectionBounds(Rect rect) {
    345         if (mGroupDivider != null && mGroupDivider.getVisibility() == View.VISIBLE) {
    346             // groupDivider is a part of MenuItemListView.
    347             // If ListMenuItem with divider enabled is hovered/clicked, divider also gets selected.
    348             // Clipping the selector bounds from the top divider portion when divider is enabled,
    349             // so that divider does not get selected on hover or click.
    350             final LayoutParams lp = (LayoutParams) mGroupDivider.getLayoutParams();
    351             rect.top += mGroupDivider.getHeight() + lp.topMargin + lp.bottomMargin;
    352         }
    353     }
    354 }
    355