废话不多说,基于上一篇博客再续前缘,上一篇博客地址:Material Design 设计风格进阶篇 <一>
转载请注明出处:http://blog.csdn.net/demokui/article/details/60962295
本篇文章出自【姜奎的博客】
目标控件
1.TabLayout
2.AppBarLayout
3.CollapsingToolbarLayout
效果图
控件介绍
1.TabLayout
这个控件在我们的项目中几乎都能用到,主要是展示分类标签用的控件,常常会和viewpager配合使用。
2.AppBarLayout
这个控件见名就知道跟标题栏有着关系,无非就是ActionBar和Toolbar的配合使用了,那么目前最常用的还是Toolbar。
3.CollapsingToolbarLayout
说到这个控件我还是非常佩服的,因为这个控件的动画效果封装的特别好,最常用的场景就是滑动折叠的标题栏。
开始之前先添加几个依赖:
compile 'com.android.support:cardview-v7:25.2.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'de.hdodenhof:circleimageview:2.1.0'
分别是卡片式布局、glide加载图片处理、圆形图片
首先从标题栏学起:AppBarLayout
上面提到过,这个布局google规定要实现特殊效果必须是CoordinatorLayout的子控件。那么我们的布局应该是这样的:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="20dp">
<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:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
上面这个xml并不全部代码,app命名控件在最外层指定,后面讲解。我们的的Toolbar在AppBarLayout里面包裹着,AppBarLayout在CoordinatorLayout里面包裹着,着符合google的规定,但是有些朋友就问了,为什么AppBarLayout必须是CoordinatorLayout的子控件呢,好了这里说一下,因为MD设计风格是非常人性化的,当用户关注点放在界面的主体内容是,这时候Toolbar是需要隐藏掉的,而CoordinatorLayout对事件的处理做了完美的封装,可根据外部事件响应子控件的行为,所以就有了这样的规定。我在ToolBar上指定了popupTheme为亮色系,因为我们的默认主题是暗色,当我们Toolbar上的弹出菜单也为暗色的话会比较难看,所以这里指定了相反的主题。
java代码
Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
setSupportActionBar(toolbar);
//获取actionbar(也就是我们的toolbar)
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
//如果不为空那么展示菜单按钮并设置图标(默认是一个返回的按钮图标,这里我们改了一下图标)
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.categoty);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar, menu);
return true;
}
上面对toolbar的实例化已经做了详细的注释,然后我们重写onCreateOptionsMenu()方法,然后通过getMenuInflater().inflate(R.menu.toolbar, menu);加载Toolbar上的菜单按钮!
在res目录下新建menu文件夹(如果有就不用新建了),然后在menu文件夹下创建toolbar文件。
toolbar.xml长这样:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/id_back"
android:icon="@drawable/ic_menu_search_mtrl_alpha"
android:title="Back"
app:showAsAction="always" />
<item
android:id="@+id/id_delete"
android:icon="@drawable/ic_menu_share_holo_dark"
android:title="Delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/id_setting"
android:icon="@drawable/ic_settings"
android:title="Setting"
app:showAsAction="never" />
</menu>
同样定义了app命名控件,可以看出来我在toolbar上定义了三个菜单,定义了Back、delete、setting。同时都定义了一个showAsAction属性,这个属性有多个值:这里我给back设置always(一直显示),delete设置ifRoom(如果控件够用就显示,不够就隐藏),Setting设置never(一直隐藏)。
到这里首页的标题栏就完了,效果图在文章最开始已经给出。
接下来学习TabLayout控件:
首先我打算将tablayout放在布局的最底层代替传统的TabHoat、RadioButton等等控件来切换viewpager。
<android.support.design.widget.CoordinatorLayout
......
<android.support.v4.view.ViewPager
android:id="@+id/id_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="58dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.TabLayout
android:id="@+id/id_tab"
style="@style/MyCustomTabLayout"
android:layout_width="match_parent"
android:layout_height="58dp"
android:background="#99000000"
app:layout_behavior="@string/bottom_sheet_behavior"
app:tabIndicatorColor="@color/colorAccent"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@android:color/holo_green_light" />
</android.support.design.widget.CoordinatorLayout>
可以看到我给viewpager和Tablayout都设置了layout_behavior这个属性,这个属性只能设置在CoordinatorLayout的子控件上,否则无效,在这里我给viewpager设置了在toolbar下方,并且CoordinatorLayout会根据子控件的滑动事件响应处理,给Tablayout设置了在CoordinatorLayout底部保持不动。
tablayout的子tab创建方式有两种,先看官网给的例子:
上面我已经给了详细解释不在过多赘述。
在这里我说一下我使用tablayout时遇到的深坑:我预想给tab设置一个icon和text,无论是动态设置还是静态设置都能设置成功。我心里当时无比喜悦,可是当我将viewpager和tablayout绑定联动的时候icon就无缘无故消失了。下面给出掉坑代码:
mTabLayout.addTab(mTabLayout.newTab().setIcon(imageResId[0]));
mTabLayout.addTab(mTabLayout.newTab().setIcon(imageResId[1]));
mTabLayout.addTab(mTabLayout.newTab().setIcon(imageResId[2]));
mTabLayout.addTab(mTabLayout.newTab().setIcon(imageResId[3]));
MyVPAdapter vpAdapter = new MyVPAdapter(getSupportFragmentManager(),list,titles,this);
mViewPager.setAdapter(vpAdapter);
mTabLayout.setupWithViewPager(mViewPager);
mTabLayout.setTabsFromPagerAdapter(vpAdapter);
我相信大家都能看明白:这段代码就是创建tab并给tab设置icon,创建viewpager适配器并设置,然后使用setupWithViewPager()将viewpager和tablayout绑定,可结果不是我想要的(Icon完全不显示),google 、百度了半天,网上说的很多原因都有,也有很多建议用其他办法例如自定义tab布局等等,但是我觉得这个方法简单,就想用这个方法实现。其实有个问题判断的跟我的很类似,就是说,setTabsFromPagerAdapter()里面会判断vp的adapter和viewpager是否为空,如果为空就移除掉之前给tab设置的所有属性,我点到源码也看到了,的确有这个判断,源码如下:
void populateFromPagerAdapter() {
// 罪魁祸首在这里
removeAllTabs();
if (mPagerAdapter != null) {
final int adapterCount = mPagerAdapter.getCount();
for (int i = 0; i < adapterCount; i++) {
addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
}
// Make sure we reflect the currently set ViewPager item
if (mViewPager != null && adapterCount > 0) {
final int curItem = mViewPager.getCurrentItem();
if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
selectTab(getTabAt(curItem));
}
}
}
}
removeAllTabs()里面遍历删除了tab的所有属性,此时我心里暗喜问题终于找到了,可是我突然想到,不对啊,我传的viewpager和adapter都打印有值啊,再者说如果移除所有之前设置的属性那么我的tab的text怎么还会显示呢?再次陷入bug深潭中。。。。。一直没找到原因,最后我使用了简单粗暴的方式设置了icon,代码如下:
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
MyVPAdapter vpAdapter = new MyVPAdapter(getSupportFragmentManager(),list,titles,this);
mViewPager.setAdapter(vpAdapter);
mTabLayout.setupWithViewPager(mViewPager);
mTabLayout.setTabsFromPagerAdapter(vpAdapter);
mTabLayout.getTabAt(0).setIcon(imageResId[0]);
mTabLayout.getTabAt(1).setIcon(imageResId[1]);
mTabLayout.getTabAt(2).setIcon(imageResId[2]);
mTabLayout.getTabAt(3).setIcon(imageResId[3]);
可以看到起初创建tab的时候什么属性都不设置,text的设置交给了viewagadapter的getPageTitle(int position)方法,icon设置的顺序放在绑定vp和tab联动的步骤之后,这样问题就完美解决。(不过我现在还不知道之前的原因怎么出现的,预感应该是和design的版本问题有关。。。)
最后学习CollapsingToolbarLayout控件:
和之前一样我们先看看这个控件如何布局:谷歌规定Toolbar必须是CollapsingToolbarLayout的子控件,而CollapsingToolbarLayout必须是AppBarlayout的子控件,并且AppBarLayout必须是CoordinatorLayout的子控件,只有这样的布局结构内部才会完美的处理一些滑动事件,好了,下面是我的布局结构:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
android:background="#fff">
<android.support.design.widget.AppBarLayout
android:id="@+id/id_appBar"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="250dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/id_collaps"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/id_girlIcon_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"/>
<android.support.v7.widget.Toolbar
android:id="@+id/id_toobar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/id_fab"
android:layout_width ="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="@drawable/stat_notify_chat"
app:layout_anchor="@id/id_appBar"
app:layout_anchorGravity="bottom|end" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
app:cardCornerRadius="5dp">
<TextView
android:id="@+id/id_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="#fff"
android:textSize="16sp" />
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
这里我为了证明滑动事件的处理故意在内部嵌套了一个可以响应滚动事件的NestedScrollView,这个控件暂时就以当成scrollView来使用,为了美观我在这里使用的锚点属性将FloatingActionButton定义在了AppBar控件的右下角,我在CollapsingToolbarLayout控件上定义了这个属性:app:contentScrim=”@color/colorPrimary”,这个属性的意思是折叠后toolbar的颜色;其次是在这个控件里我定义了一个ImageView和一个Toolbar,那么可以看出这个标题栏是组合而成的,不过我在这两个控件上都指定了这个属性:app:layout_collapseMode,它的意思是指定当前控件在CollapsingToolbarLayout折叠过程中的折叠模式,其中toolbar我指定的是“pin”,意思是折叠过程中位置始终保持不变,ImageView我指定的是“parallax”,表示在折叠过程中会有错位偏移的效果。下面是java代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_girl_detial);
Toolbar toolbar = (Toolbar) findViewById(R.id.id_toobar);
CollapsingToolbarLayout coll = (CollapsingToolbarLayout) findViewById(R.id.id_collaps);
final ImageView icon = (ImageView) findViewById(R.id.id_girlIcon_detail);
TextView text = (TextView) findViewById(R.id.id_content);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.id_fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
change(icon);
}
});
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
// 设置toobar标题
coll.setTitle(name);
//设置背景图片
Glide.with(this).load(iconID).into(icon);
text.setText(CONTENT);
}
关键代码给出了注释。好了Material Design的八大控件到这里就已经学习完了,不过这仅仅是design的一小部分而已,大量的知识还需要去学习。
最后说一下v4包下的DrawerLayout控件实现侧滑菜单栏:
要给首页添加侧滑菜单其实很简单,只需要将DrawerLayout放在布局的最外层,然后这个有两个子控件:布局如下:
<?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"
android:id="@+id/id_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
.......
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="@+id/id_ngv"
android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@drawable/left_bg"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_layout"
app:menu="@menu/nav_menu"
app:theme="@style/GirlDEtailTheme" />
</android.support.v4.widget.DrawerLayout>
可以看到我在DrawerLayout 里面只放了两个子View,第一个CoordinatorLayout就是首页正文,而NavigationView就是这侧滑菜单栏的内容,这里主要解释几个属性:
- app:headerLayout=”@layout/nav_header_layout” 这个是给侧滑菜单添加一个 自定义的头布局。
app:menu=”@menu/nav_menu” 这个是加载菜单栏的菜单按钮,
java代码中的使用:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contentt);
mDrawerlayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
mNavigationView = (NavigationView)findViewById(R.id.id_ngv);
// 获得头布局实例对象(为了初始化头布局上的子控件)
View headView = mNavigationView.getHeaderView(0);
mUserIcon = (CircleImageView)headView.findViewById(R.id.id_userIcon);
mUserIconBG = (ImageView)headView.findViewById(R.id.id_iv_iconBG);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: //Android内部的首页侧滑导航按钮的id
mDrawerlayout.openDrawer(GravityCompat.START);
break;
}
return true;
}
到这里上面的效果图的内容我们就已经全部实现完了,望大家多多指导!