您的位置:首页 > 移动开发 > Android开发

Android 自己实现 NavigationView [Design Support Library(1)]

2016-01-23 20:47 615 查看

转载请标明出处:

/article/1580253.html

本文出自:【张鸿洋的博客】



一、概述

Google I/O 2015 给大家带来了Android Design Support Library。对于希望做md风格的app的来说,简直是天大的喜讯了~大家能够通过Android Design Support Library该文章对其进行了解,也能够直接在github上下载演示样例代码执行学习。为了表达我心中的喜悦,我决定针对该库写一系列的文章来分别介绍新添加的控件。

ok,那么首先介绍的就是NavigationView。

注意下更新下as的SDK,然后在使用的过程中,在build.gradle中加入:

compile 'com.android.support:design:22.2.0'


在md风格的app中,比如例如以下风格的側滑菜单非经常见:



在之前的设计中。你可能须要考虑怎样去布局实现,比如使用ListView;再者还要去设计Item的选中状态之类~~

but,如今。google提供了
NavigationView
,你只须要写写布局文件。这种效果就ok了,而且兼容到Android 2.1。非常值得去体验一下。

接下来我们来介绍怎样去使用这个
NavigationView


二、使用

使用起来very simple ,主要就是写写布局文件~

(一)布局文件

<?xml version="1.0" encoding="utf-8"?

>
<android.support.v4.widget.DrawerLayout
android:id="@+id/id_drawer_layout"
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:fitsSystemWindows="true"
>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
android:layout_width="match_parent"
android:layout_height="?

attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

<TextView
android:id="@+id/id_tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="HelloWorld"
android:textSize="30sp"/>
</RelativeLayout>

<android.support.design.widget.NavigationView
android:id="@+id/id_nv_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left"
android:fitsSystemWindows="true"
app:headerLayout="@layout/header_just_username"
app:menu="@menu/menu_drawer"
/>

</android.support.v4.widget.DrawerLayout>


能够看到我们的最外层是
DrawerLayout
。里面一个content。一个作为drawer。我们的drawer为
NavigationView


注意这个view的两个属性
app:headerLayout="@layout/header_just_username"
app:menu="@menu/menu_drawer"
,分别代表drawer布局中的header和menuitem区域。当然你能够依据自己的情况使用。

接下来看看header的布局文件和menu配置文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="?attr/colorPrimaryDark"
android:orientation="vertical"
android:padding="16dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">

<TextView
android:id="@+id/id_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="16dp"
android:text="http://blog.csdn.net/lmj623565791"/>

<TextView
android:id="@+id/id_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/id_link"
android:text="Zhang Hongyang"/>

<ImageView
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_above="@id/id_username"
android:layout_marginBottom="16dp"
android:src="@mipmap/icon"/>

</RelativeLayout>


<?

xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_dashboard"
android:title="Home"/>
<item
android:id="@+id/nav_messages"
android:icon="@drawable/ic_event"
android:title="Messages"/>
<item
android:id="@+id/nav_friends"
android:icon="@drawable/ic_headset"
android:title="Friends"/>
<item
android:id="@+id/nav_discussion"
android:icon="@drawable/ic_forum"
android:title="Discussion"/>
</group>

<item android:title="Sub items">
<menu>
<item
android:icon="@drawable/ic_dashboard"
android:title="Sub item 1"/>
<item
android:icon="@drawable/ic_forum"
android:title="Sub item 2"/>
</menu>
</item>

</menu>


别放错目录哈~

布局文件写完了,基本就好了,是不是非常爽~看似复杂的效果,写写布局文件就ok。

ps:默认的颜色非常多是从当前的主题中提取的。比方icon的stateColor,当然你也能够通过以下属性改动部分样式:

app:itemIconTint=""
app:itemBackground=""
app:itemTextColor=""


(二)Activity

最后是Activity:

package com.imooc.testandroid;

import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;

public class NavigationViewActivity extends ActionBarActivity
{

private DrawerLayout mDrawerLayout;
private NavigationView mNavigationView;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigation_view);

mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
mNavigationView = (NavigationView) findViewById(R.id.id_nv_menu);

Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
setSupportActionBar(toolbar);

final ActionBar ab = getSupportActionBar();
ab.setHomeAsUpIndicator(R.drawable.ic_menu);
ab.setDisplayHomeAsUpEnabled(true);

setupDrawerContent(mNavigationView);

}

private void setupDrawerContent(NavigationView navigationView)
{
navigationView.setNavigationItemSelectedListener(

new NavigationView.OnNavigationItemSelectedListener()
{

@Override
public boolean onNavigationItemSelected(MenuItem menuItem)
{
menuItem.setChecked(true);
mDrawerLayout.closeDrawers();
return true;
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_navigation_view, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if(item.getItemId() == android.R.id.home)
{
mDrawerLayout.openDrawer(GravityCompat.START);
return true ;
}
return super.onOptionsItemSelected(item);
}

}


我们在Activity里面能够通过navigationView去
navigationView.setNavigationItemSelectedListener
,当selected的时候。menuItem去setChecked(true)。

别忘了设置theme~

<resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>

<style name="Theme.DesignDemo" parent="Base.Theme.DesignDemo">
</style>

<style name="Base.Theme.DesignDemo" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#673AB7</item>
<item name="colorPrimaryDark">#512DA8</item>
<item name="colorAccent">#FF4081</item>
<item name="android:windowBackground">@color/window_background</item>
</style>

</resources>

<color name="window_background">#FFF5F5F5</color>

<activity
android:name=".NavigationViewActivity"
android:label="@string/title_activity_navigation_view"
android:theme="@style/Theme.DesignDemo">
</activity>


ok。到此就搞定了~~

不过存在一个问题,此时你假设点击Sub items里面的Sub item,假设你期望当前选中应该是Sub item。你会发现不起作用。那怎么办呢?

(三)Sub Item支持Cheable

这里能够改动menu的配置文件:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group>
<item
android:id="@+id/nav_home"
android:checkable="true"
android:icon="@drawable/ic_dashboard"
android:title="Home"/>
<item
android:id="@+id/nav_messages"
android:checkable="true"
android:icon="@drawable/ic_event"
android:title="Messages"/>
<item
android:id="@+id/nav_friends"
android:checkable="true"
android:icon="@drawable/ic_headset"
android:title="Friends"/>
<item
android:id="@+id/nav_discussion"
android:checkable="true"
android:icon="@drawable/ic_forum"
android:title="Discussion"/>
</group>

<item android:title="Sub items">
<menu>
<item
android:checkable="true"
android:icon="@drawable/ic_dashboard"
android:title="Sub item 1"/>
<item
android:checkable="true"
android:icon="@drawable/ic_forum"
android:title="Sub item 2"/>
</menu>
</item>

</menu>


android:checkableBehavior="single"
去掉,然后给每一个item项加入
android:checkable="true"


然后在代码中:

navigationView.setNavigationItemSelectedListener(

new NavigationView.OnNavigationItemSelectedListener()
{

private MenuItem mPreMenuItem;

@Override
public boolean onNavigationItemSelected(MenuItem menuItem)
{
if (mPreMenuItem != null) mPreMenuItem.setChecked(false);
menuItem.setChecked(true);
mDrawerLayout.closeDrawers();
mPreMenuItem = menuItem;
return true;
}
});


存一下preMenuItem。手动切换。

效果:



ok,哈~事实上这个还是參考链接2里面的一个评论说的~~

到此使用方法就介绍完了有木有一点小激动~ 可是还有个问题。对于app,就像ActionBar最初的出现,一開始大家都欢欣鼓励,后来发现app中多数情况下须要去定制,尼玛。是不是忽然认为ActionBar太死板了,恶心死了(当然了如今有了ToolBar灵活度上好多了)对于上述
NavigationView
可能也会存在定制上的问题。比方我希望选中的Item左边有个高亮的竖线之类的效果。

那么,针对于各种需求,想要能解决各种问题,最好的方式就是说对于
NavigationView
的效果自己能够实现。

最好,我们就来看看
NavigationView
自己实现有多难?

三、自己实现NavigationView效果

事实上
NavigationView
的实现非常easy,一个ListView就能够了。甚至都不须要去自己定义,简单写一个Adapter即可了~~



首先观察该图,有没有发现奇妙之处。恩,你肯定发现不了,由于我们做的太像了。

事实上这个图就是我通过ListView写的一个~是不是和原版非常像(~哈~參考了源代码的实现,当然像。)

接下来分析。假设说是ListView,那么Item的type肯定不止一种,那我们数一数种类:

带图标和文本的

不过文本的 Sub Items

切割线

你会说还有顶部那个。顶部是headview呀~~

这么分析完毕,是不是瞬间认为没有难度了~

(一)首先布局文件

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="@+id/id_drawer_layout"
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:fitsSystemWindows="true"
>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
android:layout_width="match_parent"
android:layout_height="?

attr/actionBarSize"
android:background="?

attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

<TextView
android:id="@+id/id_tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="HelloWorld"
android:textSize="30sp"/>
</RelativeLayout>

<ListView
android:id="@+id/id_lv_left_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:paddingTop="0dp"
android:background="#ffffffff"
android:clipToPadding="false"
android:divider="@null"
android:listSelector="?attr/selectableItemBackground"
/>

</android.support.v4.widget.DrawerLayout>


布局文件上:和上文对照,我们只把NavigationView换成了ListView.

以下注意了,一大波代码来袭.

(二) Activity

package com.imooc.testandroid;

public class NavListViewActivity extends ActionBarActivity
{
private ListView mLvLeftMenu;
private DrawerLayout mDrawerLayout;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nav_list_view);

mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
mLvLeftMenu = (ListView) findViewById(R.id.id_lv_left_menu);

Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
setSupportActionBar(toolbar);

final ActionBar ab = getSupportActionBar();
ab.setHomeAsUpIndicator(R.drawable.ic_menu);
ab.setDisplayHomeAsUpEnabled(true);

setUpDrawer();
}

private void setUpDrawer()
{
LayoutInflater inflater = LayoutInflater.from(this);
mLvLeftMenu.addHeaderView(inflater.inflate(R.layout.header_just_username, mLvLeftMenu, false));
mLvLeftMenu.setAdapter(new MenuItemAdapter(this));
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_nav_list_view, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == android.R.id.home)
{
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
}

return super.onOptionsItemSelected(item);
}

}


