NavigationView也是design包下一个组件,一般用来和DrawerLayout配合使用,基本使用方法也很简单,直接在xml中使用就可以
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Hello World!" />
<android.support.design.widget.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/layout_header"
app:menu="@menu/menu"></android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
app:headerLayout用来加载头部,app:menu用来加载目录
layout_header.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_account_circle_black_24dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="登录" />
</LinearLayout>
menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="我的" />
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="安全" />
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="收藏" />
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="设置" />
</menu>
运行后的效果:
NavigationView.gif目录中想要加分割线的话,可以在menu.xml中添加group节点
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="我的" />
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="安全" />
<group android:id="@+id/gp">
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="收藏" />
<item
android:icon="@drawable/ic_account_circle_black_24dp"
android:title="设置" />
</group>
</menu>
注意要给group一个id,否则不会显示分割线
NavigationView.gifNavigationView会默认的给目录下的每个item的icon设置成灰色,如果我们想要原始图的颜色,需要在代码中调用
nv_slide.setItemIconTintList(null);
NavigationView.gif
接下来分析NavigationView的源码,它采用了MVP设计模式,写的非常好,首先看它的构造方法
private final NavigationMenu mMenu;
private final NavigationMenuPresenter mPresenter = new NavigationMenuPresenter();
public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ThemeUtils.checkAppCompatTheme(context);
// Create the menu
mMenu = new NavigationMenu(context);
// Custom attributes
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
R.styleable.NavigationView, defStyleAttr,
R.style.Widget_Design_NavigationView);
ViewCompat.setBackground(
this, a.getDrawable(R.styleable.NavigationView_android_background));
if (a.hasValue(R.styleable.NavigationView_elevation)) {
ViewCompat.setElevation(this, a.getDimensionPixelSize(
R.styleable.NavigationView_elevation, 0));
}
ViewCompat.setFitsSystemWindows(this,
a.getBoolean(R.styleable.NavigationView_android_fitsSystemWindows, false));
mMaxWidth = a.getDimensionPixelSize(R.styleable.NavigationView_android_maxWidth, 0);
final ColorStateList itemIconTint;
if (a.hasValue(R.styleable.NavigationView_itemIconTint)) {
itemIconTint = a.getColorStateList(R.styleable.NavigationView_itemIconTint);
} else {
itemIconTint = createDefaultColorStateList(android.R.attr.textColorSecondary);
}
boolean textAppearanceSet = false;
int textAppearance = 0;
if (a.hasValue(R.styleable.NavigationView_itemTextAppearance)) {
textAppearance = a.getResourceId(R.styleable.NavigationView_itemTextAppearance, 0);
textAppearanceSet = true;
}
ColorStateList itemTextColor = null;
if (a.hasValue(R.styleable.NavigationView_itemTextColor)) {
itemTextColor = a.getColorStateList(R.styleable.NavigationView_itemTextColor);
}
if (!textAppearanceSet && itemTextColor == null) {
// If there isn't a text appearance set, we'll use a default text color
itemTextColor = createDefaultColorStateList(android.R.attr.textColorPrimary);
}
final Drawable itemBackground = a.getDrawable(R.styleable.NavigationView_itemBackground);
mMenu.setCallback(new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
return mListener != null && mListener.onNavigationItemSelected(item);
}
@Override
public void onMenuModeChange(MenuBuilder menu) {}
});
mPresenter.setId(PRESENTER_NAVIGATION_VIEW_ID);
mPresenter.initForMenu(context, mMenu);
mPresenter.setItemIconTintList(itemIconTint);
if (textAppearanceSet) {
mPresenter.setItemTextAppearance(textAppearance);
}
mPresenter.setItemTextColor(itemTextColor);
mPresenter.setItemBackground(itemBackground);
mMenu.addMenuPresenter(mPresenter);
addView((View) mPresenter.getMenuView(this));
if (a.hasValue(R.styleable.NavigationView_menu)) {
inflateMenu(a.getResourceId(R.styleable.NavigationView_menu, 0));
}
if (a.hasValue(R.styleable.NavigationView_headerLayout)) {
inflateHeaderView(a.getResourceId(R.styleable.NavigationView_headerLayout, 0));
}
a.recycle();
}
其中NavigationMenu作为Model层,NavigationMenuPresenter 作为Presenter层,NavigationView作为View层,首先构造方法创建了一个Model,并通过mPresenter.initForMenu(context, mMenu);把它传给Presenter
@Override
public void initForMenu(Context context, MenuBuilder menu) {
mLayoutInflater = LayoutInflater.from(context);
mMenu = menu;
Resources res = context.getResources();
mPaddingSeparator = res.getDimensionPixelOffset(
R.dimen.design_navigation_separator_vertical_padding);
}
NavigationView作为View层,它的代码量不多,基本全是丢给NavigationMenuPresenter 处理,在initForMenu方法调用之后,Presenter与Model建立联系,然后又调用了addView((View) mPresenter.getMenuView(this));这个方法从Presenter层获取View并添加到了View层
@Override
public MenuView getMenuView(ViewGroup root) {
if (mMenuView == null) {
mMenuView = (NavigationMenuView) mLayoutInflater.inflate(
R.layout.design_navigation_menu, root, false);
if (mAdapter == null) {
mAdapter = new NavigationMenuAdapter();
}
mHeaderLayout = (LinearLayout) mLayoutInflater
.inflate(R.layout.design_navigation_item_header,
mMenuView, false);
mMenuView.setAdapter(mAdapter);
}
return mMenuView;
}
追踪NavigationMenuView发现,它就是一个RecyclerView
public class NavigationMenuView extends RecyclerView 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);
setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
}
@Override
public void initialize(MenuBuilder menu) {
}
@Override
public int getWindowAnimations() {
return 0;
}
}
getMenuView方法又对mHeaderLayout 进行了赋值,我们查看design_navigation_item_header布局文件
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/design_navigation_separator_vertical_padding" />
就是一个LinearLayout,之后getMenuView方法又给RecyclerView设置了Adapter,这个设配器我们回头再看,回到NavigationView的构造方法
public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
...
if (a.hasValue(R.styleable.NavigationView_menu)) {
inflateMenu(a.getResourceId(R.styleable.NavigationView_menu, 0));
}
if (a.hasValue(R.styleable.NavigationView_headerLayout)) {
inflateHeaderView(a.getResourceId(R.styleable.NavigationView_headerLayout, 0));
}
a.recycle();
}
判断了下我们有没有在xml中设置menu和headerLayout,再看它对这两个自定义属性的处理,首先看inflateMenu方法
/**
* Inflate a menu resource into this navigation view.
*
* <p>Existing items in the menu will not be modified or removed.</p>
*
* @param resId ID of a menu resource to inflate
*/
public void inflateMenu(int resId) {
mPresenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId, mMenu);
mPresenter.setUpdateSuspended(false);
mPresenter.updateMenuView(false);
}
其中将menu的资源文件id和mMenu(Model层),传给了xml解析器
/**
* Inflate a menu hierarchy from the specified XML resource. Throws
* {@link InflateException} if there is an error.
*
* @param menuRes Resource ID for an XML layout resource to load (e.g.,
* <code>R.menu.main_activity</code>)
* @param menu The Menu to inflate into. The items and submenus will be
* added to this Menu.
*/
public void inflate(@MenuRes int menuRes, Menu menu) {
XmlResourceParser parser = null;
try {
parser = mContext.getResources().getLayout(menuRes);
AttributeSet attrs = Xml.asAttributeSet(parser);
parseMenu(parser, attrs, menu);
} catch (XmlPullParserException e) {
throw new InflateException("Error inflating menu XML", e);
} catch (IOException e) {
throw new InflateException("Error inflating menu XML", e);
} finally {
if (parser != null) parser.close();
}
}
/**
* Called internally to fill the given menu. If a sub menu is seen, it will
* call this recursively.
*/
private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
throws XmlPullParserException, IOException {
MenuState menuState = new MenuState(menu);
int eventType = parser.getEventType();
String tagName;
boolean lookingForEndOfUnknownTag = false;
String unknownTagName = null;
// This loop will skip to the menu start tag
do {
if (eventType == XmlPullParser.START_TAG) {
tagName = parser.getName();
if (tagName.equals(XML_MENU)) {
// Go to next tag
eventType = parser.next();
break;
}
throw new RuntimeException("Expecting menu, got " + tagName);
}
eventType = parser.next();
} while (eventType != XmlPullParser.END_DOCUMENT);
boolean reachedEndOfMenu = false;
while (!reachedEndOfMenu) {
switch (eventType) {
case XmlPullParser.START_TAG:
if (lookingForEndOfUnknownTag) {
break;
}
tagName = parser.getName();
if (tagName.equals(XML_GROUP)) {
menuState.readGroup(attrs);
} else if (tagName.equals(XML_ITEM)) {
menuState.readItem(attrs);
} else if (tagName.equals(XML_MENU)) {
// A menu start tag denotes a submenu for an item
SubMenu subMenu = menuState.addSubMenuItem();
registerMenu(subMenu, attrs);
// Parse the submenu into returned SubMenu
parseMenu(parser, attrs, subMenu);
} else {
lookingForEndOfUnknownTag = true;
unknownTagName = tagName;
}
break;
case XmlPullParser.END_TAG:
tagName = parser.getName();
if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
lookingForEndOfUnknownTag = false;
unknownTagName = null;
} else if (tagName.equals(XML_GROUP)) {
menuState.resetGroup();
} else if (tagName.equals(XML_ITEM)) {
// Add the item if it hasn't been added (if the item was
// a submenu, it would have been added already)
if (!menuState.hasAddedItem()) {
if (menuState.itemActionProvider != null &&
menuState.itemActionProvider.hasSubMenu()) {
registerMenu(menuState.addSubMenuItem(), attrs);
} else {
registerMenu(menuState.addItem(), attrs);
}
}
} else if (tagName.equals(XML_MENU)) {
reachedEndOfMenu = true;
}
break;
case XmlPullParser.END_DOCUMENT:
throw new RuntimeException("Unexpected end of document");
}
eventType = parser.next();
}
}
MenuState是MenuInflater的一个内部类,最终会将所有item都添加给mMenu,实现数据的绑定,inflateMenu方法接下来又调用了mPresenter.updateMenuView(false);
@Override
public void updateMenuView(boolean cleared) {
if (mAdapter != null) {
mAdapter.update();
}
}
暂时Adapter先不看,我们在构造方法中继续往下看
public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
...
if (a.hasValue(R.styleable.NavigationView_headerLayout)) {
inflateHeaderView(a.getResourceId(R.styleable.NavigationView_headerLayout, 0));
}
a.recycle();
}
/**
* Inflates a View and add it as a header of the navigation menu.
*
* @param res The layout resource ID.
* @return a newly inflated View.
*/
public View inflateHeaderView(@LayoutRes int res) {
return mPresenter.inflateHeaderView(res);
}
又调用了Presenter的inflateHeaderView方法,将我们headerLayout的资源id传入
public View inflateHeaderView(@LayoutRes int res) {
View view = mLayoutInflater.inflate(res, mHeaderLayout, false);
addHeaderView(view);
return view;
}
public void addHeaderView(@NonNull View view) {
mHeaderLayout.addView(view);
// The padding on top should be cleared.
mMenuView.setPadding(0, 0, 0, mMenuView.getPaddingBottom());
}
最终我们的headerLayout被添加到mHeaderLayout对象中
我们现在来看这个Adapter,它就是一个内部类,我们核心关注onCreateViewHolder方法和update方法
private class NavigationMenuAdapter extends RecyclerView.Adapter<ViewHolder> {
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;
private static final int VIEW_TYPE_HEADER = 3;
private final ArrayList<NavigationMenuItem> mItems = new ArrayList<>();
private MenuItemImpl mCheckedItem;
private boolean mUpdateSuspended;
NavigationMenuAdapter() {
prepareMenuItems();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public int getItemViewType(int position) {
NavigationMenuItem item = mItems.get(position);
if (item instanceof NavigationMenuSeparatorItem) {
return VIEW_TYPE_SEPARATOR;
} 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 ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_NORMAL:
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(mTextAppearance);
}
if (mTextColor != null) {
itemView.setTextColor(mTextColor);
}
ViewCompat.setBackground(itemView, mItemBackground != null ?
mItemBackground.getConstantState().newDrawable() : null);
NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
itemView.initialize(item.getMenuItem(), 0);
break;
}
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: {
NavigationMenuSeparatorItem item =
(NavigationMenuSeparatorItem) mItems.get(position);
holder.itemView.setPadding(0, item.getPaddingTop(), 0,
item.getPaddingBottom());
break;
}
case VIEW_TYPE_HEADER: {
break;
}
}
}
@Override
public void onViewRecycled(ViewHolder holder) {
if (holder instanceof NormalViewHolder) {
((NavigationMenuItemView) holder.itemView).recycle();
}
}
public void update() {
prepareMenuItems();
notifyDataSetChanged();
}
/**
* Flattens the visible menu items of {@link #mMenu} into {@link #mItems},
* while inserting separators between items when necessary.
*/
private void prepareMenuItems() {
if (mUpdateSuspended) {
return;
}
mUpdateSuspended = true;
mItems.clear();
mItems.add(new NavigationMenuHeaderItem());
int currentGroupId = -1;
int currentGroupStart = 0;
boolean currentGroupHasIcon = false;
for (int i = 0, totalSize = mMenu.getVisibleItems().size(); i < totalSize; i++) {
MenuItemImpl item = mMenu.getVisibleItems().get(i);
if (item.isChecked()) {
setCheckedItem(item);
}
if (item.isCheckable()) {
item.setExclusiveCheckable(false);
}
if (item.hasSubMenu()) {
SubMenu subMenu = item.getSubMenu();
if (subMenu.hasVisibleItems()) {
if (i != 0) {
mItems.add(new NavigationMenuSeparatorItem(mPaddingSeparator, 0));
}
mItems.add(new NavigationMenuTextItem(item));
boolean subMenuHasIcon = false;
int subMenuStart = mItems.size();
for (int j = 0, size = subMenu.size(); j < size; j++) {
MenuItemImpl subMenuItem = (MenuItemImpl) subMenu.getItem(j);
if (subMenuItem.isVisible()) {
if (!subMenuHasIcon && subMenuItem.getIcon() != null) {
subMenuHasIcon = true;
}
if (subMenuItem.isCheckable()) {
subMenuItem.setExclusiveCheckable(false);
}
if (item.isChecked()) {
setCheckedItem(item);
}
mItems.add(new NavigationMenuTextItem(subMenuItem));
}
}
if (subMenuHasIcon) {
appendTransparentIconIfMissing(subMenuStart, mItems.size());
}
}
} else {
int groupId = item.getGroupId();
if (groupId != currentGroupId) { // first item in group
currentGroupStart = mItems.size();
currentGroupHasIcon = item.getIcon() != null;
if (i != 0) {
currentGroupStart++;
mItems.add(new NavigationMenuSeparatorItem(
mPaddingSeparator, mPaddingSeparator));
}
} else if (!currentGroupHasIcon && item.getIcon() != null) {
currentGroupHasIcon = true;
appendTransparentIconIfMissing(currentGroupStart, mItems.size());
}
NavigationMenuTextItem textItem = new NavigationMenuTextItem(item);
textItem.needsEmptyIcon = currentGroupHasIcon;
mItems.add(textItem);
currentGroupId = groupId;
}
}
mUpdateSuspended = false;
}
private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
NavigationMenuTextItem textItem = (NavigationMenuTextItem) mItems.get(i);
textItem.needsEmptyIcon = true;
}
}
public void setCheckedItem(MenuItemImpl checkedItem) {
if (mCheckedItem == checkedItem || !checkedItem.isCheckable()) {
return;
}
if (mCheckedItem != null) {
mCheckedItem.setChecked(false);
}
mCheckedItem = checkedItem;
checkedItem.setChecked(true);
}
public Bundle createInstanceState() {
Bundle state = new Bundle();
if (mCheckedItem != null) {
state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
}
// Store the states of the action views.
SparseArray<ParcelableSparseArray> actionViewStates = new SparseArray<>();
for (int i = 0, size = mItems.size(); i < size; i++) {
NavigationMenuItem navigationMenuItem = mItems.get(i);
if (navigationMenuItem instanceof NavigationMenuTextItem) {
MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
View actionView = item != null ? item.getActionView() : null;
if (actionView != null) {
ParcelableSparseArray container = new ParcelableSparseArray();
actionView.saveHierarchyState(container);
actionViewStates.put(item.getItemId(), container);
}
}
}
state.putSparseParcelableArray(STATE_ACTION_VIEWS, actionViewStates);
return state;
}
public void restoreInstanceState(Bundle state) {
int checkedItem = state.getInt(STATE_CHECKED_ITEM, 0);
if (checkedItem != 0) {
mUpdateSuspended = true;
for (int i = 0, size = mItems.size(); i < size; i++) {
NavigationMenuItem item = mItems.get(i);
if (item instanceof NavigationMenuTextItem) {
MenuItemImpl menuItem = ((NavigationMenuTextItem) item).getMenuItem();
if (menuItem != null && menuItem.getItemId() == checkedItem) {
setCheckedItem(menuItem);
break;
}
}
}
mUpdateSuspended = false;
prepareMenuItems();
}
// Restore the states of the action views.
SparseArray<ParcelableSparseArray> actionViewStates = state
.getSparseParcelableArray(STATE_ACTION_VIEWS);
if (actionViewStates != null) {
for (int i = 0, size = mItems.size(); i < size; i++) {
NavigationMenuItem navigationMenuItem = mItems.get(i);
if (!(navigationMenuItem instanceof NavigationMenuTextItem)) {
continue;
}
MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
if (item == null) {
continue;
}
View actionView = item.getActionView();
if (actionView == null) {
continue;
}
ParcelableSparseArray container = actionViewStates.get(item.getItemId());
if (container == null) {
continue;
}
actionView.restoreHierarchyState(container);
}
}
}
public void setUpdateSuspended(boolean updateSuspended) {
mUpdateSuspended = updateSuspended;
}
}
这边只把onCreateViewHolder方法和update方法单独贴出来
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_NORMAL:
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;
}
当viewType是VIEW_TYPE_HEADER,显示mHeaderLayout,也就是头部布局,上面分析的时候,在解析menu布局时,最后调用了adpater的update方法,我们来看下update方法
public void update() {
prepareMenuItems();
notifyDataSetChanged();
}
/**
* Flattens the visible menu items of {@link #mMenu} into {@link #mItems},
* while inserting separators between items when necessary.
*/
private void prepareMenuItems() {
if (mUpdateSuspended) {
return;
}
mUpdateSuspended = true;
mItems.clear();
mItems.add(new NavigationMenuHeaderItem());
int currentGroupId = -1;
int currentGroupStart = 0;
boolean currentGroupHasIcon = false;
for (int i = 0, totalSize = mMenu.getVisibleItems().size(); i < totalSize; i++) {
MenuItemImpl item = mMenu.getVisibleItems().get(i);
if (item.isChecked()) {
setCheckedItem(item);
}
if (item.isCheckable()) {
item.setExclusiveCheckable(false);
}
if (item.hasSubMenu()) {
SubMenu subMenu = item.getSubMenu();
if (subMenu.hasVisibleItems()) {
if (i != 0) {
mItems.add(new NavigationMenuSeparatorItem(mPaddingSeparator, 0));
}
mItems.add(new NavigationMenuTextItem(item));
boolean subMenuHasIcon = false;
int subMenuStart = mItems.size();
for (int j = 0, size = subMenu.size(); j < size; j++) {
MenuItemImpl subMenuItem = (MenuItemImpl) subMenu.getItem(j);
if (subMenuItem.isVisible()) {
if (!subMenuHasIcon && subMenuItem.getIcon() != null) {
subMenuHasIcon = true;
}
if (subMenuItem.isCheckable()) {
subMenuItem.setExclusiveCheckable(false);
}
if (item.isChecked()) {
setCheckedItem(item);
}
mItems.add(new NavigationMenuTextItem(subMenuItem));
}
}
if (subMenuHasIcon) {
appendTransparentIconIfMissing(subMenuStart, mItems.size());
}
}
} else {
int groupId = item.getGroupId();
if (groupId != currentGroupId) { // first item in group
currentGroupStart = mItems.size();
currentGroupHasIcon = item.getIcon() != null;
if (i != 0) {
currentGroupStart++;
mItems.add(new NavigationMenuSeparatorItem(
mPaddingSeparator, mPaddingSeparator));
}
} else if (!currentGroupHasIcon && item.getIcon() != null) {
currentGroupHasIcon = true;
appendTransparentIconIfMissing(currentGroupStart, mItems.size());
}
NavigationMenuTextItem textItem = new NavigationMenuTextItem(item);
textItem.needsEmptyIcon = currentGroupHasIcon;
mItems.add(textItem);
currentGroupId = groupId;
}
}
mUpdateSuspended = false;
}
prepareMenuItems方法中通过MenuItemImpl item = mMenu.getVisibleItems().get(i);将数据从mMenu(Model层)中取出,放到了mItems集合中,就是我们常用的RecyclerView的套路,到此我们对NavigationView的布局结构就很清晰了,如下图:
NavigationView布局结构.pngNavigationView类结构图.png
网友评论