NavigationView now supports app:actionLayout
The specified layout is inflated as an extra item in the menu row.
Bug: 22837324
Change-Id: Iaa0921c45395dd779429052019761e59d66c34be
diff --git a/design/res/layout/design_menu_item_action_area.xml b/design/res/layout/design_menu_item_action_area.xml
new file mode 100644
index 0000000..ba8141d
--- /dev/null
+++ b/design/res/layout/design_menu_item_action_area.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
diff --git a/design/res/layout/design_navigation_item.xml b/design/res/layout/design_navigation_item.xml
index 3fcd74a..14c1be3 100644
--- a/design/res/layout/design_navigation_item.xml
+++ b/design/res/layout/design_navigation_item.xml
@@ -19,8 +19,4 @@
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
- android:paddingRight="?attr/listPreferredItemPaddingRight"
- android:drawablePadding="@dimen/design_navigation_icon_padding"
- android:gravity="center_vertical|start"
- android:maxLines="1"
- android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
+ android:paddingRight="?attr/listPreferredItemPaddingRight"/>
diff --git a/design/res/layout/design_navigation_menu_item.xml b/design/res/layout/design_navigation_menu_item.xml
new file mode 100644
index 0000000..91104bb1
--- /dev/null
+++ b/design/res/layout/design_navigation_menu_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <CheckedTextView
+ android:id="@+id/design_menu_item_text"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:drawablePadding="@dimen/design_navigation_icon_padding"
+ android:gravity="center_vertical|start"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
+
+ <ViewStub
+ android:id="@+id/design_menu_item_action_area_stub"
+ android:inflatedId="@+id/design_menu_item_action_area"
+ android:layout="@layout/design_menu_item_action_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+</merge>
diff --git a/design/src/android/support/design/internal/NavigationMenuItemView.java b/design/src/android/support/design/internal/NavigationMenuItemView.java
index 7813163..1d819df 100644
--- a/design/src/android/support/design/internal/NavigationMenuItemView.java
+++ b/design/src/android/support/design/internal/NavigationMenuItemView.java
@@ -27,19 +27,30 @@
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.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.CheckedTextView;
+import android.widget.FrameLayout;
/**
* @hide
*/
-public class NavigationMenuItemView extends TextView implements MenuView.ItemView {
+public class NavigationMenuItemView extends LinearLayoutCompat implements MenuView.ItemView {
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
- private int mIconSize;
+ private final int mIconSize;
+
+ private final CheckedTextView mTextView;
+
+ private FrameLayout mActionArea;
+
private MenuItemImpl mItemData;
+
private ColorStateList mIconTintList;
public NavigationMenuItemView(Context context) {
@@ -52,8 +63,14 @@
public NavigationMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ setOrientation(HORIZONTAL);
+ LayoutInflater.from(context).inflate(R.layout.design_navigation_menu_item, this, true);
mIconSize = context.getResources().getDimensionPixelSize(
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
@@ -71,6 +88,18 @@
setEnabled(itemData.isEnabled());
setTitle(itemData.getTitle());
setIcon(itemData.getIcon());
+ setActionView(itemData.getActionView());
+ }
+
+ private void setActionView(View actionView) {
+ if (mActionArea == null) {
+ mActionArea = (FrameLayout) ((ViewStub) findViewById(
+ R.id.design_menu_item_action_area_stub)).inflate();
+ }
+ mActionArea.removeAllViews();
+ if (actionView != null) {
+ mActionArea.addView(actionView);
+ }
}
private StateListDrawable createDefaultBackground() {
@@ -91,7 +120,7 @@
@Override
public void setTitle(CharSequence title) {
- setText(title);
+ mTextView.setText(title);
}
@Override
@@ -102,6 +131,7 @@
@Override
public void setChecked(boolean checked) {
refreshDrawableState();
+ mTextView.setChecked(checked);
}
@Override
@@ -115,7 +145,7 @@
icon.setBounds(0, 0, mIconSize, mIconSize);
DrawableCompat.setTintList(icon, mIconTintList);
}
- TextViewCompat.setCompoundDrawablesRelative(this, icon, null, null, null);
+ TextViewCompat.setCompoundDrawablesRelative(mTextView, icon, null, null, null);
}
@Override
@@ -144,4 +174,13 @@
setIcon(mItemData.getIcon());
}
}
+
+ public void setTextAppearance(Context context, int textAppearance) {
+ mTextView.setTextAppearance(context, textAppearance);
+ }
+
+ public void setTextColor(ColorStateList colors) {
+ mTextView.setTextColor(colors);
+ }
+
}
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index e523c38..41b361b 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -266,6 +266,7 @@
private static final String STATE_CHECKED_ITEM = "android:menu:checked";
+ private static final String STATE_ACTION_VIEWS = "android:menu:action_views";
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SUBHEADER = 1;
private static final int VIEW_TYPE_SEPARATOR = 2;
@@ -470,6 +471,18 @@
if (mCheckedItem != null) {
state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
}
+ // Store the states of the action views.
+ SparseArray<ParcelableSparseArray> 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);
+ }
+ }
+ state.putSparseParcelableArray(STATE_ACTION_VIEWS, actionViewStates);
return state;
}
@@ -479,7 +492,7 @@
mUpdateSuspended = true;
for (NavigationMenuItem item : mItems) {
MenuItemImpl menuItem = item.getMenuItem();
- if (menuItem != null && menuItem.getItemId() == checkedItem) {
+ if (menuItem != null && menuItem.getItemId() == checkedItem) {
setCheckedItem(menuItem);
break;
}
@@ -487,6 +500,16 @@
mUpdateSuspended = false;
prepareMenuItems();
}
+ // Restore the states of the action views.
+ SparseArray<ParcelableSparseArray> 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()));
+ }
+ }
}
public void setUpdateSuspended(boolean updateSuspended) {
diff --git a/design/src/android/support/design/internal/ParcelableSparseArray.java b/design/src/android/support/design/internal/ParcelableSparseArray.java
new file mode 100644
index 0000000..74abcc2
--- /dev/null
+++ b/design/src/android/support/design/internal/ParcelableSparseArray.java
@@ -0,0 +1,76 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+/**
+ * @hide
+ */
+public class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
+
+ public ParcelableSparseArray() {
+ super();
+ }
+
+ public ParcelableSparseArray(Parcel source) {
+ super();
+ int size = source.readInt();
+ int[] keys = new int[size];
+ source.readIntArray(keys);
+ Parcelable[] values = source.readParcelableArray(
+ ParcelableSparseArray.class.getClassLoader());
+ for (int i = 0; i < size; ++i) {
+ put(keys[i], values[i]);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ int size = size();
+ int[] keys = new int[size];
+ Parcelable[] values = new Parcelable[size];
+ for (int i = 0; i < size; ++i) {
+ keys[i] = keyAt(i);
+ values[i] = valueAt(i);
+ }
+ parcel.writeInt(size);
+ parcel.writeIntArray(keys);
+ parcel.writeParcelableArray(values, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelableSparseArray> CREATOR
+ = new Creator<ParcelableSparseArray>() {
+ @Override
+ public ParcelableSparseArray createFromParcel(Parcel source) {
+ return new ParcelableSparseArray(source);
+ }
+
+ @Override
+ public ParcelableSparseArray[] newArray(int size) {
+ return new ParcelableSparseArray[size];
+ }
+ };
+
+}