mirror of
https://github.com/material-components/material-components-android.git
synced 2026-02-20 08:39:55 +08:00
am a51925c0: am c7850385: Merge "Use RecyclerView for NavigationView" into mnc-ub-dev
* commit 'a51925c0e9094179d91ceef757d98a6fc9eb3fff': Use RecyclerView for NavigationView GitOrigin-RevId=87f9c127ea653d0865131cef284e468228bd2262 PiperOrigin-RevId: 140549964
This commit is contained in:
parent
a3a1436360
commit
72d44a5a70
24
Android.mk
24
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
|
||||
|
||||
@ -5,6 +5,7 @@ archivesBaseName = 'design'
|
||||
dependencies {
|
||||
compile project(':support-v4')
|
||||
compile project(':support-appcompat-v7')
|
||||
compile project(':support-recyclerview-v7')
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
-->
|
||||
<android.support.design.internal.NavigationMenuView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/design_navigation_padding_top_default"
|
||||
@ -23,4 +24,5 @@
|
||||
android:clipToPadding="false"
|
||||
android:divider="@null"
|
||||
android:scrollbars="vertical"
|
||||
android:listSelector="?attr/selectableItemBackground"/>
|
||||
android:listSelector="?attr/selectableItemBackground"
|
||||
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
|
||||
|
||||
@ -56,6 +56,12 @@
|
||||
<attr name="headerLayout" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ForegroundLinearLayout">
|
||||
<attr name="android:foreground" />
|
||||
<attr name="android:foregroundGravity" />
|
||||
<attr name="foregroundInsidePadding" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TabLayout">
|
||||
<attr name="tabIndicatorColor" format="color"/>
|
||||
<attr name="tabIndicatorHeight" format="dimension"/>
|
||||
|
||||
229
src/android/support/design/internal/ForegroundLinearLayout.java
Normal file
229
src/android/support/design/internal/ForegroundLinearLayout.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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(
|
||||
|
||||
@ -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<ViewHolder> {
|
||||
|
||||
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<NavigationMenuItem> 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<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);
|
||||
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<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()));
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user