版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
Navigation 主要是配合 DrawerLayout 进行使用,作为 DrawerLayout 的侧滑菜单。Navigation 有分为上下两个部分,上部分是头部,下部分是菜单。
MainActivity :
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Activity 不需要写什么代码即可实现一个简单的 demo。
activity_main.xml:
"1.0" encoding="utf-8"?>
.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="com.xiaoyue.navigation.MainActivity">
"wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
.support.design.widget.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/head"
app:menu="@menu/view_menu">
.support.design.widget.NavigationView>
.support.v4.widget.DrawerLayout>
NavigationView 在这边作为 DrawerLayout 的侧滑菜单,属性 app:headerLayout 与 app:menu 分别是头部的布局文件与菜单的配置文件。
head.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="15dp"
android:src="@mipmap/ic_account_circle_white_48dp" />
<TextView
android:id="@+id/tvNickName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="15dp"
tools:text="JohnTsai"
android:text="登录"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
LinearLayout>
头部的布局文件。
view_menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/nav_me"
android:title="我"
android:icon="@mipmap/ic_mine_gray_24"
/>
<group>
<item
android:id="@+id/nav_friend"
android:title="好友"
android:icon="@mipmap/ic_friends_gray_24"/>
<item
android:id="@+id/nav_notification"
android:title="通知"
android:icon="@mipmap/ic_launcher"/>
<item
android:id="@+id/nav_message"
android:title="私信"
android:icon="@mipmap/ic_messages_gray_24"
/>
group>
<group
android:id="@+id/group_manage">
<item
android:id="@+id/nav_manage"
android:title="应用管理"
android:icon="@mipmap/ic_app_management_gray_24"/>
group>
menu>
位于 /res/menu 下,配置后会自动加载生成菜单。
上面红色区域是头部,下面是菜单部分。(头部图像由于截图的原因,较模糊)
这里会发现,下边菜单的所有图标都是灰色的,这个不是由于图标本身就是灰色的原因。通知这个菜单采用了安卓机器人的绿色小图标,结果显示出来的还是灰色的,这是安卓绘制的原因造成。
修改 activity_main.xml:
"1.0" encoding="utf-8"?>
.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="com.xiaoyue.navigation.MainActivity">
"wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
.support.design.widget.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/head"
app:menu="@menu/view_menu"
app:itemIconTint="@color/colorAccent">
.support.design.widget.NavigationView>
.support.v4.widget.DrawerLayout>
为 NavigationView 添加一个属性 app:itemIconTint。
这样图标是拥有颜色,但是所有的图标颜色全部变化成我们设置的颜色,这是谷歌提供的,但不是我们想要的。
如果说要显示其本来颜色的话,需要在在 MainActivity 中进行修改。
修改 MainActivity:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setItemIconTintList(null);
}
}
在代码中调用 navigationView.setItemIconTintList(null) 即可设置显示为原来的颜色。
NavigationView 的父类是 ScrimInsetsFrameLayout, ScrimInsetsFrameLayout 的父类是 FrameLayout,所以 NavigationView 最终也是继承于 FrameLayout。
这是 NavigationView 的两个成员变量,mPresenter 就是 MVP 中的 P 层。(MVP 不懂的可以看一下这个,传送门:http://blog.csdn.net/lmj623565791/article/details/46596109)
private final NavigationMenu mMenu;
private final NavigationMenuPresenter mPresenter = new NavigationMenuPresenter();
NavigationView 本身作为 C 层,主要是在构造函数中进行初始化初始化 NavigationView 本身样式,并没有进行数据的获取等操作。
NavigationView 构造函数:
public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
...
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();
}
NavigationView 通过 mPresenter 早先定义好的接口去获取数据,本身不与 M 层直接进行交互。NavigationView 的显示是通过 addView((View) mPresenter.getMenuView(this)) 直接添加进来的。
NavigationMenuPresenter 就是 P 层,获取数据,并刷新视图,负责 NavigationView 与数据之间的交互。
NavigationMenuPresenter 的 getMenuView:
public MenuView getMenuView(ViewGroup root) {
if (mMenuView == null) {
//NavigationView 的菜单部分
mMenuView = (NavigationMenuView) mLayoutInflater.inflate(
R.layout.design_navigation_menu, root, false);
if (mAdapter == null) {
mAdapter = new NavigationMenuAdapter();
}
//NavigationView 的头部
mHeaderLayout = (LinearLayout) mLayoutInflater
.inflate(R.layout.design_navigation_item_header,
mMenuView, false);
mMenuView.setAdapter(mAdapter);
}
return mMenuView;
}
分别看一下 NavigationView 的菜单部分和头部的布局。
design_navigation_menu.xml:
.support.design.internal.NavigationMenuView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/design_navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/design_navigation_padding_bottom"
android:clipToPadding="false"
android:scrollbars="vertical"/>
菜单部分布局就是一个 NavigationMenuView,在 NavigationMenuPresenter 的 getMenuView 中获取到这个 NavigationMenuView,并返回到 NavigationView 的构造方法中,同时直接添加到 NavigationView 中,即 NavigationView 下套了一层 NavigationMenuView。
点进去查看 NavigationMenuView 类,可以发现继承于 RecyclerView。那后面的用法就很明确了,添加 mAdapter,点进去也可以发现 NavigationMenuAdapter 就是 RecyclerView.Adapter< ViewHolder >。
NavigationMenuPresenter 的 getMenuView 中还有一个布局 design_navigation_item_header。
design_navigation_item_header:
<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 并没有把头部加到了 NavigationMenuView 里面)在 NavigationMenuAdapter 里面添加到 NavigationMenuView 中。
上张图展示一下 NavigationView 的布局架构:
最外层红色是 NavigationView 。
NavigationView 里面套了一层蓝色的 NavigationMenuView(RecyclerView 的子类),NavigationMenuView 里面加载的是一个 Adapter,Adapter 有多种 ViewType。上面黄色部分是一个 LinearLayout (),里面套了层黑色的是我么自己的布局;下面一部分是一些菜单的 item 。头部跟菜单都属于 Adapter。
返回到 NavigationView 的构造函数中继续往下。
NavigationView 构造函数:
public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, 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();
}
在最后进行判断是否在引用的时候添加了头部和菜单,有添加的话进行重新刷新。
点击查看 inflateMenu 代码:
NavigationView 的 inflateMenu:
public void inflateMenu(int resId) {
mPresenter.setUpdateSuspended(true);
//获取菜单数据
getMenuInflater().inflate(resId, mMenu);
mPresenter.setUpdateSuspended(false);
//刷新 adapter
mPresenter.updateMenuView(false);
}
resId 即我们在布局文件设置的 app:menu 属性的值,在 getMenuInflater().inflate(resId, mMenu) 中对菜单的配置文件进行解析,获取到菜单数据。