Material Design系列之Behavior实现Android知乎首页

本博客目的:仿知乎首页向上滑动时动画隐藏Toolbar、FlocationActionButton、Tab导航,下滑时显示,如果和你的期望不同,那么你可以不需要看了,免的浪费你的宝贵时间噢。

效果预览

知乎效果:

本博客实现效果:

今天效果的源代码下载链接在文章末尾。

实现分析

这个效果其实并不难实现,但是它的用处很大,当用户手指上滑,屏幕上显示下方内容的时候,隐藏Toolbar、Tab导航、FAB来腾出更大的空间显示内容,让用户爽。简单粗暴,但这就是我们的目的。

首先就是头部的Toolbar,这个就不用说了吧,基本会,不会的人随便看我一篇博客的demo都有这个效果,简直小学级别。

其次来看看FAB(FlocationActionButton)的显示和隐藏,知乎是用的平移,我们这里做个优化改动,当然平移也是可以的,如果你看过我的Material Design系列,自定义Behavior之上滑显示返回顶部按钮这篇博客的话。那么我们的FAB的动画隐藏和显示也是用上一篇博客的原理,没有看上一篇博客的同学需要回过头看看噢,这里不在赘述。

最后来看下面的Tab导航的隐藏和显示,这个确确实实用平移更好是吧,然而相信你如果看过我Material Design系列,Behavior之BottomSheetBehavior与BottomSheetDialog这篇博客的话,这个效果实现起来也不难。强烈建议看下文之前读这篇文章,不然真的没法继续看下去了。

其实代码量还是很少的,主要是Behavior原理、Behavior和CoordinatorLayout如何结合使用。so,强烈建议去上读下上面两篇博客噢。

……

好的,五分钟过去了,我相信你大概已经速读了上面提到的两篇博客了。那么在第一篇FAB的那篇博客中实现的效果是手指向上滑时(屏幕显示下方的内容时)显示FAB用来回到顶部,但是这里刚好是相反的:向上滑时隐藏FAB。如果你认真读了原理解释的那一段或者运行过demo,这个效果so easy吧。第二篇博客中也讲到了如何隐藏和显示这个Tab导航,那么有的同学就觉得今天的博客就结束了吧?答案当然是No了,不然我也不会再开一篇博客来讲这个了。

