am d3da39bb: am 2b9dabda: Merge "Initial implementation of NavigationDrawer" into lmp-mr1-ub-dev

* commit 'd3da39bbe4b007d3ee2dadd318d55b0e06697b89':
  Initial implementation of NavigationDrawer

GitOrigin-RevId=e06c9b9db020326166a5591c0483c03377418437

PiperOrigin-RevId: 140545176
This commit is contained in:
Yuichi Araki 2016-11-30 00:47:51 +00:00 committed by Travis Collins
parent 36b99e40c8
commit 30b1bcd3e6
10 changed files with 673 additions and 6 deletions

View File

@ -0,0 +1,26 @@
<?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.
-->
<android.support.design.internal.NavigationMenuItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:drawablePadding="@dimen/drawer_icon_padding"
android:gravity="center_vertical|start"
android:maxLines="1"
android:textAppearance="?attr/textAppearanceListItem"/>

View File

@ -0,0 +1,22 @@
<?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.
-->
<android.support.design.internal.NavigationMenuView
android:divider="@null"
android:listSelector="?attr/selectableItemBackground"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<declare-styleable name="FloatingActionButton">
@ -36,4 +35,15 @@
<attr name="pressedTranslationZ" format="dimension|reference" />
</declare-styleable>
</resources>
<declare-styleable name="ScrimInsetsFrameLayout">
<attr name="insetForeground" format="color|reference" />
</declare-styleable>
<declare-styleable name="NavigationDrawerView">
<attr name="android:elevation" />
<attr name="android:background" />
<attr name="android:fitsSystemWindows" />
<attr name="android:maxWidth" />
</declare-styleable>
</resources>

View File

@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<dimen name="fab_elevation">8dp</dimen>
@ -23,4 +22,9 @@
<dimen name="fab_size_normal">56dp</dimen>
<dimen name="fab_size_mini">40dp</dimen>
</resources>
<dimen name="drawer_max_width">320dp</dimen>
<dimen name="drawer_elevation">12dp</dimen>
<dimen name="drawer_icon_padding">32dp</dimen>
<dimen name="drawer_icon_size">24dp</dimen>
</resources>

View File

@ -14,7 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Widget.Design.FloatingActionButton" parent="android:Widget">
@ -27,4 +26,15 @@
<item name="rippleColor">?attr/colorControlHighlight</item>
</style>
</resources>
<style name="Widget.Design.ScrimInsetsFrameLayout" parent="">
<item name="insetForeground">#4000</item>
</style>
<style name="Widget.Design.NavigationDrawerView" parent="">
<item name="android:elevation">@dimen/drawer_elevation</item>
<item name="android:background">?android:attr/windowBackground</item>
<item name="android:fitsSystemWindows">true</item>
<item name="android:maxWidth">@dimen/drawer_max_width</item>
</style>
</resources>

View File

@ -0,0 +1,99 @@
/*
* 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.graphics.drawable.Drawable;
import android.support.design.R;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.internal.view.menu.MenuItemImpl;
import android.support.v7.internal.view.menu.MenuView;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
* @hide
*/
public class NavigationMenuItemView extends TextView implements MenuView.ItemView {
private MenuItemImpl mItemData;
private int mIconSize;
public NavigationMenuItemView(Context context) {
this(context, null);
}
public NavigationMenuItemView(Context context, AttributeSet attrs) {
super(context, attrs);
mIconSize = context.getResources().getDimensionPixelSize(R.dimen.drawer_icon_size);
}
@Override
public void initialize(MenuItemImpl itemData, int menuType) {
mItemData = itemData;
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
setTitle(itemData.getTitle());
setCheckable(itemData.isCheckable());
setIcon(itemData.getIcon());
setEnabled(itemData.isEnabled());
}
@Override
public MenuItemImpl getItemData() {
return mItemData;
}
@Override
public void setTitle(CharSequence title) {
setText(title);
}
@Override
public void setCheckable(boolean checkable) {
}
@Override
public void setChecked(boolean checked) {
}
@Override
public void setShortcut(boolean showShortcut, char shortcutKey) {
}
@Override
public void setIcon(Drawable icon) {
if (icon != null) {
icon.setBounds(0, 0, mIconSize, mIconSize);
}
TextViewCompat.setCompoundDrawablesRelative(this, icon, null, null, null);
}
@Override
public boolean prefersCondensedTitle() {
return false;
}
@Override
public boolean showsIcon() {
return true;
}
}

