diff --git a/Android.mk b/Android.mk
index 0b0b2a843..f2fc797ee 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,10 +23,12 @@ LOCAL_MODULE := android-support-design-res
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
- frameworks/support/v7/appcompat/res
+ frameworks/support/v7/appcompat/res \
+ frameworks/support/v7/recyclerview/res
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
- --extra-packages android.support.v7.appcompat
+ --extra-packages android.support.v7.appcompat \
+ --extra-packages android.support.v7.recyclerview
LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -38,7 +40,8 @@ LOCAL_SDK_VERSION := 7
LOCAL_SRC_FILES := $(call all-java-files-under, base)
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# A helper sub-library that makes direct use of Eclair MR1 APIs
@@ -49,7 +52,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, eclair-mr1)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-base
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# A helper sub-library that makes direct use of Honeycomb APIs
@@ -60,7 +64,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-eclair-mr1
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# A helper sub-library that makes direct use of Honeycomb MR1 APIs
@@ -71,7 +76,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb-mr1)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# A helper sub-library that makes direct use of Lollipop APIs
@@ -82,7 +88,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, lollipop)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb-mr1
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# Here is the final static library that apps can link against.
@@ -96,7 +103,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-lollipop
LOCAL_JAVA_LIBRARIES := android-support-design-res \
android-support-v4 \
- android-support-v7-appcompat
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview
include $(BUILD_STATIC_JAVA_LIBRARY)
# API Check
diff --git a/build.gradle b/build.gradle
index 5f5482f51..e785f83c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,6 +5,7 @@ archivesBaseName = 'design'
dependencies {
compile project(':support-v4')
compile project(':support-appcompat-v7')
+ compile project(':support-recyclerview-v7')
}
android {
diff --git a/res/layout/design_navigation_item.xml b/res/layout/design_navigation_item.xml
index 14c1be3a0..3a6a90c6f 100644
--- a/res/layout/design_navigation_item.xml
+++ b/res/layout/design_navigation_item.xml
@@ -19,4 +19,5 @@
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
- android:paddingRight="?attr/listPreferredItemPaddingRight"/>
+ android:paddingRight="?attr/listPreferredItemPaddingRight"
+ android:foreground="?attr/selectableItemBackground"/>
diff --git a/res/layout/design_navigation_menu.xml b/res/layout/design_navigation_menu.xml
index 02b7aad1e..2a3bbf058 100644
--- a/res/layout/design_navigation_menu.xml
+++ b/res/layout/design_navigation_menu.xml
@@ -16,6 +16,7 @@
-->
+ android:listSelector="?attr/selectableItemBackground"
+ app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 785308a55..478c07b09 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -56,6 +56,12 @@
+
+
+
+
+
+
diff --git a/src/android/support/design/internal/ForegroundLinearLayout.java b/src/android/support/design/internal/ForegroundLinearLayout.java
new file mode 100644
index 000000000..373ba4806
--- /dev/null
+++ b/src/android/support/design/internal/ForegroundLinearLayout.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.internal;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.design.R;
+import android.support.v7.widget.LinearLayoutCompat;
+import android.util.AttributeSet;
+import android.view.Gravity;
+
+/**
+ * @hide
+ */
+public class ForegroundLinearLayout extends LinearLayoutCompat {
+
+ private Drawable mForeground;
+
+ private final Rect mSelfBounds = new Rect();
+
+ private final Rect mOverlayBounds = new Rect();
+
+ private int mForegroundGravity = Gravity.FILL;
+
+ protected boolean mForegroundInPadding = true;
+
+ boolean mForegroundBoundsChanged = false;
+
+ public ForegroundLinearLayout(Context context) {
+ super(context);
+ }
+
+ public ForegroundLinearLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout,
+ defStyle, 0);
+
+ mForegroundGravity = a.getInt(
+ R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity);
+
+ final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground);
+ if (d != null) {
+ setForeground(d);
+ }
+
+ mForegroundInPadding = a.getBoolean(
+ R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true);
+
+ a.recycle();
+ }
+
+ /**
+ * Describes how the foreground is positioned.
+ *
+ * @return foreground gravity.
+ * @see #setForegroundGravity(int)
+ */
+ public int getForegroundGravity() {
+ return mForegroundGravity;
+ }
+
+ /**
+ * Describes how the foreground is positioned. Defaults to START and TOP.
+ *
+ * @param foregroundGravity See {@link android.view.Gravity}
+ * @see #getForegroundGravity()
+ */
+ public void setForegroundGravity(int foregroundGravity) {
+ if (mForegroundGravity != foregroundGravity) {
+ if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+ foregroundGravity |= Gravity.START;
+ }
+
+ if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
+ foregroundGravity |= Gravity.TOP;
+ }
+
+ mForegroundGravity = foregroundGravity;
+
+ if (mForegroundGravity == Gravity.FILL && mForeground != null) {
+ Rect padding = new Rect();
+ mForeground.getPadding(padding);
+ }
+
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || (who == mForeground);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+ if (mForeground != null) {
+ mForeground.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mForeground != null && mForeground.isStateful()) {
+ mForeground.setState(getDrawableState());
+ }
+ }
+
+ /**
+ * Supply a Drawable that is to be rendered on top of all of the child
+ * views in the frame layout. Any padding in the Drawable will be taken
+ * into account by ensuring that the children are inset to be placed
+ * inside of the padding area.
+ *
+ * @param drawable The Drawable to be drawn on top of the children.
+ */
+ public void setForeground(Drawable drawable) {
+ if (mForeground != drawable) {
+ if (mForeground != null) {
+ mForeground.setCallback(null);
+ unscheduleDrawable(mForeground);
+ }
+
+ mForeground = drawable;
+
+ if (drawable != null) {
+ setWillNotDraw(false);
+ drawable.setCallback(this);
+ if (drawable.isStateful()) {
+ drawable.setState(getDrawableState());
+ }
+ if (mForegroundGravity == Gravity.FILL) {
+ Rect padding = new Rect();
+ drawable.getPadding(padding);
+ }
+ } else {
+ setWillNotDraw(true);
+ }
+ requestLayout();
+ invalidate();
+ }
+ }
+
+ /**
+ * Returns the drawable used as the foreground of this FrameLayout. The
+ * foreground drawable, if non-null, is always drawn on top of the children.
+ *
+ * @return A Drawable or null if no foreground was set.
+ */
+ public Drawable getForeground() {
+ return mForeground;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mForegroundBoundsChanged |= changed;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mForegroundBoundsChanged = true;
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ super.draw(canvas);
+
+ if (mForeground != null) {
+ final Drawable foreground = mForeground;
+
+ if (mForegroundBoundsChanged) {
+ mForegroundBoundsChanged = false;
+ final Rect selfBounds = mSelfBounds;
+ final Rect overlayBounds = mOverlayBounds;
+
+ final int w = getRight() - getLeft();
+ final int h = getBottom() - getTop();
+
+ if (mForegroundInPadding) {
+ selfBounds.set(0, 0, w, h);
+ } else {
+ selfBounds.set(getPaddingLeft(), getPaddingTop(),
+ w - getPaddingRight(), h - getPaddingBottom());
+ }
+
+ Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
+ foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
+ foreground.setBounds(overlayBounds);
+ }
+
+ foreground.draw(canvas);
+ }
+ }
+
+ @Override
+ public void drawableHotspotChanged(float x, float y) {
+ super.drawableHotspotChanged(x, y);
+ if (mForeground != null) {
+ mForeground.setHotspot(x, y);
+ }
+ }
+
+}
diff --git a/src/android/support/design/internal/NavigationMenuItemView.java b/src/android/support/design/internal/NavigationMenuItemView.java
index 1d819df3d..1809789bc 100644
--- a/src/android/support/design/internal/NavigationMenuItemView.java
+++ b/src/android/support/design/internal/NavigationMenuItemView.java
@@ -27,7 +27,6 @@ import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.internal.view.menu.MenuItemImpl;
import android.support.v7.internal.view.menu.MenuView;
-import android.support.v7.widget.LinearLayoutCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -39,7 +38,7 @@ import android.widget.FrameLayout;
/**
* @hide
*/
-public class NavigationMenuItemView extends LinearLayoutCompat implements MenuView.ItemView {
+public class NavigationMenuItemView extends ForegroundLinearLayout implements MenuView.ItemView {
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
@@ -69,8 +68,6 @@ public class NavigationMenuItemView extends LinearLayoutCompat implements MenuVi
R.dimen.design_navigation_icon_size);
mTextView = (CheckedTextView) findViewById(R.id.design_menu_item_text);
mTextView.setDuplicateParentStateEnabled(true);
- // Prevent the action view from stealing the event on the item row.
- setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
}
@Override
@@ -91,6 +88,13 @@ public class NavigationMenuItemView extends LinearLayoutCompat implements MenuVi
setActionView(itemData.getActionView());
}
+ public void recycle() {
+ if (mActionArea != null) {
+ mActionArea.removeAllViews();
+ }
+ mTextView.setCompoundDrawables(null, null, null, null);
+ }
+
private void setActionView(View actionView) {
if (mActionArea == null) {
mActionArea = (FrameLayout) ((ViewStub) findViewById(
diff --git a/src/android/support/design/internal/NavigationMenuPresenter.java b/src/android/support/design/internal/NavigationMenuPresenter.java
index e79a421b7..f238bcb2c 100644
--- a/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -33,14 +33,13 @@ import android.support.v7.internal.view.menu.MenuItemImpl;
import android.support.v7.internal.view.menu.MenuPresenter;
import android.support.v7.internal.view.menu.MenuView;
import android.support.v7.internal.view.menu.SubMenuBuilder;
+import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -49,13 +48,13 @@ import java.util.ArrayList;
/**
* @hide
*/
-public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
+public class NavigationMenuPresenter implements MenuPresenter {
private static final String STATE_HIERARCHY = "android:menu:list";
private static final String STATE_ADAPTER = "android:menu:adapter";
private NavigationMenuView mMenuView;
- private LinearLayout mHeader;
+ private LinearLayout mHeaderLayout;
private Callback mCallback;
private MenuBuilder mMenu;
@@ -100,11 +99,10 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
if (mAdapter == null) {
mAdapter = new NavigationMenuAdapter();
}
- mHeader = (LinearLayout) mLayoutInflater.inflate(R.layout.design_navigation_item_header,
- mMenuView, false);
- mMenuView.addHeaderView(mHeader, null, false);
+ mHeaderLayout = (LinearLayout) mLayoutInflater
+ .inflate(R.layout.design_navigation_item_header,
+ mMenuView, false);
mMenuView.setAdapter(mAdapter);
- mMenuView.setOnItemClickListener(this);
}
return mMenuView;
}
@@ -112,7 +110,7 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
@Override
public void updateMenuView(boolean cleared) {
if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
+ mAdapter.update();
}
}
@@ -184,40 +182,25 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
}
}
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- int positionInAdapter = position - mMenuView.getHeaderViewsCount();
- if (positionInAdapter >= 0) {
- setUpdateSuspended(true);
- MenuItemImpl item = mAdapter.getItem(positionInAdapter).getMenuItem();
- boolean result = mMenu.performItemAction(item, this, 0);
- if (item != null && item.isCheckable() && result) {
- mAdapter.setCheckedItem(item);
- }
- setUpdateSuspended(false);
- updateMenuView(false);
- }
- }
-
public void setCheckedItem(MenuItemImpl item) {
mAdapter.setCheckedItem(item);
}
public View inflateHeaderView(@LayoutRes int res) {
- View view = mLayoutInflater.inflate(res, mHeader, false);
+ View view = mLayoutInflater.inflate(res, mHeaderLayout, false);
addHeaderView(view);
return view;
}
public void addHeaderView(@NonNull View view) {
- mHeader.addView(view);
+ mHeaderLayout.addView(view);
// The padding on top should be cleared.
mMenuView.setPadding(0, 0, 0, mMenuView.getPaddingBottom());
}
public void removeHeaderView(@NonNull View view) {
- mHeader.removeView(view);
- if (mHeader.getChildCount() == 0) {
+ mHeaderLayout.removeView(view);
+ if (mHeaderLayout.getChildCount() == 0) {
mMenuView.setPadding(0, mPaddingTopDefault, 0, mMenuView.getPaddingBottom());
}
}
@@ -262,7 +245,69 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
}
}
- private class NavigationMenuAdapter extends BaseAdapter {
+ private abstract static class ViewHolder extends RecyclerView.ViewHolder {
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ }
+
+ private static class NormalViewHolder extends ViewHolder {
+
+ public NormalViewHolder(LayoutInflater inflater, ViewGroup parent,
+ View.OnClickListener listener) {
+ super(inflater.inflate(R.layout.design_navigation_item, parent, false));
+ itemView.setOnClickListener(listener);
+ }
+
+ }
+
+ private static class SubheaderViewHolder extends ViewHolder {
+
+ public SubheaderViewHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.design_navigation_item_subheader, parent, false));
+ }
+
+ }
+
+ 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 HeaderViewHolder extends ViewHolder {
+
+ public HeaderViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ }
+
+ /**
+ * Handles click events for the menu items. The items has to be {@link NavigationMenuItemView}.
+ */
+ private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ NavigationMenuItemView itemView = (NavigationMenuItemView) v;
+ setUpdateSuspended(true);
+ MenuItemImpl item = itemView.getItemData();
+ boolean result = mMenu.performItemAction(item, NavigationMenuPresenter.this, 0);
+ if (item != null && item.isCheckable() && result) {
+ mAdapter.setCheckedItem(item);
+ }
+ setUpdateSuspended(false);
+ updateMenuView(false);
+ }
+
+ };
+
+ private class NavigationMenuAdapter extends RecyclerView.Adapter {
private static final String STATE_CHECKED_ITEM = "android:menu:checked";
@@ -270,6 +315,7 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SUBHEADER = 1;
private static final int VIEW_TYPE_SEPARATOR = 2;
+ private static final int VIEW_TYPE_HEADER = 3;
private final ArrayList mItems = new ArrayList<>();
private MenuItemImpl mCheckedItem;
@@ -280,49 +326,54 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
prepareMenuItems();
}
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public NavigationMenuItem getItem(int position) {
- return mItems.get(position);
- }
-
@Override
public long getItemId(int position) {
return position;
}
@Override
- public int getViewTypeCount() {
- return 3;
+ public int getItemCount() {
+ return mItems.size();
}
@Override
public int getItemViewType(int position) {
- NavigationMenuItem item = getItem(position);
- if (item.isSeparator()) {
+ NavigationMenuItem item = mItems.get(position);
+ if (item instanceof NavigationMenuSeparatorItem) {
return VIEW_TYPE_SEPARATOR;
- } else if (item.getMenuItem().hasSubMenu()) {
- return VIEW_TYPE_SUBHEADER;
- } else {
- return VIEW_TYPE_NORMAL;
+ } else if (item instanceof NavigationMenuHeaderItem) {
+ return VIEW_TYPE_HEADER;
+ } else if (item instanceof NavigationMenuTextItem) {
+ NavigationMenuTextItem textItem = (NavigationMenuTextItem) item;
+ if (textItem.getMenuItem().hasSubMenu()) {
+ return VIEW_TYPE_SUBHEADER;
+ } else {
+ return VIEW_TYPE_NORMAL;
+ }
}
+ throw new RuntimeException("Unknown item type.");
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- NavigationMenuItem item = getItem(position);
- int viewType = getItemViewType(position);
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_NORMAL:
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(R.layout.design_navigation_item,
- parent, false);
- }
- NavigationMenuItemView itemView = (NavigationMenuItemView) convertView;
+ return new NormalViewHolder(mLayoutInflater, parent, mOnClickListener);
+ case VIEW_TYPE_SUBHEADER:
+ return new SubheaderViewHolder(mLayoutInflater, parent);
+ case VIEW_TYPE_SEPARATOR:
+ return new SeparatorViewHolder(mLayoutInflater, parent);
+ case VIEW_TYPE_HEADER:
+ return new HeaderViewHolder(mHeaderLayout);
+ }
+ return null;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ switch (getItemViewType(position)) {
+ case VIEW_TYPE_NORMAL: {
+ NavigationMenuItemView itemView = (NavigationMenuItemView) holder.itemView;
itemView.setIconTintList(mIconTintList);
if (mTextAppearanceSet) {
itemView.setTextAppearance(itemView.getContext(), mTextAppearance);
@@ -332,42 +383,40 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
}
itemView.setBackgroundDrawable(mItemBackground != null ?
mItemBackground.getConstantState().newDrawable() : null);
+ NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
itemView.initialize(item.getMenuItem(), 0);
break;
- case VIEW_TYPE_SUBHEADER:
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(
- R.layout.design_navigation_item_subheader, parent, false);
- }
- TextView subHeader = (TextView) convertView;
+ }
+ case VIEW_TYPE_SUBHEADER: {
+ TextView subHeader = (TextView) holder.itemView;
+ NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
subHeader.setText(item.getMenuItem().getTitle());
break;
- case VIEW_TYPE_SEPARATOR:
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(
- R.layout.design_navigation_item_separator, parent, false);
- }
- convertView.setPadding(0, item.getPaddingTop(), 0,
+ }
+ case VIEW_TYPE_SEPARATOR: {
+ NavigationMenuSeparatorItem item =
+ (NavigationMenuSeparatorItem) mItems.get(position);
+ holder.itemView.setPadding(0, item.getPaddingTop(), 0,
item.getPaddingBottom());
break;
+ }
+ case VIEW_TYPE_HEADER: {
+ break;
+ }
}
- return convertView;
+
}
@Override
- public boolean areAllItemsEnabled() {
- return false;
+ public void onViewRecycled(ViewHolder holder) {
+ if (holder instanceof NormalViewHolder) {
+ ((NavigationMenuItemView) holder.itemView).recycle();
+ }
}
- @Override
- public boolean isEnabled(int position) {
- return getItem(position).isEnabled();
- }
-
- @Override
- public void notifyDataSetChanged() {
+ public void update() {
prepareMenuItems();
- super.notifyDataSetChanged();
+ notifyDataSetChanged();
}
/**
@@ -380,6 +429,8 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
}
mUpdateSuspended = true;
mItems.clear();
+ mItems.add(new NavigationMenuHeaderItem());
+
int currentGroupId = -1;
int currentGroupStart = 0;
boolean currentGroupHasIcon = false;
@@ -395,9 +446,9 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
SubMenu subMenu = item.getSubMenu();
if (subMenu.hasVisibleItems()) {
if (i != 0) {
- mItems.add(NavigationMenuItem.separator(mPaddingSeparator, 0));
+ mItems.add(new NavigationMenuSeparatorItem(mPaddingSeparator, 0));
}
- mItems.add(NavigationMenuItem.of(item));
+ mItems.add(new NavigationMenuTextItem(item));
boolean subMenuHasIcon = false;
int subMenuStart = mItems.size();
for (int j = 0, size = subMenu.size(); j < size; j++) {
@@ -412,7 +463,7 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
if (item.isChecked()) {
setCheckedItem(item);
}
- mItems.add(NavigationMenuItem.of(subMenuItem));
+ mItems.add(new NavigationMenuTextItem(subMenuItem));
}
}
if (subMenuHasIcon) {
@@ -426,7 +477,7 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
currentGroupHasIcon = item.getIcon() != null;
if (i != 0) {
currentGroupStart++;
- mItems.add(NavigationMenuItem.separator(
+ mItems.add(new NavigationMenuSeparatorItem(
mPaddingSeparator, mPaddingSeparator));
}
} else if (!currentGroupHasIcon && item.getIcon() != null) {
@@ -436,7 +487,7 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
if (currentGroupHasIcon && item.getIcon() == null) {
item.setIcon(android.R.color.transparent);
}
- mItems.add(NavigationMenuItem.of(item));
+ mItems.add(new NavigationMenuTextItem(item));
currentGroupId = groupId;
}
}
@@ -445,7 +496,8 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
- MenuItem item = mItems.get(i).getMenuItem();
+ NavigationMenuTextItem textItem = (NavigationMenuTextItem) mItems.get(i);
+ MenuItem item = textItem.getMenuItem();
if (item.getIcon() == null) {
if (mTransparentIcon == null) {
mTransparentIcon = new ColorDrawable(android.R.color.transparent);
@@ -474,12 +526,14 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
// Store the states of the action views.
SparseArray actionViewStates = new SparseArray<>();
for (NavigationMenuItem navigationMenuItem : mItems) {
- MenuItemImpl item = navigationMenuItem.getMenuItem();
- View actionView = item != null ? item.getActionView() : null;
- if (actionView != null) {
- ParcelableSparseArray container = new ParcelableSparseArray();
- actionView.saveHierarchyState(container);
- actionViewStates.put(item.getItemId(), container);
+ 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);
@@ -491,10 +545,12 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
if (checkedItem != 0) {
mUpdateSuspended = true;
for (NavigationMenuItem item : mItems) {
- MenuItemImpl menuItem = item.getMenuItem();
- if (menuItem != null && menuItem.getItemId() == checkedItem) {
- setCheckedItem(menuItem);
- break;
+ if (item instanceof NavigationMenuTextItem) {
+ MenuItemImpl menuItem = ((NavigationMenuTextItem) item).getMenuItem();
+ if (menuItem != null && menuItem.getItemId() == checkedItem) {
+ setCheckedItem(menuItem);
+ break;
+ }
}
}
mUpdateSuspended = false;
@@ -504,10 +560,12 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
SparseArray actionViewStates = state
.getSparseParcelableArray(STATE_ACTION_VIEWS);
for (NavigationMenuItem navigationMenuItem : mItems) {
- MenuItemImpl item = navigationMenuItem.getMenuItem();
- View actionView = item != null ? item.getActionView() : null;
- if (actionView != null) {
- actionView.restoreHierarchyState(actionViewStates.get(item.getItemId()));
+ if (navigationMenuItem instanceof NavigationMenuTextItem) {
+ MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
+ View actionView = item != null ? item.getActionView() : null;
+ if (actionView != null) {
+ actionView.restoreHierarchyState(actionViewStates.get(item.getItemId()));
+ }
}
}
}
@@ -519,37 +577,42 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
}
/**
- * Wraps {@link MenuItemImpl}. This allows separators to be counted as items in list.
+ * Unified data model for all sorts of navigation menu items.
*/
- private static class NavigationMenuItem {
+ private interface NavigationMenuItem {
+ }
+
+ /**
+ * Normal or subheader items.
+ */
+ private static class NavigationMenuTextItem implements NavigationMenuItem {
- /** The item; null for separators */
private final MenuItemImpl mMenuItem;
- /** Padding top; used only for separators */
+ private NavigationMenuTextItem(MenuItemImpl item) {
+ mMenuItem = item;
+ }
+
+ public MenuItemImpl getMenuItem() {
+ return mMenuItem;
+ }
+
+ }
+
+ /**
+ * Separator items.
+ */
+ private static class NavigationMenuSeparatorItem implements NavigationMenuItem {
+
private final int mPaddingTop;
- /** Padding bottom; used only for separators */
private final int mPaddingBottom;
- private NavigationMenuItem(MenuItemImpl item, int paddingTop, int paddingBottom) {
- mMenuItem = item;
+ public NavigationMenuSeparatorItem(int paddingTop, int paddingBottom) {
mPaddingTop = paddingTop;
mPaddingBottom = paddingBottom;
}
- public static NavigationMenuItem of(MenuItemImpl item) {
- return new NavigationMenuItem(item, 0, 0);
- }
-
- public static NavigationMenuItem separator(int paddingTop, int paddingBottom) {
- return new NavigationMenuItem(null, paddingTop, paddingBottom);
- }
-
- public boolean isSeparator() {
- return mMenuItem == null;
- }
-
public int getPaddingTop() {
return mPaddingTop;
}
@@ -558,15 +621,13 @@ public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnIte
return mPaddingBottom;
}
- public MenuItemImpl getMenuItem() {
- return mMenuItem;
- }
-
- public boolean isEnabled() {
- // Separators and subheaders never respond to click
- return mMenuItem != null && !mMenuItem.hasSubMenu() && mMenuItem.isEnabled();
- }
+ }
+ /**
+ * Header (not subheader) items.
+ */
+ private static class NavigationMenuHeaderItem implements NavigationMenuItem {
+ // The actual content is hold by NavigationMenuPresenter#mHeaderLayout.
}
}
diff --git a/src/android/support/design/internal/NavigationMenuView.java b/src/android/support/design/internal/NavigationMenuView.java
index 054b800c7..50649b70a 100644
--- a/src/android/support/design/internal/NavigationMenuView.java
+++ b/src/android/support/design/internal/NavigationMenuView.java
@@ -19,13 +19,13 @@ package android.support.design.internal;
import android.content.Context;
import android.support.v7.internal.view.menu.MenuBuilder;
import android.support.v7.internal.view.menu.MenuView;
+import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
-import android.widget.ListView;
/**
* @hide
*/
-public class NavigationMenuView extends ListView implements MenuView {
+public class NavigationMenuView extends RecyclerView implements MenuView {
public NavigationMenuView(Context context) {
this(context, null);