直接看onCreate中的
setUpDrawer()
,能够看到我们首先去addHeadView。然后去setAdapter。

那么核心代码就是我们的Adapter了~~

(三)MenuItemAdapter

public class LvMenuItem
{
public LvMenuItem(int icon, String name)
{
this.icon = icon;
this.name = name;

if (icon == NO_ICON && TextUtils.isEmpty(name))
{
type = TYPE_SEPARATOR;
} else if (icon == NO_ICON)
{
type = TYPE_NO_ICON;
} else
{
type = TYPE_NORMAL;
}

if (type != TYPE_SEPARATOR && TextUtils.isEmpty(name))
{
throw new IllegalArgumentException("you need set a name for a non-SEPARATOR item");
}

L.e(type + "");

}

public LvMenuItem(String name)
{
this(NO_ICON, name);
}

public LvMenuItem()
{
this(null);
}

private static final int NO_ICON = 0;
public static final int TYPE_NORMAL = 0;
public static final int TYPE_NO_ICON = 1;
public static final int TYPE_SEPARATOR = 2;

int type;
String name;
int icon;

}

public class MenuItemAdapter extends BaseAdapter
{
private final int mIconSize;
private LayoutInflater mInflater;
private Context mContext;

public MenuItemAdapter(Context context)
{
mInflater = LayoutInflater.from(context);
mContext = context;

mIconSize = context.getResources().getDimensionPixelSize(R.dimen.drawer_icon_size);//24dp
}

private List<LvMenuItem> mItems = new ArrayList<LvMenuItem>(
Arrays.asList(
new LvMenuItem(R.drawable.ic_dashboard, "Home"),
new LvMenuItem(R.drawable.ic_event, "Messages"),
new LvMenuItem(R.drawable.ic_headset, "Friends"),
new LvMenuItem(R.drawable.ic_forum, "Discussion"),
new LvMenuItem(),
new LvMenuItem("Sub Items"),
new LvMenuItem(R.drawable.ic_dashboard, "Sub Item 1"),
new LvMenuItem(R.drawable.ic_forum, "Sub Item 2")
));

@Override
public int getCount()
{
return mItems.size();
}

@Override
public Object getItem(int position)
{
return mItems.get(position);
}

@Override
public long getItemId(int position)
{
return position;
}

@Override
public int getViewTypeCount()
{
return 3;
}

@Override
public int getItemViewType(int position)
{
return mItems.get(position).type;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
LvMenuItem item = mItems.get(position);
switch (item.type)
{
case LvMenuItem.TYPE_NORMAL:
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.design_drawer_item, parent,
false);
}
TextView itemView = (TextView) convertView;
itemView.setText(item.name);
Drawable icon = mContext.getResources().getDrawable(item.icon);
setIconColor(icon);
if (icon != null)
{
icon.setBounds(0, 0, mIconSize, mIconSize);
TextViewCompat.setCompoundDrawablesRelative(itemView, icon, null, null, null);
}

break;
case LvMenuItem.TYPE_NO_ICON:
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.design_drawer_item_subheader,
parent, false);
}
TextView subHeader = (TextView) convertView;
subHeader.setText(item.name);
break;
case LvMenuItem.TYPE_SEPARATOR:
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.design_drawer_item_separator,
parent, false);
}
break;
}