View File

@ -0,0 +1,160 @@
/*
* 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.os.Parcelable;
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.MenuView;
import android.support.v7.internal.view.menu.SubMenuBuilder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
/**
* @hide
*/
public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
private NavigationMenuView mMenuView;
private Callback mCallback;
private MenuBuilder mMenu;
private int mId;
private NavigationMenuAdapter mAdapter;
private LayoutInflater mLayoutInflater;
@Override
public void initForMenu(Context context, MenuBuilder menu) {
mLayoutInflater = LayoutInflater.from(context);
mMenu = menu;
}
@Override
public MenuView getMenuView(ViewGroup root) {
if (mMenuView == null) {
mMenuView = (NavigationMenuView) mLayoutInflater.inflate(
R.layout.design_drawer_menu, root, false);
if (mAdapter == null) {
mAdapter = new NavigationMenuAdapter();
}
mMenuView.setAdapter(mAdapter);
mMenuView.setOnItemClickListener(this);
}
return mMenuView;
}
@Override
public void updateMenuView(boolean cleared) {
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
@Override
public void setCallback(Callback cb) {
mCallback = cb;
}
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
return false;
}
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (mCallback != null) {
mCallback.onCloseMenu(menu, allMenusAreClosing);
}
}
@Override
public boolean flagActionItems() {
return false;
}
@Override
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
@Override
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
@Override
public int getId() {
return mId;
}
public void setId(int id) {
mId = id;
}
@Override
public Parcelable onSaveInstanceState() {
return null;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mMenu.performItemAction(mAdapter.getItem(position), this, 0);
}
private class NavigationMenuAdapter extends BaseAdapter {
@Override
public int getCount() {
return mMenu.size();
}
@Override
public MenuItemImpl getItem(int position) {
return (MenuItemImpl) mMenu.getItem(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.design_drawer_item, parent, false);
}
MenuView.ItemView itemView = (MenuView.ItemView) convertView;
itemView.initialize(getItem(position), 0);
return convertView;
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.support.v7.internal.view.menu.MenuBuilder;
import android.support.v7.internal.view.menu.MenuView;
import android.util.AttributeSet;
import android.widget.ListView;
/**
* @hide
*/
public class NavigationMenuView extends ListView implements MenuView {
public NavigationMenuView(Context context) {
this(context, null);
}
public NavigationMenuView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NavigationMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void initialize(MenuBuilder menu) {
}
@Override
public int getWindowAnimations() {
return 0;
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
/**
* @hide
*/
public class ScrimInsetsFrameLayout extends FrameLayout {
private Drawable mInsetForeground;
private Rect mInsets;
private Rect mTempRect = new Rect();
public ScrimInsetsFrameLayout(Context context) {
this(context, null);
}
public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ScrimInsetsFrameLayout, defStyleAttr,
R.style.Widget_Design_ScrimInsetsFrameLayout);
mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsFrameLayout_insetForeground);
a.recycle();
setWillNotDraw(true); // No need to draw until the insets are adjusted
ViewCompat.setOnApplyWindowInsetsListener(this,
new android.support.v4.view.OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
if (null == mInsets) {
mInsets = new Rect();
}
mInsets.set(insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
setWillNotDraw(mInsets.isEmpty() || mInsetForeground == null);
ViewCompat.postInvalidateOnAnimation(ScrimInsetsFrameLayout.this);
return insets.consumeSystemWindowInsets();
}
});
}
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
int width = getWidth();
int height = getHeight();
if (mInsets != null && mInsetForeground != null) {
int sc = canvas.save();
canvas.translate(getScrollX(), getScrollY());
// Top
mTempRect.set(0, 0, width, mInsets.top);
mInsetForeground.setBounds(mTempRect);
mInsetForeground.draw(canvas);
// Bottom
mTempRect.set(0, height - mInsets.bottom, width, height);
mInsetForeground.setBounds(mTempRect);
mInsetForeground.draw(canvas);
// Left
mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
mInsetForeground.setBounds(mTempRect);
mInsetForeground.draw(canvas);
// Right
mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
mInsetForeground.setBounds(mTempRect);
mInsetForeground.draw(canvas);
canvas.restoreToCount(sc);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mInsetForeground != null) {
mInsetForeground.setCallback(this);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mInsetForeground != null) {
mInsetForeground.setCallback(null);
}
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.R;
import android.support.design.internal.NavigationMenuPresenter;
import android.support.design.internal.ScrimInsetsFrameLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.internal.view.menu.MenuBuilder;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
/**
* Represents a standard navigation drawer for application. The drawer contents can be populated by
* a menu resource
* file.
* <p>NavigationDrawer needs to be placed inside a {@link android.support.v4.widget.DrawerLayout}.
* </p>
* <pre>
* &lt;android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* android:id="@+id/drawer_layout"
* android:layout_width="match_parent"
* android:layout_height="match_parent"
* android:fitsSystemWindows="true"&gt;
*
* &lt;!-- Your contents --&gt;
*
* &lt;android.support.design.widget.NavigationDrawerView
* android:id="@+id/navigation_drawer"
* android:layout_width="wrap_content"
* android:layout_height="match_parent"
* android:layout_gravity="start" /&gt;
* &lt;/android.support.v4.widget.DrawerLayout&gt;
* </pre>
*/
public class NavigationDrawerView extends ScrimInsetsFrameLayout {
private OnNavigationItemSelectedListener mListener;
private MenuBuilder mMenu;
private int mMaxWidth;
public NavigationDrawerView(Context context) {
this(context, null);
}
public NavigationDrawerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NavigationDrawerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Custom attributes
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.NavigationDrawerView, defStyleAttr,
R.style.Widget_Design_NavigationDrawerView);
//noinspection deprecation
setBackgroundDrawable(a.getDrawable(R.styleable.NavigationDrawerView_android_background));
ViewCompat.setElevation(this,
a.getDimensionPixelSize(R.styleable.NavigationDrawerView_android_elevation, 0));
ViewCompat.setFitsSystemWindows(this,
a.getBoolean(R.styleable.NavigationDrawerView_android_fitsSystemWindows, false));
mMaxWidth = a.getDimensionPixelSize(R.styleable.NavigationDrawerView_android_maxWidth, 0);
a.recycle();
// Set up the menu
mMenu = new MenuBuilder(context);
MenuBuilder.Callback menuCallback = new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
return mListener != null && mListener.onNavigationItemSelected(item);
}
@Override
public void onMenuModeChange(MenuBuilder menu) {
}
};
mMenu.setCallback(menuCallback);
NavigationMenuPresenter presenter = new NavigationMenuPresenter();
presenter.initForMenu(context, mMenu);
addView((View) presenter.getMenuView(this));
}
/**
* Set a listener that will be notified when a menu item is clicked.
*
* @param listener The listener to notify
*/
public void setNavigationItemSelectedListener(OnNavigationItemSelectedListener listener) {
mListener = listener;
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
switch (MeasureSpec.getMode(widthSpec)) {
case MeasureSpec.EXACTLY:
// Nothing to do
break;
case MeasureSpec.AT_MOST:
widthSpec = MeasureSpec.makeMeasureSpec(
Math.min(MeasureSpec.getSize(widthSpec), mMaxWidth), MeasureSpec.EXACTLY);
break;
case MeasureSpec.UNSPECIFIED:
widthSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
break;
}
// Let super sort out the height
super.onMeasure(widthSpec, heightSpec);
}
/**
* @return The {@link Menu} associated with this NavigationDrawerView.
*/
public Menu getMenu() {
return mMenu;
}
/**
* Listener for handling events on navigation drawer items.
*/
public interface OnNavigationItemSelectedListener {
/**
* Called when an item in the navigation drawer is selected.
*
* @param item The selected item
*/
public boolean onNavigationItemSelected(MenuItem item);
}
}