Source code
package android.support.design.internal;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.design.R;
import android.support.v7.internal.view.menu.MenuBuilder;
import android.support.v7.internal.view.menu.MenuItemImpl;
import android.support.v7.internal.view.menu.MenuPresenter;
import android.support.v7.internal.view.menu.MenuPresenter.Callback;
import android.support.v7.internal.view.menu.MenuView;
import android.support.v7.internal.view.menu.SubMenuBuilder;
import android.support.v7.widget.RecyclerView.Adapter;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.Iterator;
public class NavigationMenuPresenter implements MenuPresenter {
private static final String STATE_ADAPTER = "android:menu:adapter";
private static final String STATE_HIERARCHY = "android:menu:list";
private NavigationMenuAdapter mAdapter;
private Callback mCallback;
private LinearLayout mHeaderLayout;
private ColorStateList mIconTintList;
private int mId;
private Drawable mItemBackground;
private LayoutInflater mLayoutInflater;
private MenuBuilder mMenu;
private NavigationMenuView mMenuView;
private final OnClickListener mOnClickListener = new OnClickListener() {
public void onClick(View v) {
NavigationMenuItemView itemView = (NavigationMenuItemView) v;
NavigationMenuPresenter.this.setUpdateSuspended(true);
MenuItemImpl item = itemView.getItemData();
boolean result = NavigationMenuPresenter.this.mMenu.performItemAction(item, NavigationMenuPresenter.this, 0);
if (item != null && item.isCheckable() && result) {
NavigationMenuPresenter.this.mAdapter.setCheckedItem(item);
}
NavigationMenuPresenter.this.setUpdateSuspended(false);
NavigationMenuPresenter.this.updateMenuView(false);
}
};
private int mPaddingSeparator;
private int mPaddingTopDefault;
private int mTextAppearance;
private boolean mTextAppearanceSet;
private ColorStateList mTextColor;
private interface NavigationMenuItem {
}
private class NavigationMenuAdapter extends Adapter<ViewHolder> {
private static final String STATE_ACTION_VIEWS = "android:menu:action_views";
private static final String STATE_CHECKED_ITEM = "android:menu:checked";
private static final int VIEW_TYPE_HEADER = 3;
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SEPARATOR = 2;
private static final int VIEW_TYPE_SUBHEADER = 1;
private MenuItemImpl mCheckedItem;
private final ArrayList<NavigationMenuItem> mItems = new ArrayList();
private ColorDrawable mTransparentIcon;
private boolean mUpdateSuspended;
NavigationMenuAdapter() {
prepareMenuItems();
}
public long getItemId(int position) {
return (long) position;
}
public int getItemCount() {
return this.mItems.size();
}
public int getItemViewType(int position) {
NavigationMenuItem item = (NavigationMenuItem) this.mItems.get(position);
if (item instanceof NavigationMenuSeparatorItem) {
return 2;
}
if (item instanceof NavigationMenuHeaderItem) {
return 3;
}
if (!(item instanceof NavigationMenuTextItem)) {
throw new RuntimeException("Unknown item type.");
} else if (((NavigationMenuTextItem) item).getMenuItem().hasSubMenu()) {
return 1;
} else {
return 0;
}
}
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0:
return new NormalViewHolder(NavigationMenuPresenter.this.mLayoutInflater, parent, NavigationMenuPresenter.this.mOnClickListener);
case 1:
return new SubheaderViewHolder(NavigationMenuPresenter.this.mLayoutInflater, parent);
case 2:
return new SeparatorViewHolder(NavigationMenuPresenter.this.mLayoutInflater, parent);
case 3:
return new HeaderViewHolder(NavigationMenuPresenter.this.mHeaderLayout);
default:
return null;
}
}
public void onBindViewHolder(ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case 0:
NavigationMenuItemView itemView = holder.itemView;
itemView.setIconTintList(NavigationMenuPresenter.this.mIconTintList);
if (NavigationMenuPresenter.this.mTextAppearanceSet) {
itemView.setTextAppearance(itemView.getContext(), NavigationMenuPresenter.this.mTextAppearance);
}
if (NavigationMenuPresenter.this.mTextColor != null) {
itemView.setTextColor(NavigationMenuPresenter.this.mTextColor);
}
itemView.setBackgroundDrawable(NavigationMenuPresenter.this.mItemBackground != null ? NavigationMenuPresenter.this.mItemBackground.getConstantState().newDrawable() : null);
itemView.initialize(((NavigationMenuTextItem) this.mItems.get(position)).getMenuItem(), 0);
return;
case 1:
holder.itemView.setText(((NavigationMenuTextItem) this.mItems.get(position)).getMenuItem().getTitle());
return;
case 2:
NavigationMenuSeparatorItem item = (NavigationMenuSeparatorItem) this.mItems.get(position);
holder.itemView.setPadding(0, item.getPaddingTop(), 0, item.getPaddingBottom());
return;
default:
return;
}
}
public void onViewRecycled(ViewHolder holder) {
if (holder instanceof NormalViewHolder) {
((NavigationMenuItemView) holder.itemView).recycle();
}
}
public void update() {
prepareMenuItems();
notifyDataSetChanged();
}
private void prepareMenuItems() {
if (!this.mUpdateSuspended) {
this.mUpdateSuspended = true;
this.mItems.clear();
this.mItems.add(new NavigationMenuHeaderItem());
int currentGroupId = -1;
int currentGroupStart = 0;
boolean currentGroupHasIcon = false;
int totalSize = NavigationMenuPresenter.this.mMenu.getVisibleItems().size();
for (int i = 0; i < totalSize; i++) {
MenuItemImpl item = (MenuItemImpl) NavigationMenuPresenter.this.mMenu.getVisibleItems().get(i);
if (item.isChecked()) {
setCheckedItem(item);
}
if (item.isCheckable()) {
item.setExclusiveCheckable(false);
}
if (item.hasSubMenu()) {
SubMenu subMenu = item.getSubMenu();
if (subMenu.hasVisibleItems()) {
if (i != 0) {
this.mItems.add(new NavigationMenuSeparatorItem(NavigationMenuPresenter.this.mPaddingSeparator, 0));
}
this.mItems.add(new NavigationMenuTextItem(item));
boolean subMenuHasIcon = false;
int subMenuStart = this.mItems.size();
int size = subMenu.size();
for (int j = 0; j < size; j++) {
MenuItemImpl subMenuItem = (MenuItemImpl) subMenu.getItem(j);
if (subMenuItem.isVisible()) {
if (!(subMenuHasIcon || subMenuItem.getIcon() == null)) {
subMenuHasIcon = true;
}
if (subMenuItem.isCheckable()) {
subMenuItem.setExclusiveCheckable(false);
}
if (item.isChecked()) {
setCheckedItem(item);
}
this.mItems.add(new NavigationMenuTextItem(subMenuItem));
}
}
if (subMenuHasIcon) {
appendTransparentIconIfMissing(subMenuStart, this.mItems.size());
}
}
} else {
int groupId = item.getGroupId();
if (groupId != currentGroupId) {
currentGroupStart = this.mItems.size();
currentGroupHasIcon = item.getIcon() != null;
if (i != 0) {
currentGroupStart++;
this.mItems.add(new NavigationMenuSeparatorItem(NavigationMenuPresenter.this.mPaddingSeparator, NavigationMenuPresenter.this.mPaddingSeparator));
}
} else if (!(currentGroupHasIcon || item.getIcon() == null)) {
currentGroupHasIcon = true;
appendTransparentIconIfMissing(currentGroupStart, this.mItems.size());
}
if (currentGroupHasIcon && item.getIcon() == null) {
item.setIcon(17170445);
}
this.mItems.add(new NavigationMenuTextItem(item));
currentGroupId = groupId;
}
}
this.mUpdateSuspended = false;
}
}
private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
MenuItem item = ((NavigationMenuTextItem) this.mItems.get(i)).getMenuItem();
if (item.getIcon() == null) {
if (this.mTransparentIcon == null) {
this.mTransparentIcon = new ColorDrawable(17170445);
}
item.setIcon(this.mTransparentIcon);
}
}
}
public void setCheckedItem(MenuItemImpl checkedItem) {
if (this.mCheckedItem != checkedItem && checkedItem.isCheckable()) {
if (this.mCheckedItem != null) {
this.mCheckedItem.setChecked(false);
}
this.mCheckedItem = checkedItem;
checkedItem.setChecked(true);
}
}
public Bundle createInstanceState() {
Bundle state = new Bundle();
if (this.mCheckedItem != null) {
state.putInt(STATE_CHECKED_ITEM, this.mCheckedItem.getItemId());
}
SparseArray<ParcelableSparseArray> actionViewStates = new SparseArray();
Iterator i$ = this.mItems.iterator();
while (i$.hasNext()) {
NavigationMenuItem navigationMenuItem = (NavigationMenuItem) i$.next();
if (navigationMenuItem instanceof NavigationMenuTextItem) {
MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
View actionView = item != null ? item.getActionView() : null;
if (actionView != null) {
ParcelableSparseArray container = new ParcelableSparseArray();
actionView.saveHierarchyState(container);
actionViewStates.put(item.getItemId(), container);
}
}
}
state.putSparseParcelableArray(STATE_ACTION_VIEWS, actionViewStates);
return state;
}
public void restoreInstanceState(Bundle state) {
Iterator i$;
int checkedItem = state.getInt(STATE_CHECKED_ITEM, 0);
if (checkedItem != 0) {
this.mUpdateSuspended = true;
i$ = this.mItems.iterator();
while (i$.hasNext()) {
NavigationMenuItem item = (NavigationMenuItem) i$.next();
if (item instanceof NavigationMenuTextItem) {
MenuItemImpl menuItem = ((NavigationMenuTextItem) item).getMenuItem();
if (menuItem != null && menuItem.getItemId() == checkedItem) {
setCheckedItem(menuItem);
break;
}
}
}
this.mUpdateSuspended = false;
prepareMenuItems();
}
SparseArray<ParcelableSparseArray> actionViewStates = state.getSparseParcelableArray(STATE_ACTION_VIEWS);
i$ = this.mItems.iterator();
while (i$.hasNext()) {
NavigationMenuItem navigationMenuItem = (NavigationMenuItem) i$.next();
if (navigationMenuItem instanceof NavigationMenuTextItem) {
MenuItemImpl item2 = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
View actionView = item2 != null ? item2.getActionView() : null;
if (actionView != null) {
actionView.restoreHierarchyState((SparseArray) actionViewStates.get(item2.getItemId()));
}
}
}
}
public void setUpdateSuspended(boolean updateSuspended) {
this.mUpdateSuspended = updateSuspended;
}
}
private static class NavigationMenuHeaderItem implements NavigationMenuItem {
private NavigationMenuHeaderItem() {
}
}
private static class NavigationMenuSeparatorItem implements NavigationMenuItem {
private final int mPaddingBottom;
private final int mPaddingTop;
public NavigationMenuSeparatorItem(int paddingTop, int paddingBottom) {
this.mPaddingTop = paddingTop;
this.mPaddingBottom = paddingBottom;
}
public int getPaddingTop() {
return this.mPaddingTop;
}
public int getPaddingBottom() {
return this.mPaddingBottom;
}
}
private static class NavigationMenuTextItem implements NavigationMenuItem {
private final MenuItemImpl mMenuItem;
private NavigationMenuTextItem(MenuItemImpl item) {
this.mMenuItem = item;
}
public MenuItemImpl getMenuItem() {
return this.mMenuItem;
}
}
private static abstract class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
private static class HeaderViewHolder extends ViewHolder {
public HeaderViewHolder(View itemView) {
super(itemView);
}
}
private static class NormalViewHolder extends ViewHolder {
public NormalViewHolder(LayoutInflater inflater, ViewGroup parent, OnClickListener listener) {
super(inflater.inflate(R.layout.design_navigation_item, parent, false));
this.itemView.setOnClickListener(listener);
}
}
private static class SeparatorViewHolder extends ViewHolder {
public SeparatorViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.design_navigation_item_separator, parent, false));
}
}
private static class SubheaderViewHolder extends ViewHolder {
public SubheaderViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.design_navigation_item_subheader, parent, false));
}
}
public void initForMenu(Context context, MenuBuilder menu) {
this.mLayoutInflater = LayoutInflater.from(context);
this.mMenu = menu;
Resources res = context.getResources();
this.mPaddingTopDefault = res.getDimensionPixelOffset(R.dimen.design_navigation_padding_top_default);
this.mPaddingSeparator = res.getDimensionPixelOffset(R.dimen.design_navigation_separator_vertical_padding);
}
public MenuView getMenuView(ViewGroup root) {
if (this.mMenuView == null) {
this.mMenuView = (NavigationMenuView) this.mLayoutInflater.inflate(R.layout.design_navigation_menu, root, false);
if (this.mAdapter == null) {
this.mAdapter = new NavigationMenuAdapter();
}
this.mHeaderLayout = (LinearLayout) this.mLayoutInflater.inflate(R.layout.design_navigation_item_header, this.mMenuView, false);
this.mMenuView.setAdapter(this.mAdapter);
}
return this.mMenuView;
}
public void updateMenuView(boolean cleared) {
if (this.mAdapter != null) {
this.mAdapter.update();
}
}
public void setCallback(Callback cb) {
this.mCallback = cb;
}
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
return false;
}
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (this.mCallback != null) {
this.mCallback.onCloseMenu(menu, allMenusAreClosing);
}
}
public boolean flagActionItems() {
return false;
}
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
public int getId() {
return this.mId;
}
public void setId(int id) {
this.mId = id;
}
public Parcelable onSaveInstanceState() {
Bundle state = new Bundle();
if (this.mMenuView != null) {
SparseArray<Parcelable> hierarchy = new SparseArray();
this.mMenuView.saveHierarchyState(hierarchy);
state.putSparseParcelableArray("android:menu:list", hierarchy);
}
if (this.mAdapter != null) {
state.putBundle(STATE_ADAPTER, this.mAdapter.createInstanceState());
}
return state;
}
public void onRestoreInstanceState(Parcelable parcelable) {
Bundle state = (Bundle) parcelable;
SparseArray<Parcelable> hierarchy = state.getSparseParcelableArray("android:menu:list");
if (hierarchy != null) {
this.mMenuView.restoreHierarchyState(hierarchy);
}
Bundle adapterState = state.getBundle(STATE_ADAPTER);
if (adapterState != null) {
this.mAdapter.restoreInstanceState(adapterState);
}
}
public void setCheckedItem(MenuItemImpl item) {
this.mAdapter.setCheckedItem(item);
}
public View inflateHeaderView(@LayoutRes int res) {
View view = this.mLayoutInflater.inflate(res, this.mHeaderLayout, false);
addHeaderView(view);
return view;
}
public void addHeaderView(@NonNull View view) {
this.mHeaderLayout.addView(view);
this.mMenuView.setPadding(0, 0, 0, this.mMenuView.getPaddingBottom());
}
public void removeHeaderView(@NonNull View view) {
this.mHeaderLayout.removeView(view);
if (this.mHeaderLayout.getChildCount() == 0) {
this.mMenuView.setPadding(0, this.mPaddingTopDefault, 0, this.mMenuView.getPaddingBottom());
}
}
@Nullable
public ColorStateList getItemTintList() {
return this.mIconTintList;
}
public void setItemIconTintList(@Nullable ColorStateList tint) {
this.mIconTintList = tint;
updateMenuView(false);
}
@Nullable
public ColorStateList getItemTextColor() {
return this.mTextColor;
}
public void setItemTextColor(@Nullable ColorStateList textColor) {
this.mTextColor = textColor;
updateMenuView(false);
}
public void setItemTextAppearance(@StyleRes int resId) {
this.mTextAppearance = resId;
this.mTextAppearanceSet = true;
updateMenuView(false);
}
public Drawable getItemBackground() {
return this.mItemBackground;
}
public void setItemBackground(Drawable itemBackground) {
this.mItemBackground = itemBackground;
}
public void setUpdateSuspended(boolean updateSuspended) {
if (this.mAdapter != null) {
this.mAdapter.setUpdateSuspended(updateSuspended);
}
}
}