return convertView;
}

public void setIconColor(Drawable icon)
{
int textColorSecondary = android.R.attr.textColorSecondary;
TypedValue value = new TypedValue();
if (!mContext.getTheme().resolveAttribute(textColorSecondary, value, true))
{
return;
}
int baseColor = mContext.getResources().getColor(value.resourceId);
icon.setColorFilter(baseColor, PorterDuff.Mode.MULTIPLY);
}
}


首先我们的每一个Item相应于一个LvMenuItem。包括icon、name、type。能够看到我们的type分为3类。

那么adapter中代码就非常easy了,多Item布局的写法。

这样就完毕了,我们自己去书写
NavigationView
,是不是非常easy~~假设你的app须要相似效果,可是又与
NavigationView
的效果并不是一模一样。能够考虑依照上面的思路自己写一个。

最后贴一下用到的Item的布局文件:

design_drawer_item_subheader.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?

attr/listPreferredItemHeightSmall"
android:gravity="center_vertical|start"
android:maxLines="1"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?

attr/listPreferredItemPaddingRight"
android:textAppearance="?

attr/textAppearanceListItem"
android:textColor="?android:textColorSecondary"/>


design_drawer_item_separator.xml

<?

xml version="1.0" encoding="utf-8"?

>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<View android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>

</FrameLayout>


design_drawer_item,xml:

<?

xml version="1.0" encoding="utf-8"?

>
<TextView
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="32dp"
android:gravity="center_vertical|start"
android:maxLines="1"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="?

android:attr/textColorPrimary"/>


ok。事实上上述ListView的写法也正是
NavigationView
的源代码实现~~item的布局文件直接从源代码中拖出来的,还是爽爽哒~

源代码点击下载

~~hava a nice day ~~


新浪微博

微信公众号:hongyangAndroid

(欢迎关注,第一时间推送博文信息)





參考链接

https://guides.codepath.com/android/Fragment-Navigation-Drawer

http://antonioleiva.com/navigation-view/

http://android-developers.blogspot.com/2015/05/android-design-support-library.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: