为了解决Android平台的界面风格长期难以统一的问题,Google I/O大会推出了一套全新的界面设计语言——Material Design
,并且提供了一个Design Support
库
ToolBar
我们之前写的每一个项目都是默认带有ActionBar
的,在权限清单中,指定了:
位于<application android:theme="@style/AppTheme"> </application>
res/values/styles.xml
文件中:
指定了父主题,并且重写了三个颜色<resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resource>
我们需要将原来的主题屏蔽掉,将styles.xml文件中的parent改为Theme.AppCompat.Light.NoActionBar
(浅色主题)或者Theme.AppCompat.NoActionBar
(深色主题)
然后就可以使用ToolBar
代替ActionBar
了
直接在布局文件中使用Toolbar就可以:
然后在活动中使用这个Toolbar:<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" >
如果要修改Toolbar的显示内容,在权限清单中指定Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar);
android:label
属性即可:<activity android:label="Fruits"> </activity>
- 结合
menu
使用
首先在res目录下新建一个menu
目录,然后在menu
目录下新建一个Menu resource file
名为:toolbar.xml
:<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/backup" android:icon="@drawable/ic_backup" android:title="Backup" app:showAsAction="always"/> <item android:id="@+id/delete" android:icon="@drawable/ic_delete" android:title="Delete" app:showAsAction="ifRoom"/> <item android:id="@+id/settings" android:icon="@drawable/ic_settings" android:title="Settings" app:showAsAction="ifRoom"/> </menu>
showAsAction
属性用来指定是否显示图标:always,ifRoon,never
要想使用该menu,要在活动中重写boolean onCreateOptionsMenu(Menu menu)和void onOptionsItemSelected(MenuItem item)
:public boolean onCreateOptionsMenu(Menu menu){ getMenuInflater().inflate(R.menu.toolbar,menu ); return true; } public boolean onOptionsItemSelected(MenuItem item){ switch(item.getItemId()){ case R.id.backup: } return true; }
总结:
- 在普通的ActionBar中,使用menu的话,menu的具体的几个选项不会分开显示;而在Toolbar中,menu的具体选项会分开显示;
- 除此之外使用方法是一样的,都要在主函数中重写两个方法;
- 在Toolbar中的item多了一个app:showAsAction方法,用来设置每个选项的显示方式。
-
滑动菜单
DrawerLayout
DrawerLayout是一个布局,它允许加入两个直接子控件,第一个子控件是主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容.
第2个控件中必须要指定android:layout_gravity=“start”,end,left,fight -
目前滑动菜单必须要用户手动滑动才可以,但是有的用户可能并不知道可以滑动,所以我们可以用一个导航按钮来提示用户,当点击了该按钮后,滑动菜单的内容也可以显示出来
只需要在活动的初始化方法中加入如下代码即可:private DrawerLayout mDrawerLayout; public void onCreate(){ mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout); ActionBar actionBar = getSupportActionBar(); if(actionBar !=null){ actionBar.setDisplayHomeAsUpEnabled(true);//让导航按钮显示出来 actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);//设置导航按钮图标 } } @Override public boolean OnOptionsItemSelected(MenuItem item){ switch(item.getItemId()){ case android.R.id.home: mDrawerLayout.openDrawer(GravityCompat.START); break; } }
此处,导航按钮的id始终是
android.R.id.home
。
调用DrawerLayout对象的openDrawer()可以让滑动菜单显示出来,方法要求传入一个Gravity
参数,我们直接传入Gravity.START
,让它的行为与XML文件中定义的行为一致 -
定制滑动菜单(使用
NavigationView
)
NavigationView
是Design support
库提供的,所以先在build.gradle
里面添加依赖:implementation 'com.google.android.material:material:1.0.0' implementation 'de.hdodenhof:circleimageview:3.1.0'
第一个是
Design Support
库,第二个是一个实现图片圆形化的开源项目CircleImageView
,项目地址是:https://github.com/hdodenhof/CircleImageView在开始使用导航界面以前,我们还需要准备好两个东西:
menu
和headerLayout
在menu文件夹下新建一个menu文件,名为:nav_menu
,编写如下代码:<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <!--表示一个组,single,all,none--> <item android:id="@+id/nav_call" android:icon="@drawable/nav_call" android:title="Call"/> <item android:id="@+id/nav_friends" android:icon="@drawable/nav_friends" android:title="Friends"/> <item android:id="@+id/nav_location" android:icon="@drawable/nav_location" android:title="Location"/> <item android:id="@+id/nav_mail" android:icon="@drawable/nav_mail" android:title="Mail"/> <item android:id="@+id/nav_task" android:icon="@drawable/nav_task" android:title="Tasks"/> </group> </menu>
```
接下来在layout文件夹下新建一个名为nav_header.xml的文件,这是一个可以随意定制的布局
<?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="180dp"
android:padding="10dp"
android:background="?attr/colorPrimary">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/icon_image"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/nav_icon"
android:layout_centerInParent="true"/>
<TextView
android:id="@+id/mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="yzc35326@gmail.com"
android:textColor="#fff"
android:textSize="14sp"/>
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/mail"
android:text="Zy"
android:textAllCaps="false"
android:textColor="#fff"
android:textSize="14sp"/>
</RelativeLayout>
```
```
menu和headerLayout都准备好了,接下来就可以使用NavigationView了
修改activity_main.xml中的代码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawaer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
</FrameLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_menu"指定menu
app:headerLayout="@layout/nav_header"/>指定headerLayout
</androidx.drawerlayout.widget.DrawerLayout>
```
```
处理菜单项中的点击事件
NavigationView navView = (NavigationView)findViewById(R.id.nav_view);
navView.setCheckedItem(R.id.nav_call);//将Call菜单项设为选中
navView.setNavigationItemSelectedListener(new NavigationView.onNavigationItemSelectedListener(){
@Override
public boolean onNavigationItemSelected(MenuItem item){
mDrawerLayout.closeDrawers();
return true;
});
```
悬浮按钮和可交互提示
悬浮按钮:FloatingActionButton
在xml文件中使用:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawaer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_done"
app:elevation="8dp"/>//指定高度值,高度越大,投影范围越大,但是投影越淡
</FrameLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_menu"指定menu
app:headerLayout="@layout/nav_header"/>指定headerLayout
</androidx.drawerlayout.widget.DrawerLayout>
然而FloatingActionButton注册点击的方法和普通的按钮一样……
FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Toast.makeText(MainActivity.this, "Data restored", Toast.LENGTH_SHORT).show();
}
});
一个类似于Toast的工具:Snackbar
fab.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Snackbar.make(v,"Data deleted",Snackbar.LENGTH_SHORT)
.setAction("Undo",new View.OnClickListener(){
@Override
public void onClick(View v){
Toast.makeToast(MainActivity.this,"Data restored",Toast.LENGTH.SHORT).show();
}
}).show();
}
}
);
但是当前这种方式,显示Snackbar会将悬浮按钮给遮挡住,这需要借助CoordinatorLayout
解决
直接用CoordinatorLayout替换掉原来的activity_layout.xml文件中的帧布局即可,因为CoordinatorLayout就是一个加强版的帧布局。
可以自动监听到该布局中的任何事件,然后做出合理的响应。
AppBarLayout:将Toolbar放进AppBarLayout中 可以防止被遮挡。它实际上是一个竖直方向的LinearLayout。
然后给RecyclerView一个布局行为:app:layout_behavior="@string/appbar_scrolling_view_behavior"
然后给AppBarLayout的子控件Toolbar一个属性,指定它如何去影响AppBarLayout:app:layout_scrollFlags=“scroll|enterAlways|snap”
其中:scroll表示,当RecyclerView向上滚动的时候,Toolbar会跟着一起向上滚动并实现隐藏
enterAlways:表示当RecyclerView向下滚动的时候,Toolbar会跟着一起向下滚动并重新显示
snap:表示当Toolbar还没完全显示或隐藏的时候,根据当前滚动的距离,自动选择是隐藏还是显示
下拉刷新:SwipeRefreshLayout
SwiperRefreshLayout swipeRefresh = (SwipeRefreshLayout)findViewById(R.id.swipe_refresh);
swiperRefresh.setColorSchemeResources(R.color.colorPrimary);//设置下拉刷新进度条的颜色
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener(){
@Override
public void onRefresh(){
refreshFruits();
}
});//设置下拉刷新的监听器
总结:
使用滑动菜单(DrawerLayout
)的话,可以向里面放两个直接子控件,第一个子控件是主界面,第二个子界面是滑动菜单,要设置layout_gravity方式。
主界面:首先可以使用Toolbar代替原来的标题栏。并加入悬浮按钮(FloatingActionButton
),设置响应事件,用SnackBar代替弹出的Toast,但是弹出的时候会遮住FloatingActionBar,所以考虑将主界面放进一个CoordinatorLayout中
,这是一个加强版的FrameLayout,然后就会解决遮挡的情况。主界面可以放入滚动布局RecyclerView,并且用网格式布局来实现,滚动布局用卡片布局(也是加强版的FrameLayout)来实现,卡片布局里面套一个LinearLayout方便定位,并且顶部的Toolbar可以用AppBarLayout来代替,实现动态的隐藏和显示的功能
下拉刷新用SwiperRefreshLayout实现