为什么呢?还是有难点的,难点在哪里?就是上面讲到的两个Behavior如何和CoordinatorLayout结合使用,同时实现两个效果。而且BottomSheetBehavior隐藏和显示Tab导航这个里面之前我们使用Button来控制的,如何做到`CoordinatorLayout中的ContentView滑动时动态的显示和隐藏Tab导航呢?

接下来来点真材实料,带领大家一起代码撸起来。

页面布局

上面的引文和介绍,我们已经知道了FAB的显示和隐藏用自定义Behavior实现,Tab导航用BottomSheetBehavior来实现,那么我们布局文件也该问世了:

<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.support.design.widget.AppBarLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:theme="@style/AppTheme.AppBarOverlay">

  <android.support.v7.widget.Toolbar
   android:id="@+id/toolbar"
   android:layout_width="match_parent"
   android:layout_height="?attr/actionBarSize"
   app:layout_scrollFlags="scroll|enterAlways|snap"
   app:popupTheme="@style/AppTheme.PopupOverlay" />
 </android.support.design.widget.AppBarLayout>

 <android.support.v7.widget.RecyclerView
  android:id="@+id/recyclerView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layout_behavior="@string/appbar_scrolling_view_behavior" />

 <LinearLayout
  android:id="@+id/tab_layout"
  android:layout_width="match_parent"
  android:layout_height="?actionBarSize"
  android:layout_alignParentBottom="true"
  android:background="@android:color/white"
  app:layout_behavior="@string/bottom_sheet_behavior">

  <Button
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:text="第一" />

  <Button
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:text="第二" />

  <Button
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:text="第三" />

  <Button
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:text="第四" />
 </LinearLayout>

 <android.support.design.widget.FloatingActionButton
  android:id="@+id/fab"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="bottom|end"
  android:layout_marginBottom="70dp"
  android:layout_marginEnd="16dp"
  android:layout_marginRight="16dp"
  android:src="@mipmap/ic_action_new"
  app:layout_behavior="@string/scale_down_show_behavior"
  app:layout_scrollFlags="scroll|enterAlways|snap" />
</android.support.design.widget.CoordinatorLayout>

还是稍微解释下,内容区域是一个RecyclerView,使用的Behavior是design的ScrollingViewBehavior:

app:layout_behavior="@string/appbar_scrolling_view_behavior"

然后一个LinearLayout,使用的Behavior是design的BottomSheetBehavior:

app:layout_behavior="@string/bottom_sheet_behavior"

最后一个FloatingActionButton,使用我们的自定义ScaleDownShowBehavior:

app:layout_behavior="@string/scale_down_show_behavior"

其他两个都是design自带的,唯有FloatingActionButton的ScaleDownShowBehavior需要我们自定义,那么下面我们就来实现下ScaleDownShowBehavior。

自定义Behavior实现FAB的动画控制

这里又谈到了自定义Behavior了,首先就来实现:用户手指在屏幕上滑,隐藏FAB,留出更多位置给用户。

这里还是继承FloatingActionButton.Behavior:

public class ScaleDownShowBehavior extends FloatingActionButton.Behavior {
 public ScaleDownShowBehavior(Context context, AttributeSet attrs) {
  super();
 }
}

这里我们的滑动方向还是不变,监听竖着方向的滑动:

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, ...) {
 return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}

那么我们就要稍微改一下我们的开始滑动时回调这个方法了:onNestedScroll():

@Override
// 隐藏动画是否正在执行
private boolean isAnimatingOut = false;

public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
 if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimatingOut
 && child.getVisibility() == View.VISIBLE) {// 手指上滑,隐藏FAB
  AnimatorUtil.scaleHide(child, listener);
 } else if ((dyConsumed < 0 || dyUnconsumed < 0) && child.getVisibility() != View.VISIBLE) {
  AnimatorUtil.scaleShow(child, null);// 手指下滑,显示FAB
 }
}

private ViewPropertyAnimatorListener listener = new ViewPropertyAnimatorListener() {
 @Override
 public void onAnimationStart(View view) {
  isAnimatingOut = true;
 }

 @Override
 public void onAnimationEnd(View view) {
  isAnimatingOut = false;
  view.setVisibility(View.GONE);
 }

 @Override
 public void onAnimationCancel(View arg0) {
  isAnimatingOut = false;
 }
};

好吧,代码非常少,完成了。那么我们就在string.xml中定义好,刚才我们引用的变量@string/scale_down_show_behavior:

代码如下:

<string name="scale_down_show_behavior">com.yanzhenjie.definebehavior.behavior.ScaleDownShowBehavior</string>

啊呀,好激动呀,我赶紧运行一下。但是但是。。。运行后发现见鬼啊,只有FAB会跟着显示和隐藏,完全看不到Tab导航呀,严振杰你是在忽悠人麽?哈哈哈哈,且听我细细道来。

通过监听ScaleDownShowBehavior中的view显示/隐藏来控制Tab导航栏

其实只要看过Material Design系列,Behavior之BottomSheetBehavior与BottomSheetDialog这篇文章的同学会发现,用BottomSheetBehavior的控件默认都是隐藏起来的,需要我们去调用它的方法来控制它的View的显示。所以我们这里需要在CoordinatorLayout中的ContentView滚动的时候来调用BottomSheetBehavior的方法使它依附的View显示与隐藏。

那么我们发现ScaleDownShowBehavior被系统自动调用了,也触发了View的隐藏和显示,CoordinatorLayout这货没有给我们自动调用BottomSheetBehavior,我们怎么办?如果你没有忘记的话,我们自定义ScaleDownShowBehavior的时候,在onNestedScroll()方法中有个地方是去调用了FAB的显示和隐藏,所以我们在这里加一个回调监听,让外部可以监听到它的动作,是不是同时可以控制BottomSheetBehavior了?如果还没有向明的话看代码。

先在ScaleDownShowBehavior中定一个Listener:

// 外部监听显示和隐藏。
public interface OnStateChangedListener {
 void onChanged(boolean isShow);
}

然后在ScaleDownShowBehavior的onNestedScroll()方法中回调:

private OnStateChangedListener mOnStateChangedListener;

public void setOnStateChangedListener(OnStateChangedListener mOnStateChangedListener) {
 this.mOnStateChangedListener = mOnStateChangedListener;
}

@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
 if ((dyConsumed > 0 || dyUnconsumed > 0) && !isAnimatingOut
 && child.getVisibility() == View.VISIBLE) {//往下滑
  AnimatorUtil.scaleHide(child, viewPropertyAnimatorListener);
  if (mOnStateChangedListener != null) {
   mOnStateChangedListener.onChanged(false);
  }
 } else if ((dyConsumed < 0 || dyUnconsumed < 0) && child.getVisibility() != View.VISIBLE) {
  AnimatorUtil.scaleShow(child, null);
  if (mOnStateChangedListener != null) {
   mOnStateChangedListener.onChanged(true);
  }
 }
}

好完美啊。来来来,设置一个监听。。。我勒个去,突然发现怎么从FAB拿到这个Behavior啊?且看我下面的分析,保证让你柳暗花明又一村啊。

拿到FAB的Behavior对象,通过监听控制BottomSheetBehavior的View的显示/隐藏

我们这知道,给一个View设置Behavior对象的时候是在xml中设置,所以Behavior是一个View的LayoutParams属性吧?哈哈哈明白了吧,然后Behavior又必须和CoordinatorLayout结合使用,不然也是扯淡,so,这个View也必须是CoordinatorLayout的子View,所以在ScaleDownShowBehavior中产生了如下的一个静态方法(为了方便阅读,提示写了中文):

public static <V extends View> ScaleDownShowBehavior from(V view) {
 ViewGroup.LayoutParams params = view.getLayoutParams();
 if (!(params instanceof CoordinatorLayout.LayoutParams)) {
  throw new IllegalArgumentException("这个View不是CoordinatorLayout的子View");
 }
 CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params).getBehavior();
 if (!(behavior instanceof ScaleDownShowBehavior)) {
  throw new IllegalArgumentException("这个View的Behaviro不是ScaleDownShowBehavior");
 }
 return (ScaleDownShowBehavior) behavior;
}

所以我们在Activity中:

private BottomSheetBehavior mBottomSheetBehavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.zhihu_main);

 ScaleDownShowBehavior scaleDownShowFab = ScaleDownShowBehavior.from(FAB);
 scaleDownShowFab.setOnStateChangedListener(onStateChangedListener);
 mBottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.tab_layout));
}

private OnStateChangedListener onStateChangedListener = new OnStateChangedListener() {
 @Override
 public void onChanged(boolean isShow) {
  mBottomSheetBehavior.setState(
  isShow ? BottomSheetBehavior.STATE_EXPANDED
  : BottomSheetBehavior.STATE_COLLAPSED);
 }
};

哎哟喂,不知不觉中已经把我们的效果实现了,这里最重要的就是onStateChangedListener了,这里实现了Tab导航的隐藏和显示,它的状态是从ScaleDownShowBehavior中回调出来的。

页面初始化好后显示Tab导航

我们上文中说道,添加了BottomSheetBehavior属性的View,默认是隐藏的,所以我们在页面初始化时要把我们的Tab导航显示出来:

private boolean initialize = false;

@Override
public void onWindowFocusChanged(boolean hasFocus) {
 super.onWindowFocusChanged(hasFocus);
 if (!initialize) {
  initialize = true;
  mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
 }
}

源码下载:http://xiazai.jb51.net/201609/yuanma/AndroidBehavior(jb51.net).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 学习Android Material Design(RecyclerView代替ListView)

    本文实例实现一下 RecyclerView,代码比较简单,适合初学者,如有错误,欢迎指出. 复习 ListView 可以查看这篇文章深入浅出学习Android ListView基础,了解关于ListView 的基础知识. 实现过程中需要复写BaseAdapter,主要是这4个方法 public int getCount() :适配器中数据集中 数据的个数,即ListView需要显示的数据个数 public Object getItem(int position) : 获取数据集中与指定索引对应的

  • 详解Android Material Design自定义动画的编写

    新的动画Api,让你在UI控件里能创建触摸反馈,改变View的状态,切换activity的一系列自定义动画 具体有: 响应View的touch事件的触摸反馈动画 隐藏和显示View的循环展示动画 两个Activity间的切换动画 更自然的曲线运动的动画 使用View的状态更改动画,能改变一个或多个View的属性 在View的状态更改时显示状态列表动画 这些new animations Api,已内置在标准Widget中,如Button.在自定义view时也可使用这些api 动画在Material

  • Android App仿QQ制作Material Design风格沉浸式状态栏

    一.概述 近期注意到QQ新版使用了沉浸式状态栏,ok,先声明一下效果图: 恩,接下来正题. 首先只有大于等于4.4版本支持这个半透明状态栏的效果,但是4.4和5.0的显示效果有一定的差异,所有本文内容为: 1.如何实现半透明状态栏效果在大于4.4版本之上. 2.如何让4.4的效果与5.0的效果尽可能一致. 先贴下模拟器效果图,以便和实现过程中做下对比 4.4 模拟器 5.x 真机 二.实现半透明状态栏 因为本例使用了NavigationView,所以布局代码稍多,当然如果你不需要,可以自己进行筛

  • Android5.0中Material Design的新特性

     Material Design简介 Material Design是谷歌新的设计语言,谷歌希望寄由此来统一各种平台上的用户体验,Material Design的特点是干净的排版和简单的布局,以此来突出内容. Material Design对排版.材质.配色.光效.间距.文字大小.交互方式.动画轨迹都做出了建议,以帮助设计者设计出符合Material Design风格的应用. Material Design设计语言鼓励大家使用充满活力的鲜艳色彩,并在同一界面建议使用三种色调,并保障有一个强色调,

  • 学习使用Material Design控件(四)Android实现标题栏自动缩放、放大效果

    本文要实现内容移动时,标题栏自动缩放/放大的效果,效果如下: 控件介绍 这次需要用到得新控件比较多,主要有以下几个: CoordinatorLayout 组织它的子views之间协作的一个Layout,它可以给子View切换提供动画效果. AppBarLayout 可以让包含在其中的控件响应被标记了ScrollingViewBehavior的View的滚动事件 CollapsingToolbarLayout 可以控制包含在CollapsingToolbarLayout其中的控件,在响应colla

  • Android构建Material Design应用详解

    长久以来.Android的UI并不算美观,以至于很多IT公司在进行界面设计的时候,为了保证双平台的统一性,强烈要求Android端的界面风格必须与iOS端一致,我认为这里非常不合理的,同一操作系统中各个应用之间的界面统一性要远比一个应用在双平台的界面统一性重要的多,只有这样,才能给使用者带来更好的用户体验.为了解决这个问题,Google公司在2014年IO大会上推出了一套全新的界面设计语言--Material Design(材料设计语言),这次Google在界面设计上确实下足了功夫,一个词,好看

  • Material Design系列之Behavior实现Android知乎首页

    本博客目的:仿知乎首页向上滑动时动画隐藏Toolbar.FlocationActionButton.Tab导航,下滑时显示,如果和你的期望不同,那么你可以不需要看了,免的浪费你的宝贵时间噢. 效果预览 知乎效果: 本博客实现效果: 今天效果的源代码下载链接在文章末尾. 实现分析 这个效果其实并不难实现,但是它的用处很大,当用户手指上滑,屏幕上显示下方内容的时候,隐藏Toolbar.Tab导航.FAB来腾出更大的空间显示内容,让用户爽.简单粗暴,但这就是我们的目的. 首先就是头部的Toolbar,

  • Material Design系列之Behavior实现支付密码弹窗和商品属性选择效果

    今天的效果在支付宝.淘宝.京东等电商App中很常见.比如支付宝输入密码弹窗.商城下单时选择商品属性时,从下面浮动上来一个PopupWindow,那么今天就带大家用Behavior来实现这两个效果,结果你会发现简直只需要一行代码. 总结下现在用的APP: 1. 仿支付宝弹出的输入支付密码窗口. 2. 仿淘宝/天猫弹出商品属性选择框. 3. 知乎首页上下滑动隐藏ToolBar和NavigationBar. 4. - 系列博客: 1. Material Design系列,Behavior之Bottom

  • Material Design系列之Behavior上滑显示返回顶部按钮

    效果预览 源码在文章末尾. 引文 有时候我们的页面内容过长的时候,滑动到页面底部用户再滑动到顶部很麻烦,Android不像iOS可以点击statusBar回到顶部,一般都是双击Toolbar/ActionBar或者在底部放一个按钮. 今天就底部放一个回到顶部按钮这个效果来做一个基于Behavior的实现.那么我们传统的方式来做就是监听这个滑动View,比如:ScrollView/ListView/RecyclerView/GridView等,那么如果我们使用了CoordinatorLayout,

  • Material Design系列之自定义Behavior支持所有View

    本文实例为大家分享了Android自定义Behavior支持所有View ,供大家参考,具体内容如下 一.实现效果图 这个右下角的FAB,动画当然可以多种多样,可以放在界面的任何地方,我们这里只举个例子.但是v7包中提供的Behavior目前只能是FloatingActionButton来用,所以今天我们实现的这个Behavior是支持所有的View的,可以用在ImageView.Button.Layout,只要是继承View的类都可以用. 二.自定义Behavior和动画的封装 我们知道Beh

  • Android Material Design 阴影实现示例

    本文介绍了Android Material Design 阴影实现示例,分享给大家,具体如下: 让 View 产生阴影有以下 4 种方式: 控制 elevation 使用 OutlineProvider 使用 9 图 使用 MD 风格的控件,如 CardView,FloatingActionButton 0.Z轴的概念 Android 在 5.0 及以后采用了 Material Design 设计语言,引入了 Z 轴的概念,也就是垂直于屏幕的轴,Z 轴会让 View 产生阴影的效果: 想象有一束

  • 学习使用Material Design控件(一)

    Google 发布的Material Design支持库,对我们的APP设计有很大的影响,如果重新设计APP,支持库应该直接用V4提升到V7了,我们可以用Toolbar代替ActionBar,以及引入了RecycleView, SnakeBar等新控件. 我写了一个Demo来学习使用这些新控件. 新建项目,加入依赖包 我们使用Android Studio来开发这个Demo,在Android Studio新建一个项目,修改App Module的build.gradle文件,把compileSdkV

  • 基于Bootstrap实现Material Design风格表单插件 附源码下载

    Jquery Material Form Plugin是一款基于Bootstrap的Material Design风格的jQuery表单插件.该表单通过自定义样式和jQuery来将Bootstrap的表单修改为扁平风格的表单,并带有浮动标签特效. 在线预览         源码下载 使用方法 使用该Material Design风格表单需要在页面中引入jquery,bootstrap相关文件和materialFormStyles.css.materialForm.js文件. <link rel=

随机推荐