Android(安卓)进阶——Material Design新控件之AppBarLayout+Toolbar+CollapsingToolbarLayout实现动态变化的头部(八)
文章大纲
- 引言
- 一、Toolbar
- 1、Toolbar概述
- 2、Toolbar的应用
- 1、在XMl中配置Toolbar
- 2、简单使用Toolbar
- 二、AppBarLayout概述
- 1、layout_scrollFlags
- 2、AppBarLayout的方法
- 3、AppBarLayout的使用
- 三、CollapsingToolbarLayout
- 四、AppBarLayout+Toolbar+CollapsingToolbarLayout
- 1、定义布局
- 2、监听对应事件实现动效
引言
前面系列文章总结了Material Design 兼容库提供大部分新控件的使用,如果你看完前一篇关于Android进阶——Material Design新控件之利用CoordinatorLayout协同多控件交互(七)的文章,你会发现Material Design不仅仅是提供了一种统一的设计标准,同时还提供了对应的一套控件,相比于传统的控件增强了交互功能及动画效果,使得原来需要自己用很多代码去实现的效果,现在只需要使用对应的控件即可,而且很多控件都借助了“Behavior”机制,系列文章链接:
- Android进阶——Material Design新控件之初识TabLayout(一)
- Android进阶——Material Design新控件之TabLayout制作可滚动的Tabs页面(二)
- Android进阶——Material Design新控件之Snackbar(三)
- Android进阶——Material Design新控件之TextInputLayout(四)
- Android进阶——Material Design新控件之FloatingActionButton(五)
- Android进阶——Material Design新控件之NavigationView(六)
- Android进阶——Material Design新控件之利用CoordinatorLayout协同多控件交互(七)
- Android 进阶——Material Design新控件之利用AppBarLayout实现动态变化的头部(八)
一、Toolbar
1、Toolbar概述
ToolBar直接继承ViewGroup是对原来ActionBar的整合,可以看成ActionBar的升级和替代者,简而言之就是ToolBar 内部支持了更多配置的属性以及设计了这些元素的事件监听接口,以及在ToolBar内部支持以下元素:
- Navigation Button,可以用于侧边栏的弹出按钮,也可以作为返回按钮
- Logo
- Title和SubTitle
- 任意自定义布局
- Action Menu溢出菜单
2、Toolbar的应用
1、在XMl中配置Toolbar
很多属性都可以直接在xml中配置使用,当然也可以通过对应的方法,只有溢出菜单需要在代码中动态生成。
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".view.ToolBarActivity"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="55dp" android:background="@color/colorPrimaryDark" app:logo="@mipmap/ic_logo" app:navigationIcon="@mipmap/ic_navig" app:subtitle=" next" app:titleTextAppearance="@style/AppTheme" app:subtitleTextColor="@android:color/white" app:title=" Material Design" app:titleTextColor="@android:color/white"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="32dp" android:background="@color/backgroundColor" android:orientation="vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_logo" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="自定义View" /> LinearLayout> android.support.v7.widget.Toolbar>android.support.design.widget.CoordinatorLayout>
2、简单使用Toolbar
public class ToolBarActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toolbar); initToolBar(); } private void initToolBar() { toolbar = findViewById(R.id.toolbar); //设置溢出菜单 toolbar.inflateMenu(R.menu.layout_toolbar_menu); //设置navigationIcon toolbar.setNavigationIcon(getResources().getDrawable(R.mipmap.more)); //给navigationIcon注册点击时事件 toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(ToolBarActivity.this,"点击我啦",Toast.LENGTH_LONG).show(); } }); //给溢出菜单注册点击事件 toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { switch (menuItem.getItemId()){ case R.id.github: Toast.makeText(ToolBarActivity.this,"点击info啦",Toast.LENGTH_LONG).show(); break; case R.id.about: Toast.makeText(ToolBarActivity.this,"点击about啦",Toast.LENGTH_LONG).show(); break; case R.id.more: Toast.makeText(ToolBarActivity.this,"点击more啦",Toast.LENGTH_LONG).show(); break; default: break; } return false; } }); }}
二、AppBarLayout概述
AppBarLayout继承自LinearLayout,可以看成加强型的竖直线性布局(不支持水平布局),相比传统的线性布局,AppBarLayout 增加了滑动手势的支持以及提供了MD 风格的动画和视觉效果(比如说浮层效果,立体感、交互特效),通过给其子View上app:layout_scrollFlags属性并配合CoordinatorLayout可以使得对应的子View接收到可滚动的View滑动手势改变时的事件,
1、layout_scrollFlags
layout_scrollFlags是AppbarLayout提供给其子View使用的属性(也可以通过setScrollFlags方法设置),其中layout_scrollFlags的值是scroll,enterAlways,enterAlwaysCollapsed,exitUntilCollapsed,snap组合构成五种动效:
-
scroll——子View将会随着可滚动View(如ScrollView、ListView、RecycleView、NestedScrollView等)一起滚动,就好像子View 是属于ScrollView的一部分一样。
-
scroll | enterAlways—— 当ScrollView 向下滑动时,子View 将直接向下滑动,而不管ScrollView 是否在滑动。必须要与scroll 搭配使用,否者是不能滑动的。
-
scroll|enterAlways|enterAlwaysCollapsed_ enterAlwaysCollapsed 是对enterAlways 的补充,当ScrollView 向下滑动的时候,滑动View(也就是设置了enterAlwaysCollapsed 的View)下滑至折叠的高度(是通过View的minimum height (最小高度)指定的),当ScrollView 到达滑动范围的结束值的时候,滑动View剩下的部分开始滑动。
-
exitUntilCollapsed——当ScrollView 滑出屏幕时(即滑出边界时),滑动View先响应滑动事件,滑动至折叠高度,即通过minimum height 设置的最小高度后,就固定不动了,再把滑动事件交给 scrollview 继续滑动。
-
snap——在滚动结束后,如果view只是部分可见,它将滑动到最近的边界。比如view的底部只有25%可见,它将滚动离开屏幕,而如果底部有75%可见,它将滚动到完全显示。
2、AppBarLayout的方法
-
public void addOnOffsetChangedListener——当AppbarLayout 的偏移发生改变的时候回调。
-
public final int getTotalScrollRange——返回AppbarLayout 所有子View的滑动范围
-
public void removeOnOffsetChangedListener——移除监听器
-
public void setExpanded (boolean expanded, boolean animate)——设置AppbarLayout 是展开状态还是折叠状态,animate 参数控制切换到新的状态时是否需要动画
-
public void setExpanded (boolean expanded)——设置AppbarLayout 是展开状态还是折叠状态,默认有动画
3、AppBarLayout的使用
AppBarLayout的布局略。
public class AppbarActivity extends AppCompatActivity { private AppBarLayout appbar_layout; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_appbar); initView(); } private void initView() { appbar_layout = findViewById(R.id.appbar_layout); //当AppbarLayout 的偏移发生改变的时候回调,也就是子View滑动 appbar_layout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int i) { // } }); //返回子View的可滑动距离 appbar_layout.getTotalScrollRange(); //移除偏移监听器 appbar_layout.removeOnOffsetChangedListener(null); }}
三、CollapsingToolbarLayout
CollapsingToolbarLayout继承自FrameLayout,顾名思义折叠工具栏布局,常作为AppBarLayout·的子View使用。当Collapsing title布局全部可见的时候,title 是最大的,当布局开始滑出屏幕,title 将变得越来越小,可以通过setTitle(CharSequence) 来设置要显示的标题。
当Toolbar 和CollapsingToolbarLayout 同时设置了title时,不会显示Toolbar中的title,只是显示CollapsingToolbarLayout 的title;但如果要显示Toolbar 的title,可在代码中添加如下代码:collapsingToolbarLayout.setTitle("")。另外必须给CollapsingToolbarLayout设置一个具体值(wrap_parent无效或者toolbar设置一个值来撑大CollapsingToolbarLayout也无效)
-
设置Content scrim(内容纱布)——当CollapsingToolbarLayout滑动到一个确定的阀值时将显示或者隐藏内容纱布,可以通过setContentScrim(Drawable)方法来设置纱布的图片。
-
设置Status bar scrim(状态栏纱布)——当CollapsingToolbarLayout滑动到一个确定的阀值时,状态栏显示或隐藏纱布,你可以通过setStatusBarScrim(Drawable)来设置Status bar scrim(状态栏纱布)。
-
Pinned position children(固定子View的位置)——子View可以固定在全局空间内,这对于实现了折叠并且允许通过滚动布局来固定Toolbar 这种情况非常有用
-
Parallax scrolling children(有视差地滚动子View)——在布局中配置app:layout_collapseMode="parallax"让CollapsingToolbarLayout 的子View 可以有视差的滚动
app:layout_collapseParallaxMultiplier=“0.7” 这个参数是设置视差范围的,0-1,越大视差越大
四、AppBarLayout+Toolbar+CollapsingToolbarLayout
1、定义布局
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar_layout" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapse_layout" android:layout_width="match_parent" android:layout_height="200dp" app:layout_scrollFlags="scroll|exitUntilCollapsed" > <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@mipmap/default_header" app:layout_collapseMode="parallax" /> <android.support.v7.widget.Toolbar android:id="@+id/appbar_layout_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:title="AppbarLayout" app:titleTextColor="@android:color/white" app:navigationIcon="@mipmap/ic_navig" app:layout_collapseMode="pin" /> android.support.design.widget.CollapsingToolbarLayout> android.support.design.widget.AppBarLayout> <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="match_parent" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="88dp" android:background="#FF7FFFD4" /> <View android:layout_width="match_parent" android:layout_height="88dp" android:background="#FF458B74"/> <View android:layout_width="match_parent" android:layout_height="88dp" android:background="#FF00CED1"/> <View android:layout_width="match_parent" android:layout_height="88dp" android:background="#FF7FFF00"/> <View android:layout_width="match_parent" android:layout_height="88dp" android:background="#FFCD5C5C"/> LinearLayout> android.support.v4.widget.NestedScrollView>android.support.design.widget.CoordinatorLayout>
2、监听对应事件实现动效
/** * 折叠控件 */public class CollapsingToolbarLayoutActivity extends AppCompatActivity { private Toolbar toolbar; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_collapsing_toolbar); initView(); } private void initView(){ initToolBar(); //设置沉浸式状态栏 StatusBarUtils.setTranslucentImageHeader(this,0,toolbar); AppBarLayout appBarLayout = findViewById(R.id.appbar_layout); final CollapsingToolbarLayout collapsingToolbarLayout = findViewById(R.id.collapse_layout); collapsingToolbarLayout.setTitle(""); collapsingToolbarLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white)); collapsingToolbarLayout.setExpandedTitleColor(getResources().getColor(R.color.white)); collapsingToolbarLayout.setExpandedTitleColor(Color.TRANSPARENT); //设置纱布 collapsingToolbarLayout.setContentScrim(getResources().getDrawable(R.mipmap.collapse_header)); //监听appBarLayout的偏移 appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { if(Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()){ toolbar.setTitleTextColor(getResources().getColor(R.color.white)); collapsingToolbarLayout.setTitle("AppbarLayout"); }else{ collapsingToolbarLayout.setTitle(""); } } }); } private void initToolBar() { toolbar = findViewById(R.id.appbar_layout_toolbar); //设置标题颜色 toolbar.setTitleTextColor(Color.TRANSPARENT); //设置溢出菜单 toolbar.inflateMenu(R.menu.layout_toolbar_menu); //设置navigationIcon toolbar.setNavigationIcon(getResources().getDrawable(R.mipmap.more)); //给navigationIcon注册点击时事件 toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(CollapsingToolbarLayoutActivity.this,"点击Navagtion button",Toast.LENGTH_LONG).show(); } }); //给溢出菜单注册点击事件 toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { switch (menuItem.getItemId()){ case R.id.github: Toast.makeText(CollapsingToolbarLayoutActivity.this,"点击info啦",Toast.LENGTH_LONG).show(); break; case R.id.about: Toast.makeText(CollapsingToolbarLayoutActivity.this,"点击about啦",Toast.LENGTH_LONG).show(); break; case R.id.more: Toast.makeText(CollapsingToolbarLayoutActivity.this,"点击more啦",Toast.LENGTH_LONG).show(); break; default: break; } return false; } }); }}
public class StatusBarUtils { public static void setColor(Activity activity, @ColorInt int color, int statusBarAlpha){ //先设置的全屏模式 setFullScreen(activity); //在透明状态栏的垂直下方放置一个和状态栏同样高宽的view addStatusBarBehind(activity,color,statusBarAlpha); } /** * 添加了一个状态栏(实际上是个view),放在了状态栏的垂直下方 */ public static void addStatusBarBehind(Activity activity, @ColorInt int color, int statusBarAlpha) { //获取windowphone下的decorView ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); //判断是否已经添加了statusBarView if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); } else { //新建一个和状态栏高宽的view StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha); decorView.addView(statusView); } setRootView(activity); } public static void setTranslucentImageHeader(Activity activity, int alpha,View needOffsetView){ setFullScreen(activity); //获取windowphone下的decorView ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); //判断是否已经添加了statusBarView if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { decorView.getChildAt(count - 1).setBackgroundColor(Color.argb(alpha, 0, 0, 0)); } else { //新建一个和状态栏高宽的view StatusBarView statusView = createTranslucentStatusBarView(activity, alpha); decorView.addView(statusView); } if (needOffsetView != null) { ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams(); layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0); } } private static StatusBarView createTranslucentStatusBarView(Activity activity, int alpha) { // 绘制一个和状态栏一样高的矩形 StatusBarView statusBarView = new StatusBarView(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); return statusBarView; } /** * 设置根布局参数 */ private static void setRootView(Activity activity) { ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); //rootview不会为状态栏流出状态栏空间 ViewCompat.setFitsSystemWindows(rootView,true); rootView.setClipToPadding(true); } private static StatusBarView createStatusBarView(Activity activity, int color, int alpha) { // 绘制一个和状态栏一样高的矩形 StatusBarView statusBarView = new StatusBarView(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); return statusBarView; } /** * 获取状态栏高度 * * @param context context * @return 状态栏高度 */ private static int getStatusBarHeight(Context context) { // 获得状态栏高度 int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); return context.getResources().getDimensionPixelSize(resourceId); } /** * 计算状态栏颜色 * * @param color color值 * @param alpha alpha值 * @return 最终的状态栏颜色 */ private static int calculateStatusColor(int color, int alpha) { float a = 1 - alpha / 255f; int red = color >> 16 & 0xff; int green = color >> 8 & 0xff; int blue = color & 0xff; red = (int) (red * a + 0.5); green = (int) (green * a + 0.5); blue = (int) (blue * a + 0.5); return 0xff << 24 | red << 16 | green << 8 | blue; } public static void setFullScreen(Activity activity){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 设置透明状态栏,这样才能让 ContentView 向上 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } public static class StatusBarView extends View { public StatusBarView(Context context) { super(context); } public StatusBarView(Context context, AttributeSet attrs) { super(context, attrs); } public StatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }}
理论上Material Design库都是应该放在CoordinatorLayout下才会发挥最大的效果的,因为CoordinatorLayout是相当于给他们提供了交互的能力,核心还是Behavior。
更多相关文章
- android EditText设置不可写
- 三、安卓UI学习(1)
- android“设置”里的版本号
- android用户界面之按钮(Button)教程实例汇
- 在Fragment中设置控件点击方法,执行失败。
- Android(安卓)闹钟管理类的使用
- TabHost与RadioGroup结合完成的菜单【带效果图】5个Activity
- Android设置通知栏/状态栏透明改变通知栏颜色和app最上部分颜色
- android 设置中划线 下划线等